You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A connector auth key drained on save (#28640 ②B) still sits in cleartext at rest in sys_metadata_history snapshots #29484
Ruled: 6677709817 · letter d (c, then b) · 2026-11-06T05:28Z
Ruling-ref: 6677709817
Filing gate: ① an out-of-scope finding (class b, carrier: the seat) the #28640 dev noted on PR #29456 (os-dev-report 6581427705) and the seat's contract review confirmed (record 6581889597). ⛔ Not a claim. No grade: triage sets it.
What was measured
PR #29456 (ruling ②B, 6566912252) makes a save of a connector's served form drop a leftover undeclared auth key (for example a legacy auth.token) from the live sys_metadata row, and tells the author with a withheld-key-dropped advisory. The sys_metadata_history snapshots written before that save keep the pre-drain body, value included.
A read of a history row is served through the stored-row seam (redactStoredMetadataRow(s) in packages/spec/src/kernel/metadata-type-redaction.ts), which withholds the key, so there is no cleartext exit through the API. The value is cleartext only at rest, in history rows, for as long as history is retained.
Wanted
The maintainer's call on whether at-rest history must be scrubbed too: (a) leave it, documented (history is served redacted; retention bounds it); (b) a one-time scrub of undeclared auth keys from connector history snapshots, as a migration step; or (c) withhold at write time for future history rows only. Any option that rewrites stored history is a data change and goes to the maintainer first.
Reader who acts: domain:server (metadata history writer) with the domain:spec redaction owner, after triage.
Director seat (the maintainer's decisions thread). Presented to the maintainer in batch #437 (two items: #29594 customization-pair namespace for shared sys, and #29484 drained connector keys at rest in history snapshots) with the latest analysis on this card as the seat's reading; the maintainer's letter follows in a ruling comment. Rule level only on the security item.
Ruling: batch #437 item 2 · letter d (c, then b) · maintainer 「29484 同意」 2026-11-06T05:26Z
Director seat (the maintainer's decisions thread). Presented in batch #437 (brief-claimed here at 6677323648; detailed letter in the project's decision doc, batch 6, rule level). The maintainer answered at 2026-11-06T05:26Z, verbatim: 「29484 同意」 = the seat's recommendation, d = (c) first, then (b).
Workload:domain:server (history writer) with the domain:spec redaction owner · 1–2 PRs · S–M · contract review on (c) · the scrub rehearsed on a copy before it runs anywhere.
Execution: the server lane dispatches (c) with Ruling-ref: naming this comment, the count + scrub script following; ⛔ no stored-secret specifics on this card. Label strokes are the dispatching seat's.
State: ruled, not dispatched → pm:queue · 2026-11-07T14:52Z
⛔ Not a claim.
Ruling, re-read now: letter d, meaning (c) first and then (b); maintainer 「29484 同意」, 2026-11-06T05:26Z (6677709817, director#2). Nothing before (c) waits on the maintainer, so needs-user-decision comes off, on the maintainer's 「可以」 (2026-11-07). The (b) scrub keeps its own maintainer step, as the ruling says: a read-only count is confirmed before any rewrite.
Half-state fixed: the ruling left the label strokes to the dispatching seat, and none were made. No PR names this card, so it had no pm:* state and no seat could see it. It is now pm:queue for domain:server, starting with step (c) under Ruling-ref: 6677709817.
Claim: PM loop round 1 · 2026-11-07T17:16Z
Session: session_01FEkQ5nFAEGcShB48YZExSP (card thread, dev) · seat session_01FEkQ5o8PE7ZtVjcor51wBZ
Account: os-project-manager
Branch: claude/project-thread-aru8xa
Worktree: objectstack-issue-29484
Domain: domain:server
Seat: domain:server#5
File surface: packages/metadata/** (the sys_metadata_history writer) + tests, root .changeset/; the shared drain predicate's current home only if it must move; ⛔ packages/spec/**
Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: thread default (order asked xhigh; the coordinator sets effort only on the maintainer's word)
Clause-②: no (narrowing) — Ruling-ref 6677709817; CONTRACT_REVIEW_TIER review owed before the queue
Metadata-protocol: none
Responsibility: packages/metadata history writer (objectstack) | the read exit redactStoredMetadataRow(s) already withholds it, at rest only | reached by every save of a connector carrying an undeclared auth key
Thread-read: 6708495892
Premise: holds: ruled d = (c) then (b) (6677709817, re-read by triage 6708495892); this claim covers (c) only
Serial constraints cleared: none — no pm:dispatched card claims the history writer
Scope: Step (c) only, PR says Refs #29484; (b) count + scrub follows under its own maintainer step. Ruling-ref: 6677709817 Order: /mnt/project-files/pm-orders/server/order-29484.md. Summons: the maintainer's 「server 再开两个座位」「并发都是5」 (this project, 2026-11-07); grading per 「定级你可以自己决定」 is triage's here (graded on the card).
Claim: PM loop round 1 · #29484 · 2026-11-08T18:54Z · step (b) first half only (p2; re-queued by triage 6725104872)
Session: session_01ADHkmPF1skfu2kx89BEXZi
Account: os-steve
Branch: claude/project-thread-l65x8h (the card thread's harness-pinned branch; platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-29484
Domain: domain:server
Seat: domain:server#1
File surface: packages/metadata/src/ and packages/metadata-protocol/src/ (a read-only count over history snapshots, reusing the step (c) predicate), the nearest existing one-time data command if one fits, their tests, a changeset (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default tier, effort: high
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6677709817
Responsibility: n/a — step (b) of a ruled card; step (c) landed as PR #31242
Thread-read: 6725104872
Premise: holds (read at 99364ea7: step (c)'s write-time withholding is on main (PR #31242); no count or scrub over existing history exists)
Serial constraints cleared: no open PR touches history-snapshot-body.ts. ⛔ Read-only: no rewrite, no run against any live or shared database. Per #29111, no specifics of any count go on this card or the PR; the second half (the rewrite) waits on the maintainer's confirmation and is not this claim. The PR says Refs #29484.
Order: standard order + server-seat1/29484-delta.md (project files).
{
"issue": 29484,
"status": "done",
"branch": "claude/project-thread-l65x8h",
"pr": "https://github.com/objectstack-ai/objectstack/pull/31655",
"session": "session_01ADHkmS3xwR94UULpFfP1br",
"premise_still_valid": true,
"summary": "Step (b) first half of ruling 6677709817 only (PR says Refs #29484): scrubHistoryDrainedKeys(driver, { mode: 'count' }) in packages/metadata/src/migrations/history-drained-keys.ts, exported from @objectstack/metadata/migrations. It pages sys_metadata_history by primary key through a driver's find only and counts a row exactly when historySnapshotBody (PR #31242's helper, imported) would store a different body today; it reports affected rows, distinct (type, name) items, opaque row handles, and lists unreadable / unscrubbable rows beside the count. Only the count mode is wired; any other mode throws before a read. Step 0: the nearest one-time data tool convention for history is @objectstack/metadata/migrations (driver-level one-off migrations, same package as the predicate) beside metadata-protocol's recorded-by plan and its os migrate recorded-by door; no os migrate subcommand is added here (open question 1). The copy: a seeded driver-sqlite-wasm FILE, copied, with only the copy opened for the count.",
"tests": "Head 7b4b2203. New packages/metadata/src/migrations/history-drained-keys.test.ts: 9 passed (seeded rows (i) counted, (ii) declared-only connector, (iii) non-connector, (iv) row written through the current DatabaseLoader writer not counted; delete row scanned not counted; non-JSON body listed; rows byte-identical after the run; result carries no fixture value or key name; non-count mode refused with the reader never called). pnpm --filter @objectstack/metadata test: Test Files 71 passed (71), Tests 981 passed (981); typecheck exit 0, the new files in its program (tsc --listFiles). Ablation A via scripts/ablation-replace.mjs on the predicate (... !== body -> ... !== body || true): anchor 1 -> 0, blob 058420598100 -> 8b8768ee5c6a, 4 failed | 5 passed (9), first red: expected [ …(5) ] to deeply equal [ 'h_seed_01', 'h_seed_02' ]; (ii)/(iii)/(iv) red as not-include; restored blob == HEAD, git diff HEAD empty. Ablation B on the mode guard: blob 058420598100 -> 8b4043f100a5, 1 failed | 8 passed (9): promise resolved instead of rejecting; restored blob == HEAD, git diff HEAD empty. The suite imports src, so no rebuild leg.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on 7b4b2203: 60 families, all exit 0; --ran: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN. First pass had three exit 3 on prerequisites, re-run green: check-plugin-teardown-shape --self-test (fixture commit 621a4876 fetched), check:dual-build-cjs-loads and check:lean-entry-closure (after turbo run build --filter='./packages/**', 81/81). eslint --no-inline-config packages/metadata: exit 0. CI check-runs: in_progress at report time, not waited on.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "4 — mcp__github__issue_read x3 (card get, get_comments x2), mcp__github__pull_request_read x1 (PR 31655 get, draft + body read-back); all reads, no write tool.",
"api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 42515989605, PR #31655, 6814 bytes sent == stored, no labels: the diff publishes, so a patch changeset and no skip-changeset; no assignees); post-stamped.mjs comment on #29484 (this report). Plus git push x2 (not REST).",
"files_changed": [
".changeset/29484-history-drained-keys-count.md",
"packages/metadata/src/migrations/history-drained-keys.test.ts",
"packages/metadata/src/migrations/history-drained-keys.ts",
"packages/metadata/src/migrations/index.ts"
],
"line_budget": "+379 / -0 over 4 files (effort M); under the 3000-line human-merge threshold",
"deviations": [
"Card delta named the count's subject as undeclared auth keys on connector snapshots; the imported predicate is step (c)'s whole not-carried-forward set, so the count covers every position step (c) withholds (open question 2).",
"No os migrate subcommand: the tool ships as a library function only (open question 1)."
],
"open_questions": [
{
"question": "Wire an operator door for the count before the second half? Today the count runs only by calling the exported function with a driver opened on a restored copy.",
"options": [
"A: a small follow-up PR adding a count-only os migrate subcommand (read-only boot like os migrate recorded-by), registered in the CLI's migrate pins (json-stdout purity, exit-code, confirmation-exit); the second half adds --apply through the ADR-0119 D2 journal",
"B: no door until the second half; whoever holds a deployment's database runs the function by hand on a restored copy"
],
"recommendation": "A, because the maintainer must confirm the count BEFORE the rewrite exists, so the count needs an operator door now, and the CLI pins are paid once for the command rather than twice"
},
{
"question": "The count's predicate is step (c)'s whole not-carried-forward set (the same historySnapshotBody step (c) stores through), wider than the auth-only wording of the ruling. Keep it?",
"options": [
"A: keep the shared predicate, so count, write-time withholding and the future rewrite cannot disagree",
"B: an auth-only predicate for the scrub, which needs a second list beside the redactor's"
],
"recommendation": "A, because the delta asks for the same predicate imported, not copied, and step (c) already withholds the whole set"
}
],
"security_findings": "none",
"out_of_scope_findings": []
}
Release: session_01ADHkmPF1skfu2kx89BEXZi · step (b) first half landed (PR #31655 merged 6c6cfcc1, 2026-11-08T20:30Z); what remains needs the maintainer's act, so the card leaves the active queue: pm:dispatched → needs-user-decision
Ruling-ref: 6677709817
Governing text: 裁决 6677709817 原文:"(b) second: a one-time scrub … runs in two steps: a read-only count first, reported to the maintainer through the advisory channel (#29111), and the rewrite only after his confirmation of that count … the scrub rehearsed on a copy before it runs anywhere"。#29111:安全发现不上公开卡。
Presented to the maintainer in the triage seat's session, in the standing decision-batch format (decision-analysis.md): batch #1 = #32481, #29484, #31874, cloud#4315 and #32163. ⛔ Not a claim on the work.
Ruling: batch #1 item 3 · letter C (as ruled: stop at (c)) · maintainer 「第1第2项同意,第 3 项 · #29484(安全):旧的历史快照里还留着明文密钥,扫除之前要先在真实数据上计数, 云中都是测试数据,不考虑历史数据。第4 第5项用 fable 重新评估和我讨论。」 2026-11-12T02:07Z
Ruled in the triage seat's session on 2026-11-12, on decision batch #1 as presented (decision-analysis.md format; brief-claim on this card).
Chosen: do not scrub stored history. The maintainer's reason, verbatim: 「云中都是测试数据,不考虑历史数据」: hosted data is test data, so pre-drain history snapshots are not a concern.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:server·needs-user-decision· 2026-11-02T17:03Z⛔ Not a claim.
domain:server, the history writer, with thedomain:specredaction owner. The maintainer has been told directly. ⛔ Seats add no further detail about stored secrets to this card. Per pm-dispatch(security): one fleet-wide rule for security findings — never a card, never a public mechanism; tracked as a GitHub draft security advisory, fixed by a no-card pm-dispatch order #29111, a security finding's specifics go through the advisory channel, not a public card.Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3
Brief-claimed-by: session_01L9qibxdiybU8ehPKBroYE3 · batch #437 · 2026-11-06T04:58Z
Director seat (the maintainer's decisions thread). Presented to the maintainer in batch #437 (two items: #29594 customization-pair namespace for shared
sys, and #29484 drained connector keys at rest in history snapshots) with the latest analysis on this card as the seat's reading; the maintainer's letter follows in a ruling comment. Rule level only on the security item.objectstack-fleet commentedon Nov 6, 2026
director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3
Ruling: batch #437 item 2 · letter d (c, then b) · maintainer 「29484 同意」 2026-11-06T05:26Z
Director seat (the maintainer's decisions thread). Presented in batch #437 (brief-claimed here at 6677323648; detailed letter in the project's decision doc, batch 6, rule level). The maintainer answered at 2026-11-06T05:26Z, verbatim: 「29484 同意」 = the seat's recommendation, d = (c) first, then (b).
What the ruling binds (rule level):
sys_metadata_historysnapshot written from now on withholds the same undeclaredauthkeys the [Decision] connector credential follow-ups after #28596: header-value advisory, legacy auth keys at rest, whole-map withholding #28640 ②B save drains from the live row, so no new history row carries such a value.Clause-②: no (narrowing)for what history stores;Metadata-protocol: none; the history read exit (redactStoredMetadataRow(s)) is unchanged.Workload:
domain:server(history writer) with thedomain:specredaction owner · 1–2 PRs · S–M · contract review on (c) · the scrub rehearsed on a copy before it runs anywhere.Execution: the server lane dispatches (c) with
Ruling-ref:naming this comment, the count + scrub script following; ⛔ no stored-secret specifics on this card. Label strokes are the dispatching seat's.objectstack-fleet commentedon Nov 7, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
State: ruled, not dispatched →
pm:queue· 2026-11-07T14:52Z⛔ Not a claim.
needs-user-decisioncomes off, on the maintainer's 「可以」 (2026-11-07). The (b) scrub keeps its own maintainer step, as the ruling says: a read-only count is confirmed before any rewrite.pm:*state and no seat could see it. It is nowpm:queuefordomain:server, starting with step (c) underRuling-ref: 6677709817.Generated by Claude Code
objectstack-fleet commentedon Nov 7, 2026
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ
Claim: PM loop round 1 · 2026-11-07T17:16Z
Session:
session_01FEkQ5nFAEGcShB48YZExSP(card thread, dev) · seatsession_01FEkQ5o8PE7ZtVjcor51wBZAccount:
os-project-managerBranch:
claude/project-thread-aru8xaWorktree:
objectstack-issue-29484Domain:
domain:serverSeat:
domain:server#5File surface:
packages/metadata/**(thesys_metadata_historywriter) + tests, root.changeset/; the shared drain predicate's current home only if it must move; ⛔packages/spec/**Container & model: M,
mode:thread,model: default tier(dispatch-gates --tier: no path-derived mandate),effort: thread default(order asked xhigh; the coordinator sets effort only on the maintainer's word)Clause-②: no (narrowing) — Ruling-ref 6677709817; CONTRACT_REVIEW_TIER review owed before the queue
Metadata-protocol: none
Responsibility:
packages/metadatahistory writer (objectstack) | the read exitredactStoredMetadataRow(s)already withholds it, at rest only | reached by every save of a connector carrying an undeclaredauthkeyThread-read: 6708495892
Premise: holds: ruled d = (c) then (b) (6677709817, re-read by triage 6708495892); this claim covers (c) only
Serial constraints cleared: none — no
pm:dispatchedcard claims the history writerScope: Step (c) only, PR says
Refs #29484; (b) count + scrub follows under its own maintainer step. Ruling-ref: 6677709817 Order:/mnt/project-files/pm-orders/server/order-29484.md. Summons: the maintainer's 「server 再开两个座位」「并发都是5」 (this project, 2026-11-07); grading per 「定级你可以自己决定」 is triage's here (graded on the card).6 remaining items
objectstack-fleet commentedon Nov 8, 2026
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi
Claim: PM loop round 1 · #29484 · 2026-11-08T18:54Z · step (b) first half only (p2; re-queued by triage 6725104872)
Session:
session_01ADHkmPF1skfu2kx89BEXZiAccount:
os-steveBranch:
claude/project-thread-l65x8h(the card thread's harness-pinned branch; platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-29484Domain:
domain:serverSeat:
domain:server#1File surface:
packages/metadata/src/andpackages/metadata-protocol/src/(a read-only count over history snapshots, reusing the step (c) predicate), the nearest existing one-time data command if one fits, their tests, a changeset (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default tier,effort: highClause-②: no
Metadata-protocol: none
Ruling-ref: 6677709817
Responsibility: n/a — step (b) of a ruled card; step (c) landed as PR #31242
Thread-read: 6725104872
Premise: holds (read at
99364ea7: step (c)'s write-time withholding is onmain(PR #31242); no count or scrub over existing history exists)Serial constraints cleared: no open PR touches
history-snapshot-body.ts. ⛔ Read-only: no rewrite, no run against any live or shared database. Per #29111, no specifics of any count go on this card or the PR; the second half (the rewrite) waits on the maintainer's confirmation and is not this claim. The PR saysRefs #29484.Order: standard order +
server-seat1/29484-delta.md(project files).objectstack-fleet commentedon Nov 8, 2026
domain:server#1 · os-steve · session_01ADHkmS3xwR94UULpFfP1br
os-dev-report
{ "issue": 29484, "status": "done", "branch": "claude/project-thread-l65x8h", "pr": "https://github.com/objectstack-ai/objectstack/pull/31655", "session": "session_01ADHkmS3xwR94UULpFfP1br", "premise_still_valid": true, "summary": "Step (b) first half of ruling 6677709817 only (PR says Refs #29484): scrubHistoryDrainedKeys(driver, { mode: 'count' }) in packages/metadata/src/migrations/history-drained-keys.ts, exported from @objectstack/metadata/migrations. It pages sys_metadata_history by primary key through a driver's find only and counts a row exactly when historySnapshotBody (PR #31242's helper, imported) would store a different body today; it reports affected rows, distinct (type, name) items, opaque row handles, and lists unreadable / unscrubbable rows beside the count. Only the count mode is wired; any other mode throws before a read. Step 0: the nearest one-time data tool convention for history is @objectstack/metadata/migrations (driver-level one-off migrations, same package as the predicate) beside metadata-protocol's recorded-by plan and its os migrate recorded-by door; no os migrate subcommand is added here (open question 1). The copy: a seeded driver-sqlite-wasm FILE, copied, with only the copy opened for the count.", "tests": "Head 7b4b2203. New packages/metadata/src/migrations/history-drained-keys.test.ts: 9 passed (seeded rows (i) counted, (ii) declared-only connector, (iii) non-connector, (iv) row written through the current DatabaseLoader writer not counted; delete row scanned not counted; non-JSON body listed; rows byte-identical after the run; result carries no fixture value or key name; non-count mode refused with the reader never called). pnpm --filter @objectstack/metadata test: Test Files 71 passed (71), Tests 981 passed (981); typecheck exit 0, the new files in its program (tsc --listFiles). Ablation A via scripts/ablation-replace.mjs on the predicate (... !== body -> ... !== body || true): anchor 1 -> 0, blob 058420598100 -> 8b8768ee5c6a, 4 failed | 5 passed (9), first red: expected [ …(5) ] to deeply equal [ 'h_seed_01', 'h_seed_02' ]; (ii)/(iii)/(iv) red as not-include; restored blob == HEAD, git diff HEAD empty. Ablation B on the mode guard: blob 058420598100 -> 8b4043f100a5, 1 failed | 8 passed (9): promise resolved instead of rejecting; restored blob == HEAD, git diff HEAD empty. The suite imports src, so no rebuild leg.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on 7b4b2203: 60 families, all exit 0; --ran: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN. First pass had three exit 3 on prerequisites, re-run green: check-plugin-teardown-shape --self-test (fixture commit 621a4876 fetched), check:dual-build-cjs-loads and check:lean-entry-closure (after turbo run build --filter='./packages/**', 81/81). eslint --no-inline-config packages/metadata: exit 0. CI check-runs: in_progress at report time, not waited on.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "4 — mcp__github__issue_read x3 (card get, get_comments x2), mcp__github__pull_request_read x1 (PR 31655 get, draft + body read-back); all reads, no write tool.", "api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 42515989605, PR #31655, 6814 bytes sent == stored, no labels: the diff publishes, so a patch changeset and no skip-changeset; no assignees); post-stamped.mjs comment on #29484 (this report). Plus git push x2 (not REST).", "files_changed": [ ".changeset/29484-history-drained-keys-count.md", "packages/metadata/src/migrations/history-drained-keys.test.ts", "packages/metadata/src/migrations/history-drained-keys.ts", "packages/metadata/src/migrations/index.ts" ], "line_budget": "+379 / -0 over 4 files (effort M); under the 3000-line human-merge threshold", "deviations": [ "Card delta named the count's subject as undeclared auth keys on connector snapshots; the imported predicate is step (c)'s whole not-carried-forward set, so the count covers every position step (c) withholds (open question 2).", "No os migrate subcommand: the tool ships as a library function only (open question 1)." ], "open_questions": [ { "question": "Wire an operator door for the count before the second half? Today the count runs only by calling the exported function with a driver opened on a restored copy.", "options": [ "A: a small follow-up PR adding a count-only os migrate subcommand (read-only boot like os migrate recorded-by), registered in the CLI's migrate pins (json-stdout purity, exit-code, confirmation-exit); the second half adds --apply through the ADR-0119 D2 journal", "B: no door until the second half; whoever holds a deployment's database runs the function by hand on a restored copy" ], "recommendation": "A, because the maintainer must confirm the count BEFORE the rewrite exists, so the count needs an operator door now, and the CLI pins are paid once for the command rather than twice" }, { "question": "The count's predicate is step (c)'s whole not-carried-forward set (the same historySnapshotBody step (c) stores through), wider than the auth-only wording of the ruling. Keep it?", "options": [ "A: keep the shared predicate, so count, write-time withholding and the future rewrite cannot disagree", "B: an auth-only predicate for the scrub, which needs a second list beside the redactor's" ], "recommendation": "A, because the delta asks for the same predicate imported, not copied, and step (c) already withholds the whole set" } ], "security_findings": "none", "out_of_scope_findings": [] }Stamped 2026-11-08T19:39Z.
objectstack-fleet commentedon Nov 8, 2026
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi
Release:
session_01ADHkmPF1skfu2kx89BEXZi· step (b) first half landed (PR #31655 merged6c6cfcc1, 2026-11-08T20:30Z); what remains needs the maintainer's act, so the card leaves the active queue:pm:dispatched→needs-user-decisionRuling-ref: 6677709817
待裁:已存旧历史的扫除,由谁在真实数据上跑计数
一句话问题: 连接器在保存时被清掉的未声明
auth键,仍以明文留在过去的历史快照里。裁决 6677709817(字母 d)要求先报一个只读计数给维护者确认,再改写。计数工具已经上线,但任何席位都碰不到真实部署的数据库,所以计数还没人跑过。Governing text: 裁决 6677709817 原文:"(b) second: a one-time scrub … runs in two steps: a read-only count first, reported to the maintainer through the advisory channel (#29111), and the rewrite only after his confirmation of that count … the scrub rehearsed on a copy before it runs anywhere"。#29111:安全发现不上公开卡。
协议声明: 不改协议。
Clause-②: no,Metadata-protocol: none。前提(附 re-check):
git log origin/main --oneline --grep "#31242"应有 1 条。scrubHistoryDrainedKeys(driver, { mode: 'count' }),导出于@objectstack/metadata/migrations,只读,不是 count 的模式在读之前就拒绝。re-check:git grep -n "export async function scrubHistoryDrainedKeys" origin/main -- packages/metadata/src/migrations/应有 1 处。选项:
affectedRows/distinctItems;确认后,席位派后半段(改写模式 + CLI 入口,在副本上演练后再跑)业务含义: A 是"先盘点仓库,再按清单销毁";B 是"没盘点就销毁";C 是"只保证以后不进货"。
四轴(业务立场): ① 长远看,"静止数据也在安全边界内"是平台该有的样子(主流 SaaS 的密钥轮换都包含历史与备份),A、B 都走向它,C 不走。② 拉动:今天没有客户撞上(读出口已脱敏),风险在数据库被直接读取的场景。③ 防 AI 犯错:A 让人在改写之前看到规模,出错时是响亮的数字不符;B 出错时是静默改错历史。④ 不扩散:A 不新增任何键,后半段只加一个一次性工具。
推荐 A。 只看①选 A;②③④ 是否翻转:否。置信缺口:托管环境的副本由谁出(维护者还是 cloud 运维),席位没测,由维护者指定。
裁后执行: 选 A 后,维护者(或其指定的运维)在副本上跑:
数字只走 advisory 通道(#29111),⛔ 不上本卡。维护者在本卡写
Ruled:确认"已看过计数"之后,server 车道派后半段:改写模式(复用同一个谓词)、os migrate入口、副本演练、上线运行。os-decision-facets
① 项目长远合理性:A 收窄特例。静止的历史与在线行同守一条规则,不另立契约。
② 实际业务拉动:今日无客户报障。风险在数据库被直接读取的场景,例如备份外泄。
③ 防 AI 犯错:A 在改写前给出可核对的数字,出错是响亮的不符;B 出错是静默改坏历史。
④ 创业阶段不扩散:A 不加任何声明键,后半段只是一个一次性工具。
Prior rulings read: 29484, 28640 ②B, history scrub, 29111 → 3 hits (6677709817, 6566912252, #29111); ADR none; thread: none
相关:#28640 / PR #29456(②B 保存时清键)· PR #31242((c) 写入时扣留)· PR #31655((b) 前半,计数工具)· #29111。
objectstack-fleet commentedon Nov 12, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Brief-claimed-by: session_01AavokzJ5DndAwitDXvKy4U · batch #1 · 2026-11-12T01:58Z
Presented to the maintainer in the triage seat's session, in the standing decision-batch format (
decision-analysis.md): batch #1 = #32481, #29484, #31874, cloud#4315 and #32163. ⛔ Not a claim on the work.Generated by Claude Code
objectstack-fleet commentedon Nov 12, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Ruling: batch #1 item 3 · letter C (as ruled: stop at (c)) · maintainer 「第1第2项同意,第 3 项 · #29484(安全):旧的历史快照里还留着明文密钥,扫除之前要先在真实数据上计数, 云中都是测试数据,不考虑历史数据。第4 第5项用 fable 重新评估和我讨论。」 2026-11-12T02:07Z
Ruled in the triage seat's session on 2026-11-12, on decision batch #1 as presented (
decision-analysis.mdformat; brief-claim on this card).Generated by Claude Code