Skip to content

Seed replay rewrites every seeded row that carries a password field on every boot: the existing row is read through engine.find, which masks the password, so isNoOpReplay compares the seed plaintext with the mask #24624

Closed

Description

@objectstack-fleet

Carries the second half of #24562: the fifth churning showcase row, "Specimen — Full", whose cause is not a temporal spelling. #24562 keeps the temporal comparison, fixed by PR #24617.

Filing gate: ① — a product defect with a reproduction and a named location. Filed by domain:engine seat 2 (seat post #20966) · os-warren · session_01AHeUBbe6S3JRawcpbLejXD, from the os-dev-report on #24562 (6271901541, out-of-scope finding 1). In-flight derivation: #24562's own reproduction (all 132 showcase rows unchanged on a second boot) does not pass without it, so it is that card's in-scope remainder, this seat's own, with the parent's priority and lane.

reach: the parent card's public-boot measurement (two objectstack dev boots of the stock showcase on one file DB, read through GET /api/v1/data/showcase_field_zoo) names "Specimen — Full" among the 5 rows whose updated_at moves. After PR #24617, the dev's harness (every examples/app-showcase object definition and seed dataset, real ObjectQL + SqlDriver on one SQLite file DB) shows that row as the only one still rewritten, and f_password as its only key read as changed.

What happens

  • examples/app-showcase/src/data/objects/field-zoo.object.ts declares f_password: Field.password({ ..., ackPlaintextMasking: true }), and the showcase seed writes f_password: 'hunter2' for "Specimen — Full".
  • SeedLoaderService.loadExistingRecords (packages/metadata-protocol/src/seed-loader.ts) reads the existing rows through engine.find. ObjectQL.maskSecretFields (packages/objectql/src/engine.ts, fed by collectMaskedReadFields) masks every password field on a generic object on read, in every context, the seed's system context included.
  • So isNoOpReplay compares the seed's plaintext with the mask, reads it as changed, and rewrites the row on every boot: updated_at moves, lifecycle validation runs again, and history can record an edit nobody made. That breaks platform-core.seed-integrity acceptance[3] ("upsert with externalId no-ops on unchanged rows") the same way Seed replay rewrites unchanged rows on every boot: seedValueEquals compares time and ISO-datetime strings literally, so 09:15 vs 09:15:00 and …Z vs ….000Z bump updated_at (5 showcase rows per restart) #24562 did.
  • Any app that seeds a row with a password field is affected, not only the showcase.

The decision a fix needs (measured first by the claimant)

How a replay compares a value its own read path masks. Three shapes, from the dev's report:

  • A. Compare against the stored value through a read that is not masked for the seed loader only. This crosses the secret-masking boundary, so if it needs a new unmasked read seam it goes to the maintainer before any code (a security boundary is not a seat call).
  • B. Treat a masked field as not comparable: it never blocks the skip. A changed seed password is then never re-applied on replay, which is a silent drop of a declared seed value.
  • C. Keep rewriting. The churn stays.

The claimant measures first: how the field is stored (ackPlaintextMasking: true reads as plaintext at rest), whether an existing unmasked read for system callers already exists (the engine's raw-read or masking-exempt paths), and what history records today on each churned replay.

Dedupe

MCP search_issues on this repository, closed included: "seed replay password field masked rewrites row every boot updated_at isNoOpReplay maskSecretFields" → 4 hits: #24562 (the parent), #21787 (closed, credential-field handling on writes, a different clause), #3434 and #3433 (closed, other replay defects). None covers this mechanism.

Dedupe words: seed replay password masked updated_at churn; isNoOpReplay SECRET_MASK; maskSecretFields seed loader existing row.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    priority:p1High: required for production / M2
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Oct 19, 2026
  2. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: spot-check: grade and lane confirmed (p1, domain:engine) · target:v18 added from its parent · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-19T17:01Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch. Fresh read on origin/main c03b17d784.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    待裁:seed 回放遇到读不回来的凭据字段时怎么判「没变」 · domain:engine seat 2 · session_01AHeUBbe6S3JRawcpbLejXD · 2026-10-19T19:42Z

    ⛔ Not a claim. This card moves pm:queue → needs-user-decision in the same act as this comment. The maintainer reads the analysis below (Chinese, per the decision-analysis channel).

    一句话问题: 每次重启,凡是 seed 里带密码(或密钥)字段的记录都会被重写一遍(更新时间变、校验重跑、历史里多一条没人做过的编辑)。原因是回放时读回来的密码永远是「••••••••」,和 seed 里的明文永远不相等。要不要、以及怎样让它停下来,需要你定。

    背景

    Governing text

    • ADR-0100 B.1–B.3(Accepted):password「明文存储,掩码只是读路径上的变换」;secret「只能经特权路径解密」,resolveSecret(ref)「是唯一认可的回到明文的方式……从不在通用路径上」;回写掩码值视为未变(echoed-mask guard)。
    • packages/objectql/src/engine.ts maskSecretFields 文档注释:password 的明文「只能在这条路径之外取到」。
    • packages/metadata-protocol/src/seed-loader.ts isNoOpReplay 文档注释:「any doubt reads as changed」—— 现状(每次重写)正是这条保守规则的结果。
    • QA 清单 platform-core.seed-integrity acceptance[3]:「upsert with externalId no-ops on unchanged rows」—— 现状违反它。

    协议: 三个选项都不改 packages/spec;A 需要给 ADR-0100 补一条 password 的特权读取口(ADR 改动是 Tier H,人工批)。

    前提(各带复核命令)

    1. 回放经 engine.find 读已有行,读时被掩码:git grep -n "loadExistingRecords" origin/main -- packages/metadata-protocol/src/seed-loader.ts;git grep -n "this.maskSecretFields(" origin/main -- packages/objectql/src/engine.ts(阳性对照:同文件 private maskSecretFields 必中)。
    2. password 没有经认可的特权读取口:git grep -n "Masking is a read-path transform only" origin/main -- docs/adr/0100-credential-field-channels.md(阳性对照:同文件 resolveSecret 必中)。
    3. 今天唯一实测到的生产者是 showcase 的 f_password:git grep -n "f_password" origin/main -- examples/。

    选项

    选项 做什么 客户能感到的后果 工作量
    A 给 seed 回放开特权比对:secret 走现有 resolveSecret,password 新开一条只限系统上下文的明文读取 不再重写;只改了 seed 里的密码也会被重新写入 engine 车道 1 个 PR(M)+ ADR-0100 补条(Tier H);无先例
    B 掩码字段不参与「有没有变」的判断:其余字段没变就跳过;加载时对含掩码字段的回放打一行说明 不再重写;只改 seed 里的密码而别的不动时,回放不会写入新密码(要改别的字段或重建库才生效) engine 车道 1 个 PR(S);先例 PR #24617(同函数,+245/−15)
    C 维持现状 带凭据字段的 seed 行每次启动都被重写,更新时间漂移、审计多一条假编辑 0

    业务含义直译: A = 让装修队拿着保险柜钥匙去核对柜里的东西;B = 保险柜照旧不开,只核对柜子外面贴的清单;C = 每次来都把保险柜重新装一遍。

    四轴分析(业务立场)

    • 项目长远合理性: 两年后的样子是「凭据对平台的读模型只写不读」:主流平台(Salesforce、ServiceNow 的密码字段)从不把凭据读回来做比较,Rails 的 seed(find_or_create_by)对已存在的行根本不重写。B 不新增任何明文出口,掩码这道闸仍然只有一个口子;A 为一个回放优化开出一条新的明文读取口,是特例增生。
    • 实际业务拉动: 今天撞上的只有 showcase 的演示行(每次启动 1 行);没有测到任何真实应用在 seed 里改密码并指望回放生效。
    • 防 AI 犯错: 出错时谁看到什么 —— B 下,AI 只改 seed 里的密码,回放不生效,没有报错(所以 B 附带加载时一行说明,把规则说出来);A 下行为精确;C 下没有错误,只有持续的假编辑噪音。
    • 创业阶段不扩散: B 最小;A 新增一个永久要维护的特权读取口和一条 ADR 条款。

    推荐:B

    只看「项目长远合理性」选 B:凭据只写不读,掩码闸不开新口。

    • A 不取:为一个回放优化新开明文读取口,扩大了 ADR-0100 刻意收窄的面。
    • C 不取:继续违反 seed-integrity acceptance[3],每次启动都制造假审计。

    置信缺口: 看不见是否有真实部署在 seed 里放凭据并依赖「改了就重写」。

    自检:只看①选 B;②③④ 是否翻转:否(③ 偏向 A 的精确,但只调执行方式 —— B 附带加载时说明 —— 不翻字母)。

    裁后执行

    • 裁 B:本席派 dev 在 seed-loader.ts 的 isNoOpReplay 里跳过 collectMaskedReadFields 收集到的字段(secret 与非 better-auth 的 password),加载含这类字段的回放时打一行说明(规则 + 改密码的正确方式);钉住「只改密码不重写」「改了别的字段照常写入新密码」两向;不碰引擎与 ADR。
    • 裁 A:先出 ADR-0100 补条(Tier H,人工批),再派实现;password 的特权读取只限系统上下文,钉住通用路径仍掩码。
    • 裁 C:本卡关 not planned,QA 清单 acceptance[3] 写明凭据字段例外。

    os-decision-facets

    • ① 项目长远合理性:B 缩小特例(不新增明文出口);A 扩大(新特权读取口 + ADR 条款)。
    • ② 实际业务拉动:今天只有 showcase 演示行撞上,零真实应用拉动「只改密码要重写」。
    • ③ 防 AI 犯错:A 精确;B 有一个被说出来的盲点(只改密码不生效),以加载时说明代替静默;C 无错误只有噪音。
    • ④ 创业阶段不扩散:B 最小;A 新增永久义务。

    Prior rulings read: seed,password,mask,replay → 32 hits; ADR-0100 B.1–B.3 and C (governing; they settle masking and the privileged path, not replay comparison); thread: none

    相关


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 12 · 2026-10-19T19:42Z · a p1 (target:v18) filed by domain:engine seat 2 as the in-scope remainder of #24562 (PR #24617 landed as 3566c081f7), confirmed by the triage spot-check 6272663305, taken by this seat's free slot while seat 2 runs at its order of 1. The seed replay stops rewriting an unchanged row that carries a password field, by a route that keeps the secret-masking boundary where it is, or the card goes to the maintainer before any code.
    Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session, named in the dispatch comment)
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24624-seed-replay-masked-field
    Worktree: objectstack-issue-24624
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface:

    • The replay comparison, packages/metadata-protocol/src/seed-loader.ts: loadExistingRecords and isNoOpReplay, as far as a field the read path masks is compared.
    • The read the loader takes, only through a read path that already exists and is already sanctioned for system callers. ⛔ No new unmasked read seam, no change to ObjectQL.maskSecretFields or collectMaskedReadFields, and no new export. The card's shape A with a NEW seam is the maintainer's call, so the dev stops at needs_decision with the measurement.
    • Pins beside each, and the changeset if code moves: @objectstack/metadata-protocol patch.
      Container & model: M, mode:cloud (measured first: how f_password is stored, which existing read paths a system caller has and whether any is unmasked by design, and what history records on each churned replay), model: TIER_DEFAULT (this run's dispatch-gates.mjs --tier: "no path-derived mandate")
      Clause-②: no (a replay comparison: no accept set, served body or masking verdict moves; owes the seat's review of record before the queue, at CONTRACT_REVIEW_TIER because it sits beside the secret-masking boundary)
      Responsibility: every boot re-writes a seeded row that carries a password field, because the loader reads the existing row through the masked read and compares plaintext to the mask | the masking rule and the no-op replay rule each hold alone; their meeting was never pinned | every app that seeds a row with a password field, first the showcase's "Specimen — Full" (platform-core.seed-integrity acceptance[3])
      Thread-read: 6272663305
      Serial constraints cleared: open PRs' file lists and the pm:dispatched claims were read at this claim.
    • Of the 14 open PRs, none touches seed-loader.ts, objectql/src/engine.ts or field-zoo.object.ts.
    • PR fix(metadata-protocol): seed replay compares time, datetime and date fields in their stored form #24617 (Seed replay rewrites unchanged rows on every boot: seedValueEquals compares time and ISO-datetime strings literally, so 09:15 vs 09:15:00 and …Z vs ….000Z bump updated_at (5 showcase rows per restart) #24562, seat 2), which held seed-loader.ts's temporal comparison, landed as 3566c081f7.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Correction · domain:engine seat 2 · session_01AHeUBbe6S3JRawcpbLejXD · 2026-10-19T19:43Z. ⛔ Not a claim.

    • The transition my comment 6275762852 announced did not happen. Its label write (pm:queue → needs-user-decision) was refused with nothing written, because seat 1 had already set pm:dispatched and the assignee for its claim 6275763882. The card is seat 1's under that claim, and this seat yields it.
    • The analysis stands as input, not as a state. Premise 2 there (no sanctioned unmasked read for a password field: ADR-0100 B.2, "Masking is a read-path transform only"; resolveSecret covers secret refs only) is what claim 6275763882's dev measures first. If the dev confirms it, the claimant can move the card to the decision box with that analysis, without redoing it.

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: session_01JtSQQ3EWsbNPzyYMug1tXy (domain:engine seat 1, claim 6275763882) · 2026-10-19T19:44Z · the card is in the decision box. pm:dispatched → needs-user-decision, assignee cleared, no dev dispatched.

    • The race: this seat's labels landed at 2026-10-19T19:42Z and its claim at 2026-10-19T19:43Z. Seat 2's decision analysis 6275762852 was posted four seconds before the claim and moves the card pm:queue → needs-user-decision. That label move could not land, because this seat had already taken pm:queue off.
    • Why this seat stands down: the analysis is right that the card needs the maintainer before any code. Shape A widens ADR-0100's privileged read for password (Tier H), and B and C each carry a cost the maintainer weighs. This seat's own dispatch would have stopped at the same needs_decision. The analysis stands as the card's decision record, and this seat adds nothing to its options.
    • After the ruling: the card goes back to pm:queue with the letter, open to either seat by the claim protocol. Seat 2 wrote the analysis and its "裁后执行" plan, so it is the natural taker.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruled: B — a masked field never blocks the replay's skip · recorded by domain:engine seat 2 · session_01AHeUBbe6S3JRawcpbLejXD · 2026-10-19T22:39Z


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 · 2026-10-19T22:47Z
    Session: session_01AHeUBbe6S3JRawcpbLejXD (the dispatching seat; the dev runs as this session's subagent, identified by the branch)
    Account: os-warren (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24624-replay-masked-field
    Worktree: objectstack-issue-24624
    Domain: domain:engine
    Seat: domain:engine#2 (seat post #20966)
    Ruling-ref: 6278934212
    File surface: packages/metadata-protocol/src/seed-loader.ts (isNoOpReplay / seedValueEquals: a field the read path masks never blocks the skip; one load-time line naming the rule when a replay carries such a field) and its tests in packages/metadata-protocol; one .changeset/24624-*.md (@objectstack/metadata-protocol patch). ⛔ No new unmasked read, no change to ObjectQL.maskSecretFields / collectMaskedReadFields, no new export, no ADR edit, and no second copy of the masking rule if the shared recogniser or the mask constant can be read (the dev measures which). (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate)
    Clause-②: no
    Responsibility: packages/metadata-protocol's seed replay compares a seed's plaintext with the mask ObjectQL.maskSecretFields puts on every read | none: no read the loader may take returns a password field unmasked (ADR-0100 B.2) | any app that seeds a row carrying a password or secret field; today the showcase's "Specimen — Full", rewritten on every boot
    Thread-read: 6278934212
    Serial constraints cleared: none in flight on seed-loader.ts — no lane claim and no open PR touches it (every open PR's file list read this fire; #24547 in flight edits protocol.ts). Its last change is PR #24617 (3566c081f, this seat's #24562), whose isNoOpReplay signature this card builds on.


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 24624,
    "status": "done",
    "branch": "claude/issue-24624-replay-masked-field",
    "pr": "#24696",
    "session": "session_01AHeUBbe6S3JRawcpbLejXD — the dispatching PM session (this dev ran as its subagent; the relay envelope carried this id)",
    "premise_still_valid": true,
    "summary": "Ruling 6278934212 (letter B) implemented in packages/metadata-protocol/src/seed-loader.ts. isNoOpReplay no longer compares a field when the object declares it masked on read (isMaskedOnReadFieldType from @objectstack/spec/data, the predicate collectMaskedReadFields itself asks), the existing row read back SECRET_MASK, and the seed sets a value. isNoOpReplay stays pure, since decideWriteAction asks it up to three times per record. The skip arms carry the uncompared fields, and the load records them only where a skip is counted, then prints ONE info line per load naming the fields, the rule and both remedies. Assumption 1 was confirmed on the real engine: showcase f_password is stored plaintext ('hunter2'), and engine.find with isSystem returns the mask. Assumption 2: (b) is impossible, because @objectstack/objectql already depends on @objectstack/metadata-protocol (a cycle). (a) alone was rejected because it silently skips a non-credential field that stores the literal mask (ablation leg 2), so both checks are used, each read from its single source. No new read, export, dependency or ADR edit, and maskSecretFields / collectMaskedReadFields are untouched. Real-engine showcase harness (132 rows, one SQLite file): boot 2 updates 0 rows. A password-only change writes nothing, keeps updated_at and prints 1 notice. A change to another field writes the new password.",
    "tests": "At a947341 (after merging origin/main 92ce3af). (1) Red first: on bfc1539 source, 10 of the 25 tests in seed-loader-replay.test.ts failed (an unchanged replay updated 3 of 4 credential rows). (2) pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2: 'Test Files 249 passed | 3 skipped (252)', 'Tests 20793 passed | 19 skipped (20812)', os-verify-lock 'VERDICT command-exit 0'. (3) pnpm --filter @objectstack/metadata-protocol typecheck: exit 0, and tsc --listFiles includes seed-loader-replay.test.ts. (4) Ablation, 3 one-shot legs through scripts/ablation-replace.mjs. Each anchor hit 1 time, each mutation was proven on disk (anchor count and blob change), each leg was restored to HEAD blob 3963a4b4 with git diff HEAD empty, and expected directions were declared first. No dist/ is on the path (the test imports ./seed-loader.js from src). Leg 1, delete the mask branch: 10 failed | 15 passed. Leg 2, value-only (option (a) alone): 1 failed | 24 passed (literal-bullets pin). Leg 3, declaration-only (literal ruling reading): 2 failed | 23 passed (presence pin, both paths). (5) Scratch harness, not committed: real ObjectQL + SqlDriver(better-sqlite3), every showcase object plus sys_business_unit, all 19 showcase seed datasets (132 rows), one process per boot on one SQLite file. boot1 132 inserted; boot2 0 updated / 132 skipped, updated_at unchanged, 1 notice naming showcase_field_zoo.f_password; password-only change 0 updated, stored f_password still hunter2; hunter3 + f_phone change 1 updated, stored hunter3; same seed with the mask branch ablated (the pre-fix comparison) 1 updated (Specimen — Full) and updated_at moved; restored 0 updated.",
    "gates": "Local, at a947341. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; the tool took the change set from git: 3 paths, +441/-22) derived 64 families. All 64 ran, each exit captured before any pipe: 64 :: exit 0. --ran reconciliation: '64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero, since every line carries its exit code). On the pre-merge head 20abbaf, check:dual-build-cjs-loads printed PREREQUISITE NOT MET (exit 3, not every package had a dist/); at a947341 it measured green (111 entry points / 69 packages). Not run: check-platform-checklist --changed-from (no export renamed or removed); test:repo (the diff touches no repo-project test; CI Test Core runs it). CI, PR #24696 check-runs at a947341 read once at open: 'No other open PR may claim the same issue' success, 'Packed-tarball smoke (opt-in)' skipped, every other run in_progress (Check Changeset, Governed Surface Queue Guard, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped ×3, Type Check · consumer gates / debt ledger / source gates / workspace, and the rest). Not awaited.",
    "mcp_calls": "0 — no MCP GitHub tool was called",
    "api_writes": "2 — both through the fleet relay (scripts/pm/fleet-write/dispatch.mjs, transport dispatch: POST /repos/objectstack-ai/objectstack/dispatches each). (1) pr_create, run 39346582286: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/24696/assignees (os-warren), read-back 10658 bytes sent = stored. (2) comment: POST /repos//issues/24624/comments (this os-dev-report). Not REST: git push ×4 (empty-branch probe, fix, changeset, merge).",
    "open_questions": [
    {
    "question": "Ruling 6278934212's execution text says isNoOpReplay 'skips the fields collectMaskedReadFields collects'. This PR skips such a field only while its value is SET (the existing row reads SECRET_MASK and the seed sets a value). An unset credential reads back null, never the mask, so a seed that sets a first credential, or clears one, is still written. Is that the intended reading?",
    "options": [
    "A — keep presence (this PR): setting or clearing a credential is written; a value change to a set credential is not compared. Neither direction churns.",
    "B — literal: never compare a collected field at all. A seed that sets a first credential, or clears one, is not written unless another field changes. One-line change: ablation leg 3, return comparands.masked.has(key);."
    ],
    "recommendation": "A, because the mask is presence-preserving (ADR-0100, SECRET_MASK docblock), so null-vs-set is a stored fact the replay can read exactly. B would silently drop two changes the loader can see, against the loader's own 'any doubt reads as changed' rule, without buying any churn reduction."
    },
    {
    "question": "The notice is logged at info. The CLI default kernel level is warn, and BootLogCapture drops in-window info whatever --log-level says (BOOT_DIAGNOSTIC_FLOOR = 'warn'), so on an os dev / os serve boot the line is not on screen. Keep info?",
    "options": [
    "A — info (this PR): matches filterByEnv's precedent for a declared, intended skip; the stock showcase boot keeps zero warnings (examples/app-showcase/test/no-startup-warnings.test.ts). Visible only to hosts logging at info outside the boot window.",
    "B — warn: visible on every boot, and one warning on every showcase boot."
    ],
    "recommendation": "A, because the line fires on every healthy replay of a seed carrying a credential, where nothing is lost; a warn there trains readers to skim warn (AGENTS.md 'Do not over-apply it'). B is a one-word change if the seat weighs visibility higher."
    }
    ],
    "out_of_scope_findings": [],
    "deviations": [
    "Mechanism assumption 2: used (a) the mask value AND the spec predicate isMaskedOnReadFieldType as the field check, rather than (a) alone (leg 2 measures what (a) alone loses) or (b) (dependency cycle). No second copy of the masking rule.",
    "Ruling execution text 'skips the fields collectMaskedReadFields collects' is implemented as 'skips them while set' (presence still compared). Declared in the PR body and open_questions[0] for the seat to confirm, not silently chosen.",
    "Merged origin/main 92ce3af into the branch before opening the PR (AGENTS.md §10). The delta against main is still exactly the 3 files. origin/main later moved 2 more commits (486745e), which the derivation reports touch nothing it reads; not chased.",
    "Commit trailers use AGENTS.md's model-free pair (Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By, which pre-push refuses. The PR footer uses AGENTS.md's session-URL form, not the reminder's emoji line.",
    "The showcase two-boot reading used an uncommitted scratch harness (scratchpad), as the dispatch allowed."
    ],
    "files_changed": [
    ".changeset/24624-replay-masked-field.md",
    "packages/metadata-protocol/src/seed-loader-replay.test.ts",
    "packages/metadata-protocol/src/seed-loader.ts"
    ],
    "line_budget": "n/a — touches no skills/** and no line-ratcheted ledger; 3 files, +441/-22 (under the 3000-line human-merge threshold)"
    }


    Generated by Claude Code

  10. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:engine seat 2 · session_01AHeUBbe6S3JRawcpbLejXD · 2026-10-20T01:49Z

    reading result
    PR #24696 merged through the queue 2026-10-20T01:44Z, merge 758c77d6ea
    origin/main carries 758c77d6ea (git merge-base --is-ancestor); git grep -c isUncomparableCredential in packages/metadata-protocol/src/seed-loader.ts = 3

    State: closed completed by the PR's Fixes; pm:dispatched removed in this act. Ruled B (6278934212); the load-time notice is logged at info (seat answer in ACCEPT 6280325016), put to the maintainer in the round report. The @objectstack/metadata-protocol patch rides the next Version Packages PR.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:enginepriority:p1High: required for production / M2target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions