Repository navigation
decision(plugin-auth): sys_audit_log rows the admin identity endpoints wrote before #21174's fix still carry user-field values in free metadata. Scrub them with a one-time migration, or leave the append-only ledger untouched? #21198
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actions更正:本卡的推荐由乙改为甲 ·
domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-01T15:55Z立卡时本席漏读了一条直接适用的维护者先例。现在补上,推荐随之改变。
先例(Governing text): #20790 的 Q1 = B(
5909514730,维护者「20790 同意」):"Q1 = B. Rotate, don't scrub. … History rows and audit snapshots stay append-only. ⛔ No rewrite of history or audit tables."
那张卡问的是同一个问题:一项敏感值在修复前已经落进审计快照,要不要改写审计表把它擦掉。维护者裁的是不改写。
和本卡的差别(如实呈现): #20790 的密钥可以轮换,旧值轮换后就作废了。本卡存量行里的值是用户字段值(标识、电话、角色标量、强制改密标志),没有"轮换"这个动作:换了号码,旧号码依然是一条真实信息。所以对本卡来说,选甲意味着已测到的泄露对存量行永久存在,而不只是"存在到轮换为止"。
修订后的推荐:甲(不改写),与先例一致。
- ① 长远:先例对 ① 的读法是"审计只追加、写入点唯一"。本卡沿用,不为一类旧行开改写口子。
- ② 拉动:受影响的只有升级前用过管理员建用户入口的部署,而且读者必须持有账本授权。
- ③④ 不翻转。
- 如果您认为"值不可轮换"足以构成例外,那就选乙。乙的形态不变:由生产端拥有的一次性迁移,只删封闭决定集之外的键。
你要做的(一个动作)不变: 回复 甲 / 乙。
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsRuling: batch #264 item 3 · letter 甲 · maintainer 「同意264」 2026-10-01T23:02Z
Director seat, summon #32,
session_016tKoy8NJa35Yih1FdzrVmn. Written asobjectstack-fleet[bot]through the relay. ⛔ Classes and positions only.- How it was ruled. Batch 🔗 Broken links detected in documentation #264 was presented in the live director chat. This card was item 3, director recommendation 甲, fallback 乙. The seat's own corrected recommendation (5935199195) was also 甲. The maintainer answered the batch as presented: 「同意264」.
- Freshness gate: no comment after 5935199195.
The ruling
甲: the
sys_audit_logrows written before #21174's fix stay as they are. ⛔ No one-time migration rewrites them. Closednot_planned: the migration is not done.Governing text.
- ADR-0052 (accepted):
sys_audit_logis 「immutable, append-only, retained, security-gated」 and 「immutable, compliance-grade」 (docs/adr/0052-audit-is-not-the-activity-feed.md:36,:80). An accepted ADR binds until a superseding one says otherwise (AGENTS.md Prime Directive [WIP] Add Chinese version of the documentation #13). Option 乙 would have needed a dated note under that decision. - security(flows): move a flow's inbound-hook secret out of flow metadata into the write-only secret seam #7799 established — no read, the generic data door included, returns it #20790 Q1 B (5909514730): 「⛔ No rewrite of history or audit tables」. That precedent's own reason was rotation, which these values do not have, so it is not inherited mechanically (the SKILL's rule: the inheritance carries the reason). The binding text here is ADR-0052.
Why it holds.
- Ledger exposure is answered on the read side: PR fix(plugin-audit): the compliance ledger's before/after snapshots serve a parent field only to a reader the security service serves that field (#21155) #21171 narrows fields, and security(plugin-audit): sys_audit_log has no parent-record read gate, so a ledger reader is served the rows about a record the data plane answers 404 to (the ledger's #20833) #21175 (A, ruled; PR fix(plugin-audit): a read of the compliance ledger returns only the rows about records the caller can read (#21175) #21194) gates rows by the parent record. Under security(plugin-audit): sys_audit_log has no parent-record read gate, so a ledger reader is served the rows about a record the data plane answers 404 to (the ledger's #20833) #21175, a reader must be able to read the subject user record to be served these rows at all, so the residual population is narrow.
- Every compliance fact (who, when, on whom, what) also stays in the CRUD-mirror rows, narrowed per reader.
四棱(本裁决新记录)
- ① 长远:审计日志只追加、不可变(ADR-0052)是合规根基;泄露在读取侧处理,不开「一次性改写」的先例。
- ② 拉动:只限升级前用过管理员建用户 / 设密码入口的部署,且读者须能读该用户记录(security(plugin-audit): sys_audit_log has no parent-record read gate, so a ledger reader is served the rows about a record the data plane answers 404 to (the ledger's #20833) #21175 之后)。
- ③ 防 AI:乙的键集封闭可控,但改写口子本身是风险;甲零动作。
- ④ 不扩散:零改动。
- 只看①选甲;②③④ 是否翻转:否。
Execution
- No code. The note owed: if security(plugin-auth): the compliance-ledger row an admin create-user writes carries a withheld user field's value in its decision metadata, which a ledger reader withheld that field is served #21174's changeset (PR fix(plugin-auth): the admin identity rows on the compliance ledger record the admin's decisions, never a value of a user field (#21174) #21195) has not shipped yet, the
domain:servicesseat adds one sentence to it saying that rows written before this release keep their metadata (ruling 甲 here). This record is the maintainer's word for that edit of a pending changeset. If it has shipped, this record is the note. - What reopens it: a reversal of 甲, or an erasure obligation (a data-subject request). That would be a separate capability with its own card.
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (ruling execution on a closed card) · 2026-10-02T02:26Z
Session:session_01DiCSbmJrkzNhuEAier4VoJ
Account:os-bill(the seat's linked user)
Branch:claude/issue-21198-changeset-note
Worktree:objectstack-issue-21198
Domain:domain:services
Seat:domain:services#2(seat post #21118)
File surface:.changeset/21174-admin-audit-metadata.mdonly. One sentence is added, saying that rows written before this release keep theirmetadata, as ruling 甲 decides (5942375063, maintainer 「同意264」). Nothing else. Stop on breach; explain in the report.
Container & model:S,mode:subagent,model: sonnet(one sentence in a pending changeset; no code).
Clause-②: no
Thread-read: 5942375063
Serial constraints cleared, read in this act againstorigin/mainat748b24072: the changeset is still pending onmain(not consumed by a release), and no open PR touches it.The foreign-changeset gate (#17712), declared before the build. This PR edits a pending changeset it did not add, so
Check Changesetgoes red on the PR by design. Ruling5942375063is the maintainer's confirmation of that edit ("This record is the maintainer's word for that edit of a pending changeset"). The by-design-red exception needs three conditions, all checked at landing: (1) the gate's own source says the red is by design on a pushed branch (.github/workflows/pr-automation.yml:728: "The red is the point … 'Check Changeset' is not one of the required contexts"); (2) the check does not run onmerge_group; (3) a PR comment names the gate and the cause. If the changeset has shipped by the time this lands, the ruling record itself is the note and this PR closes unmerged.
The card stays closednot_planned(the migration is not done). The PR body readsRefs #21198, never a closing keyword.
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
- added a commit that references this issue
on Oct 2, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21198, "status": "done", "branch": "claude/issue-21198-changeset-note", "pr": "https://github.com/objectstack-ai/objectstack/pull/21300", "session": "session_01DiCSbmJrkzNhuEAier4VoJ", "premise_still_valid": true, "summary": "Ruling 甲 execution note, no code. Draft PR #21300 appends ONE sentence to the last paragraph of .changeset/21174-admin-audit-metadata.md, right after 'Rows written before this release are stored data and are not rewritten.': 'They keep the values their `metadata` already holds: the ledger is append-only (ADR-0052), so they stay as written by decision (#21198).' (the PM's suggested wording, unchanged; 1 file, +1/-1; no other edit). Premise held: the changeset is still on origin/main at 4e6dc2338 (so no release consumed it), plugin-auth CHANGELOG.md has no 21174 hit, PR #21195 merged as 55012df30, and none of the 16 open PRs (Version Packages #20639 included) lists the file. `Check Changeset` is red by design (#17712): locally `node scripts/check-empty-changeset.mjs --base origin/main` exits 1 with 'This PR changes a changeset it did not add'; it is not a required context (scripts/check-required-contexts.mjs REQUIRED_CONTEXTS, line 368, pins seven: Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard; AGENTS.md:518-522 lists the same seven) and does not run on merge_group (.github/workflows/pr-automation.yml:41-43 triggers on pull_request only, zero merge_group occurrences in the file; the job's own text at :728-730 says 'The red is the point ... not one of the required contexts'). No label and no changeset of this PR's own was added (skip-changeset is refused for this class, ruling D on #18375). PR #21300 is draft, assignee os-bill, no labels; body stored byte-identical (4176 bytes, diff against the sent file empty), first lines 'Refs #21198' and 'Clause-②: no', no closing keyword; the single PR comment (5944572645) names the gate, the cause and the confirmation (ruling 5942375063). CI on the PR had no check-runs yet at my one read (not polled); convergence is the PM's. Two deviations: (1) the commit trailer pair is model-free (Claude-Session + Co-authored-by: Claude) per os-dev.md, not the harness reminder's model-named Co-Authored-By, because the pre-push hook refuses a model identifier; (2) the PR body footer is the dispatch order's session-URL form, not the harness reminder's alternative form. One observation worth the PM's eyes: ADR-0052's own status line (docs/adr/0052-audit-is-not-the-activity-feed.md:3) reads 'Proposed (2026-06-16) - partially implemented', while ruling 5942375063 calls it 'accepted' and leans on Prime Directive #13 for it; the sentence I added cites the ADR only for the property it declares (append-only, :36 and :80; the code agrees: packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts:22 managedBy 'append-only'), not for its status, so it is true either way, and the ruling stands on the maintainer's word 「同意264」 regardless.", "tests": "No code, no build, no test run (os-verify-lock not used). Gates derived at the final head 98cd8b924 (git rev-parse --short HEAD) with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (no paths; 1 path, +1/-1, 98cd8b924 vs merge base 4e6dc2338): 19 commands, each run with output redirected before the exit code was read. exit 0: check-adr-0087-registration.mjs --base origin/main ('this PR adds no declared-breaking changeset'); check-adr-0087-registration.mjs --self-test; check-changeset-no-major.mjs --base origin/main ('This diff introduces no `major` bump.'); check-changeset-no-major.mjs --self-test; check-closing-keyword-parity.mjs and its --self-test; check-comment-mask-corpus.mjs; check-empty-changeset.mjs --self-test; scripts/pm/release-rehearsal-clone.mjs --self-test; pnpm check:changeset-gate-self-tests; check:driver-memory-census; check:gitlink-declared; check:nul-bytes ('OK (scanned 9939 text file(s) ... no raw ASCII control bytes)'); check:objectui-changeset; check:pm-changeset-deadline-census; check:published-files; check:refd-timer-probe; check:watch-hint-literal. exit 1 (the by-design red, the only red): node scripts/check-empty-changeset.mjs --base origin/main, printing 'This PR changes a changeset it did not add: .changeset/21174-admin-audit-metadata.md present on the merge base and CHANGED by this PR'. Reconciliation (`--ran`, exit codes recorded as 'cmd :: exit N'): 'Run reconciliation - 19 derived, 19 run, 0 NOT-MEASURED, 0 UNRUN.' Outside the derivation, also run: node scripts/check-changeset-fixed.mjs exit 0 (roster under .changeset, flagged by the derivation) and pnpm check:required-contexts exit 0 ('7 required context(s) pinned'). Byte hygiene: control-byte scan over the changeset, the PR body and the comment texts empty. NOT MEASURED: repo-wide lint/typecheck lanes and the 59 artifact-roster families (CI-owned; diff touches no TypeScript); CI on PR #21300 (no check-runs existed on 98cd8b924 at my single read, no polling; convergence is the PM's). Shallow checkout (git rev-parse --is-shallow-repository true): no ancestry or history claim is made; 'not consumed by a release' rests on the file's presence on origin/main plus the absent CHANGELOG hit, not on an is-ancestor read. Hygiene: no managed block appeared in AGENTS.md (git status clean at the end); worktree node_modules removed and the worktree removed without --force.", "mcp_calls": "0 - no MCP calls of any kind; all reads were gh api GETs and local commands", "api_writes": "4 REST writes, all through the scripts/pm fleet-write relay as objectstack-fleet[bot] (each is one repository_dispatch to objectstack-ai/objectstack, executed by the relay run, each read back): (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls (draft), request fw-20261002T023613Z-03f35e, run 36956395960, read-back 4176 bytes sent / 4176 stored identical, PR #21300; (2) label-write.mjs --issue 21300 --assign os-bill, POST /repos/objectstack-ai/objectstack/issues/21300/assignees, run 36956437364, read-back matches (assignee os-bill, 0 labels); (3) the one PR comment, POST /repos/objectstack-ai/objectstack/issues/21300/comments, run 36956486696, comment 5944572645, 1141 bytes sent / stored identical; (4) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/21198/comments. Not REST: two git pushes of claude/issue-21198-changeset-note (the empty-branch probe at 4e6dc2338, then 98cd8b924). No second Claim comment, no PATCH, no label write beyond the assignee, no write to the card's assignee. Reads (not writes): gh api GETs of issue 21198 and its comments, the open-PR list and per-PR file lists, PR 21195, PR 21300 and its comments, and one check-runs listing.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed (an observation, not a class a/b/c) · ADR-0052's status line (docs/adr/0052-audit-is-not-the-activity-feed.md:3) reads 'Proposed (2026-06-16) - partially implemented' while ruling 5942375063 cites it as 'accepted' and applies Prime Directive #13 ('an accepted ADR binds') to it; the ruling's outcome does not depend on it (maintainer word 「同意264」, and the append-only property is also declared in code at sys-audit-log.object.ts:22), but a later reader following the ruling's chain will hit the mismatch. docs/adr/** is a governed surface, so no edit was made. Dedupe words: ADR-0052 status Proposed accepted audit ledger append-only" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsACCEPT (seat review; lands under the by-design-red exception) · PR #21300 @
98cd8b924·domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-02T02:42ZBasis: the dev report
5944586903, the execution claim5944467576, ruling 甲5942375063(maintainer 「同意264」), and the diff.- Shape: draft, base
main. Line 1 isRefs #21198(no closing keyword: the card stays closednot_planned), line 2Clause-②: no. The footer carries the session URL. - Scope: 1 file, +1/−1,
.changeset/21174-admin-audit-metadata.mdonly, inside the claim. The premise holds: the changeset is still pending onmain, andplugin-auth's CHANGELOG has no security(plugin-auth): the compliance-ledger row an admin create-user writes carries a withheld user field's value in its decision metadata, which a ledger reader withheld that field is served #21174 entry. - The sentence, checked: "They keep the values their
metadataalready holds: the ledger is append-only (ADR-0052), so they stay as written by decision (decision(plugin-auth): sys_audit_log rows the admin identity endpoints wrote before #21174's fix still carry user-field values in free metadata. Scrub them with a one-time migration, or leave the append-only ledger untouched? #21198)." It is true on both counts. ADR-0052 declares the ledger append-only (docs/adr/0052-audit-is-not-the-activity-feed.md:36,:80), and so does the code (sys-audit-log.object.ts:22,managedBy: 'append-only'). "By decision" is ruling 甲. The sentence makes no claim about who is served those rows. It follows "Rows written before this release are stored data and are not rewritten." and adds only what the ruling owed. - The by-design red (finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR's
minorchangeset and every gate stayed green #17712), all three conditions:- The gate's source says the red is by design and that the check is not required (
.github/workflows/pr-automation.yml:728-730). The seven required contexts are pinned inscripts/check-required-contexts.mjs(REQUIRED_CONTEXTS), andCheck Changesetis not among them. pr-automation.ymltriggers onpull_requestonly, so it does not run onmerge_group.- The PR comment
5944572645names the gate, the cause and the confirmation.
- The gate's source says the red is by design and that the check is not required (
- Gates: 19 derived, 19 run. The only red is
check-empty-changeset.mjs --base origin/main("This PR changes a changeset it did not add"), the by-design one.
Noted for the director's ledger (no card): ruling
5942375063cites ADR-0052 as "accepted" and applies Prime Directive #13 to it. The ADR's own status line (:3) reads "Proposed (2026-06-16) - partially implemented". The ruling's outcome does not depend on that status: it stands on the maintainer's 「同意264」, and the append-only property is declared in code too. A later reader following the ruling's chain will still meet the mismatch.docs/adr/**is governed, so nothing is edited here.Landing: once every check except
Check Changesetis green on this head.
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
- Shape: draft, base
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsExecution record: the note is this ruling record (the changeset shipped first) ·
domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-02T02:48Z- The pending
.changeset/21174-admin-audit-metadata.mdwas consumed by the version commit617f25f8a(chore: version packages #20639) before PR docs(changeset): the admin-audit changeset notes that rows written before the release keep their metadata (#21198) #21300 could land. Its entry is inpackages/plugins/plugin-auth/CHANGELOG.md:81, and it ends "Rows written before this release are stored data and are not rewritten.", a true statement. - PR docs(changeset): the admin-audit changeset notes that rows written before the release keep their metadata (#21198) #21300 (one added sentence) is closed unmerged (
5944662173), as ruling5942375063provides: "If it has shipped, this record is the note." The card stays closednot_planned. Release notes are release-owned; the seat does not edit them. - Lesson for the seat (no card): an "edit a pending changeset" note races the six-hourly Version Packages PR. The claim's serial read ("still pending on
main") was true when read, but chore: version packages #20639 was open and merged minutes later. Next time, the claim reads whether the open Version Packages PR already lists the changeset. - Noted for the director's ledger: the ruling cites ADR-0052 as "accepted"; the ADR's status line (
:3) reads "Proposed (2026-06-16) - partially implemented" (see the ACCEPT5944619258).
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
- The pending
Ruled: 5942375063 · letter 甲 · 2026-10-01T23:03Z
立卡门 ②:只有维护者能做的决定(改写一张 append-only 的合规表)
⚠️ 安全类卡:只写类别与位置,不写复现
动手的读者:维护者裁甲或乙;若裁乙,
domain:servicesseat 2(#21118)把迁移派给一名 os-dev,由 PR 落在plugin-auth查重:
mcp__github__search_issues"scrub existing audit log rows migration rewrite ledger metadata legacy values",6 条命中(含 closed),全部不是本题。最近的是 #21175(读侧父记录门,不涉及存量改写)和 #8147(action 枚举退役)维护者速读
#21174(PR #21195)修的是生产端:从这次发布开始,
plugin-auth的管理员建用户 / 设密码两个入口写的显式账本行,metadata里只放管理员的决定,不再复制写进用户字段的值。可是升级前写下的那些行,metadata里还是那份副本。只要读者持有账本授权,就能读到它们,包括数据面对这些字段正在屏蔽的读者。分诊已经排除了读时过滤(
5932908541:在自由 metadata 上再推导一遍遮罩,等于造第二套遮罩)。所以存量行只剩两条路:原样保留,或者一次性迁移把它们洗掉。账本是 append-only 的合规表,改不改写它属于安全边界,席位不能代决。你要做的(一个动作): 回复 甲 / 乙。
事实(类别级)
packages/plugins/plugin-auth/src/admin-user-endpoints.ts的writeAdminAudit,事件为user.admin_created和user.admin_password_set。metadata带四类写进用户字段的值,password-set 行带一类。其余键(事件名、是否生成密码、占位地址、成员绑定、组织引用)是决定,迁移后保留。选项
plugin-auth)拥有的一次性迁移:只处理这两个事件的显式行,删掉不在封闭决定集里的metadata键,其余列不动sys_migration留收据plugin-audit,不在本车道范围。列出来只是为了完整四维
关联
Generated by Claude Code ·
domain:servicesseat 2 ·session_01DiCSbmJrkzNhuEAier4VoJ· https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ