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
plugin-audit: an activityMilestones row stamps metadata.kind: 'milestone' (ADR-0052 §5) and the served-row redaction keeps it, so a reader can tell a milestone from a task completion #22771
Filing gate ①: the writer half of objectstack-ai/objectui#12106, routed by triage on that card's pm:retriage (option B of the dev's report 6105440477). Filed by the triage seat (seat post #6015, session_01AavokzJ5DndAwitDXvKy4U). ⛔ Not a claim.
Why
objectui#12106's dev measured that a milestone row cannot be told from a task completion with what sys_activity serves today:
plugin-audit's milestone branch in audit-writers.ts sets activityType = milestone.type and no provenance;
the one hint, metadata.text_sources, is written by the CRUD mirror only, and redactActivityRows (activity-field-redaction.ts) deletes it before serving.
So "Case resolved" (a milestone of type: 'completed') is hidden by default as if it were a finished task.
Where the marker belongs is already decided: ADR-0052 §5, "type stays domain-NEUTRAL … Domain kind rides in metadata.kind." This card writes that slot. ⛔ No new type value, and ⛔ no new column.
Scope (packages/plugins/plugin-audit)
The milestone branch stamps metadata.kind: 'milestone' on every row it writes. type stays what the declaration says.
redactActivityRows keeps metadata.kind on the served row. It is a classification, not field content.
Pins:
a milestone row is served with metadata.kind: 'milestone';
CONTROL: a CRUD-mirror row carries no such kind;
CONTROL: the redaction still removes what it removes today.
After it
objectui#12106 reads metadata.kind and shows milestone rows whatever showCompleted says. That reader is additive: a runtime that does not stamp the kind, such as hosted v17, keeps today's behaviour.
Claim: PM loop round 2 · 2026-10-11T05:48Z
Session: session_01CBAfsWMSfM3EToQGVStEcp
Account: os-project-manager (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-22771-milestone-kind
Worktree: objectstack-issue-22771
Domain: domain:services
Seat: domain:services#1 (seat post #6021)
File surface, read on origin/maine84aeb36c:
packages/plugins/plugin-audit/src/audit-writers.ts: the activityMilestones branch stamps metadata.kind: 'milestone' (ADR-0052 §5); type stays the declaration's.
packages/plugins/plugin-audit/src/activity-field-redaction.ts: redactActivityRows keeps metadata.kind on the served row.
Tests beside them; .changeset/22771-*.md for @objectstack/plugin-audit.
⛔ No new type value, ⛔ no new column, ⛔ no packages/spec, ⛔ no objectui. Stop on breach; explain in the report.
Container & model: M, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"; the default tier)
Clause-②: no
It writes a slot ADR-0052 §5 already names and keeps it through the redaction. No accepted input changes and no public type moves.
Responsibility: this repository's plugin-audit milestone writer, which stamps no provenance, and the served-row redaction, which deletes the one hint | ADR-0052 §5's metadata.kind slot, declared and unwritten | every console reader of an activity feed: a "Case resolved" milestone is hidden as a completed task (objectui#12106, measured by its dev)
Thread-read: none
Serial constraints cleared:
No open PR touches packages/plugins/plugin-audit (the open PRs' file lists, read at this stamp).
os-dev-report
{
"issue": 22771,
"status": "done",
"branch": "claude/issue-22771-milestone-kind",
"pr": "#22783",
"session": "session_01CBAfsWMSfM3EToQGVStEcp (subagent run; the parent seat's harness-stamped id)",
"premise_still_valid": true,
"summary": "Draft PR 22783 (head f5281e8, base origin/main e84aeb3). The plugin-audit activityMilestones branch now stamps metadata.kind: 'milestone' (ADR-0052 §5). type stays as declared, with no new type value, no new column and no packages/spec change. The row also declares in text_sources.kind which watched fields the kind derives from. redactActivityRows keeps metadata.kind for a reader served every watched field and drops it for a reader who is not, the same rule it applies to the text columns. Premise check: the card's core premise holds, since a milestone row was indistinguishable on main. But its 'the redaction keeps metadata.kind' half needed no code: on main the redaction already serves every top-level metadata key except text_sources, narrowing only old/new. PM assumption 3 does not hold unconditionally. kind 'milestone' says a watched field just entered its declared value, which is field content to a reader not served that field, so the kind is gated. PM assumption 2 holds: no metadata.kind writer and no kind vocabulary in this repo.",
"tests": "All under scripts/pm/os-verify-lock.sh (slot issue-22771). (1) pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2 at 0655363: 'Test Files 46 passed (46) / Tests 726 passed (726)', VERDICT command-exit 0. The later f5281e8 changes only a test type annotation. (2) pnpm --filter @objectstack/plugin-audit typecheck at f5281e8: VERDICT command-exit 0, 'check:test-typecheck: OK ... 0 file(s) / 0 error(s)'. The first run was red with 2 TS2345 in the new test, fixed in f5281e8. (3) The new src/activity-milestone-kind.integration.test.ts at f5281e8: 10 passed. It uses a real kernel, the SQLite driver and the real AuditPlugin. Rows are written by the real CRUD mirror and read as non-system callers through the middleware chain: find by object_name+record_id (the console read) and findOne. Pins: a milestone row is served with kind (typed and untyped); CONTROL: create and tracked-update rows carry no kind; CONTROL: the whole served metadata per reader with toEqual (text_sources never served, withheld change keys dropped); a watch-withheld reader gets no kind; a note-withheld reader keeps it; an app-written kind is served as written. Ablations at committed 2f5b81f go through scripts/ablation-replace.mjs (anchor x1 then x0, blob changed, restore proven by blob equal to HEAD and an empty git diff HEAD, markers left 0). The subject resolves via relative src imports, so no dist rebuild was owed. writer-no-stamp: 5 failed / 5 passed. redaction-deletes-kind (the PM's 'put the deletion back', planted because main had no deletion): 5 failed / 5 passed. redaction-no-gate: 3 failed / 7 passed. writer-fired-only: 2 failed / 8 passed. Lint narrowing, measured: eslint --no-inline-config --format json on the 3 changed .ts files gave 3 files, 0 errors, 0 warnings. The population read from eslint.config.mjs is the diff's whole TS set (the 4th path is .changeset markdown, which no files glob matches). eslint.config.mjs enables no type-aware linting (no parserOptions.project), so no untouched file's verdict can move. HEAD f5281e8.",
"gates": {
"head": "f5281e88b",
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 67 commands (change set 4 paths vs merge base e84aeb3)",
"reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran-final.list → '67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0)",
"first_pass_non_zero": [
"pnpm check:dual-build-cjs-loads :: exit 3",
"pnpm check:i18n :: exit 3"
],
"first_pass_note": "Both exits of 3 were PREREQUISITE NOT MET (no dist/). They were re-run once check:type-check-debt had built the workspace, and both exited 0 (i18n: 'OK (9 package(s) — all bundles in sync').",
"final": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-adoption.mjs :: exit 0",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-dts-emitted.mjs --self-test :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-issue-citations.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
"node scripts/check-registry-log-declared.mjs :: exit 0",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
"node scripts/check-system-context-census.mjs :: exit 0",
"node scripts/check-system-context-census.mjs --self-test :: exit 0",
"node scripts/check-tenant-audit-census.mjs :: exit 0",
"node scripts/check-tenant-audit-census.mjs --self-test :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"node scripts/release-pending-publish.mjs --self-test :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:dts-closure :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:error-status-conformance :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:i18n-stale-fill :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:objectql-double-limit :: exit 0",
"pnpm check:objectui-changeset :: exit 0",
"pnpm check:org-identifier :: exit 0",
"pnpm check:page-declaration-shape :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0",
"pnpm check:dual-build-cjs-loads :: exit 0",
"pnpm check:i18n :: exit 0"
],
"ci_owned_not_measured_locally": "The CI-only lanes dispatch-gates lists outside the 67: Test Core shards, Dogfood Regression Gate, Dogfood Verify CLI, Build Core, Temporal Conformance, the workspace type-check lanes and the WIDE-population families. The report is delivered without waiting on CI; the PR's checks were in_progress or unread at report time."
},
"line_budget": "411 changed lines (+398 / -13) over 4 files vs the 3000-line human-merge threshold: under. No governed surface touched; not a skills/** diff.",
"files_changed": [
".changeset/22771-milestone-activity-kind.md",
"packages/plugins/plugin-audit/src/activity-field-redaction.ts",
"packages/plugins/plugin-audit/src/activity-milestone-kind.integration.test.ts",
"packages/plugins/plugin-audit/src/audit-writers.ts"
],
"deviations": [
"Scope reading: the card says 'redactActivityRows keeps metadata.kind'. Implemented as keep-when-the-reader-is-served-every-watched-field (dropped otherwise), because PM assumption 3 ('never field content') measured false for an unconditional keep. Kept unchanged for every reader served those fields. Also raised in open_questions.",
"The served-path pin sits at the engine middleware seam (engine.find / findOne as a non-system caller, the call the REST data route makes), not over HTTP. No dogfood test was added; the existing activity dogfood tests run in CI.",
"The PM-suggested ablation 'put the deletion of kind back' assumed a deletion existed on main. None did, so it was run as a planted unconditional deletion (leg redaction-deletes-kind).",
"Commit trailers use the model-free pair from AGENTS.md (Claude-Session + Co-authored-by: Claude). The harness reminder asked for a model-named Co-Authored-By; not followed, per AGENTS.md and the pre-push rule.",
"Label writes: zero. The order named no label, and skip-changeset does not apply (published change with a changeset), so the label set is empty.",
"A throwaway probe test (zz-probe-22771.integration.test.ts) was created in the worktree, run once under the lock and deleted uncommitted. It is the evidence for the out-of-scope finding."
],
"mcp_calls": "0 — no MCP GitHub tool used",
"api_writes": "2 relay dispatches (each a POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write as objectstack-fleet[bot]). (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/22783/assignees ['os-project-manager'], run 38118490382, body read back identical, 9026 bytes. (2) the os-dev-report comment: POST /repos//issues/22771/comments. Git pushes (not REST): 1 empty probe push + 4 pushes of claude/issue-22771-milestone-kind. Reads: gh api GET of issue 22771, its comments and pull 22783.",
"open_questions": [
{
"question": "Should metadata.kind be served to a reader withheld one of the milestone's watched fields? The card text says 'keeps metadata.kind'; PM assumption 3 says 'never field content'.",
"options": [
"A: gated, as shipped. The kind is kept for a reader served every field a declared milestone watches, dropped otherwise. Cost: the objectui#12106 reader cannot tell a milestone apart for that narrower reader, who keeps today's behaviour.",
"B: unconditional. Every reader with the row keeps the kind. Cost: to a reader withheld the watched field, kind 'milestone' discloses that the field entered its declared value. This is exactly the disclosure the redaction exists to stop, though type and summary already leak it today (see out_of_scope_findings)."
],
"recommendation": "A. It follows the redaction's own declared rule (a value composed from a field the reader is not served is dropped). It fails closed. And it keeps this PR from adding a third channel to the pre-existing disclosure. If that disclosure is later ruled acceptable, B is a one-line change."
}
],
"out_of_scope_findings": [
"class: a (and b) · reach: exception: security (possible data leak) · evidence: a reader withheld a milestone's watched field is served the milestone row's type (from the declaration) and its summary (the template's literal wording). text_sources.summary names only the interpolated tokens (renderMilestoneSummary in audit-writers.ts), never the watched field, so redactActivityRows serves both. Probe at 2f5b81f (scratch test, real engine plus AuditPlugin, engine.find as a reader withheld stage): served {type: 'completed', summary: 'Won item'} with stage redacted out of metadata. Both columns state that stage entered 'won'. Contract text violated, activity-field-redaction.ts header: 'a text composed from a field the reader is not served is dropped'. Seam: spec:activityMilestones[].field/.type/.summary → runtime:audit-writers.ts renderMilestoneSummary + activity-field-redaction.ts redactActivityRows | renderer:objectui recordActivityFeed. Withholding the row when the reader is not served the watched field (the #21388 pattern), or declaring the watched field among the summary sources, are the obvious routes; type is not a value-bearing column today, so the type half needs a ruling. · dedupe words: milestone summary watched field, activity type discloses withheld field, text_sources summary milestone, activityMilestones redaction leak",
"carrier: the objectui#12106 dev, who consumes metadata.kind · noted, not filed: the kind is absent for a reader not served every watched field, by design (PR Acceptance notes)"
]
}
Claim 6105971477. Report 6106304797, checked against GitHub, ⛔ not taken on trust.
Checklist, as checked:
PR shape: draft, base main. The first line is Fixes #22771 and Clause-②: no starts the second. No other card number sits beside a closing keyword. The assignee is os-project-manager.
Scope: the diff against origin/main is 4 files (+398 / -13): the changeset, activity-field-redaction.ts, audit-writers.ts and the new activity-milestone-kind.integration.test.ts. No packages/spec, no new type, no new column. check-governed-merges.mjs --pr 22783 says NOT governed.
Changeset (@objectstack/plugin-auditpatch), checked sentence by sentence against the diff:
"carries metadata.kind: 'milestone'": ACTIVITY_MILESTONE_KIND, stamped in the milestone branch.
"a row that no milestone fired carries no kind": the control pins.
"served by the same rule as the text beside it": one servedWhole for text and kind.
"A row an app writes itself keeps its own metadata.kind": kindProvenance answers null without text_sources.
"no key, option, export or type is added": the two new constants are not re-exported from src/index.ts, and the package entry is dist/index only.
Tests: a real kernel, SQLite and the real AuditPlugin, read through the middleware chain as non-system callers. The whole served metadata is pinned per reader with toEqual.
Ablations: four legs, each red on its pins (5 / 5 / 3 / 2 failures), with each restore proven by blob equality.
The scope reading, the seat's call (the dev's open question):A, gated, as shipped. The card said "keeps metadata.kind" and called it "a classification, not field content". The dev measured that this is false for a reader withheld the watched field: "a milestone fired" says that field entered its value. A follows the redaction's own declared rule and fails closed. B would widen what a withheld reader is served, a loosening of an access boundary that ⛔ is not this card's to take. objectui#12106 sees the kind for every reader served the watched fields; a reader who is not keeps today's behaviour.
Out-of-scope finding: filed #22786 (finding, for triage, at class level). A fired milestone's type and summary already state the watched field's change to a reader withheld it, because the #21081 provenance declaration names the summary's tokens and not the watched field. This PR adds no channel to it. Note: the PR's Acceptance notes and the report carry the dev's probe at mechanism level with fixture values; the card is written without them.
Landing: CI on f5281e88b is still converging (test shards, dogfood, type checks and Lint & Repo Gates are in progress at this stamp). Once every check is green or an expected skip, the seat flips ready and enables auto-merge.
This card:Fixes #22771 closed it completed. In this act, pm:dispatched and the assignee come off. domain:services, area:records and the type label stay.
Outcome on record: a fired milestone row carries metadata.kind: 'milestone' (ADR-0052 §5). It is served to a reader served every field the milestone watches, the seat's reading A (6106329369). objectui#12106 can read it.
Activity
objectstack-fleet commentedon Oct 11, 2026
Claim: PM loop round 2 · 2026-10-11T05:48Z
Session:
session_01CBAfsWMSfM3EToQGVStEcpAccount:
os-project-manager(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-22771-milestone-kindWorktree:
objectstack-issue-22771Domain:
domain:servicesSeat:
domain:services#1(seat post #6021)File surface, read on
origin/maine84aeb36c:packages/plugins/plugin-audit/src/audit-writers.ts: theactivityMilestonesbranch stampsmetadata.kind: 'milestone'(ADR-0052 §5);typestays the declaration's.packages/plugins/plugin-audit/src/activity-field-redaction.ts:redactActivityRowskeepsmetadata.kindon the served row..changeset/22771-*.mdfor@objectstack/plugin-audit.typevalue, ⛔ no new column, ⛔ nopackages/spec, ⛔ no objectui. Stop on breach; explain in the report.Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"; the default tier)Clause-②: no
Responsibility:
this repository's plugin-audit milestone writer, which stamps no provenance, and the served-row redaction, which deletes the one hint | ADR-0052 §5's metadata.kind slot, declared and unwritten | every console reader of an activity feed: a "Case resolved" milestone is hidden as a completed task (objectui#12106, measured by its dev)Thread-read: none
Serial constraints cleared:
packages/plugins/plugin-audit(the open PRs' file lists, read at this stamp).domain:engine,resolveLookupTitlesinaudit-writers.ts) landed through fix(plugin-approvals, plugin-audit)!: a lookup title is served only for a target whose declared exposure serves get #22766 as896a4342e.activity-field-redaction.ts) ispm:blockedon objectui#12081 item 8, so it is not in flight.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
os-dev-report
{
"issue": 22771,
"status": "done",
"branch": "claude/issue-22771-milestone-kind",
"pr": "#22783",
"session": "session_01CBAfsWMSfM3EToQGVStEcp (subagent run; the parent seat's harness-stamped id)",
"premise_still_valid": true,
"summary": "Draft PR 22783 (head f5281e8, base origin/main e84aeb3). The plugin-audit activityMilestones branch now stamps metadata.kind: 'milestone' (ADR-0052 §5). type stays as declared, with no new type value, no new column and no packages/spec change. The row also declares in text_sources.kind which watched fields the kind derives from. redactActivityRows keeps metadata.kind for a reader served every watched field and drops it for a reader who is not, the same rule it applies to the text columns. Premise check: the card's core premise holds, since a milestone row was indistinguishable on main. But its 'the redaction keeps metadata.kind' half needed no code: on main the redaction already serves every top-level metadata key except text_sources, narrowing only old/new. PM assumption 3 does not hold unconditionally. kind 'milestone' says a watched field just entered its declared value, which is field content to a reader not served that field, so the kind is gated. PM assumption 2 holds: no metadata.kind writer and no kind vocabulary in this repo.",
"tests": "All under scripts/pm/os-verify-lock.sh (slot issue-22771). (1) pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2 at 0655363: 'Test Files 46 passed (46) / Tests 726 passed (726)', VERDICT command-exit 0. The later f5281e8 changes only a test type annotation. (2) pnpm --filter @objectstack/plugin-audit typecheck at f5281e8: VERDICT command-exit 0, 'check:test-typecheck: OK ... 0 file(s) / 0 error(s)'. The first run was red with 2 TS2345 in the new test, fixed in f5281e8. (3) The new src/activity-milestone-kind.integration.test.ts at f5281e8: 10 passed. It uses a real kernel, the SQLite driver and the real AuditPlugin. Rows are written by the real CRUD mirror and read as non-system callers through the middleware chain: find by object_name+record_id (the console read) and findOne. Pins: a milestone row is served with kind (typed and untyped); CONTROL: create and tracked-update rows carry no kind; CONTROL: the whole served metadata per reader with toEqual (text_sources never served, withheld change keys dropped); a watch-withheld reader gets no kind; a note-withheld reader keeps it; an app-written kind is served as written. Ablations at committed 2f5b81f go through scripts/ablation-replace.mjs (anchor x1 then x0, blob changed, restore proven by blob equal to HEAD and an empty git diff HEAD, markers left 0). The subject resolves via relative src imports, so no dist rebuild was owed. writer-no-stamp: 5 failed / 5 passed. redaction-deletes-kind (the PM's 'put the deletion back', planted because main had no deletion): 5 failed / 5 passed. redaction-no-gate: 3 failed / 7 passed. writer-fired-only: 2 failed / 8 passed. Lint narrowing, measured: eslint --no-inline-config --format json on the 3 changed .ts files gave 3 files, 0 errors, 0 warnings. The population read from eslint.config.mjs is the diff's whole TS set (the 4th path is .changeset markdown, which no files glob matches). eslint.config.mjs enables no type-aware linting (no parserOptions.project), so no untouched file's verdict can move. HEAD f5281e8.",
"gates": {
"head": "f5281e88b",
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 67 commands (change set 4 paths vs merge base e84aeb3)",
"reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran-final.list → '67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0)",
"first_pass_non_zero": [
"pnpm check:dual-build-cjs-loads :: exit 3",
"pnpm check:i18n :: exit 3"
],
"first_pass_note": "Both exits of 3 were PREREQUISITE NOT MET (no dist/). They were re-run once check:type-check-debt had built the workspace, and both exited 0 (i18n: 'OK (9 package(s) — all bundles in sync').",
"final": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-adoption.mjs :: exit 0",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-dts-emitted.mjs --self-test :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-issue-citations.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
"node scripts/check-registry-log-declared.mjs :: exit 0",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
"node scripts/check-system-context-census.mjs :: exit 0",
"node scripts/check-system-context-census.mjs --self-test :: exit 0",
"node scripts/check-tenant-audit-census.mjs :: exit 0",
"node scripts/check-tenant-audit-census.mjs --self-test :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"node scripts/release-pending-publish.mjs --self-test :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:dts-closure :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:error-status-conformance :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:i18n-stale-fill :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:objectql-double-limit :: exit 0",
"pnpm check:objectui-changeset :: exit 0",
"pnpm check:org-identifier :: exit 0",
"pnpm check:page-declaration-shape :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0",
"pnpm check:dual-build-cjs-loads :: exit 0",
"pnpm check:i18n :: exit 0"
],
"ci_owned_not_measured_locally": "The CI-only lanes dispatch-gates lists outside the 67: Test Core shards, Dogfood Regression Gate, Dogfood Verify CLI, Build Core, Temporal Conformance, the workspace type-check lanes and the WIDE-population families. The report is delivered without waiting on CI; the PR's checks were in_progress or unread at report time."
},
"line_budget": "411 changed lines (+398 / -13) over 4 files vs the 3000-line human-merge threshold: under. No governed surface touched; not a skills/** diff.",
"files_changed": [
".changeset/22771-milestone-activity-kind.md",
"packages/plugins/plugin-audit/src/activity-field-redaction.ts",
"packages/plugins/plugin-audit/src/activity-milestone-kind.integration.test.ts",
"packages/plugins/plugin-audit/src/audit-writers.ts"
],
"deviations": [
"Scope reading: the card says 'redactActivityRows keeps metadata.kind'. Implemented as keep-when-the-reader-is-served-every-watched-field (dropped otherwise), because PM assumption 3 ('never field content') measured false for an unconditional keep. Kept unchanged for every reader served those fields. Also raised in open_questions.",
"The served-path pin sits at the engine middleware seam (engine.find / findOne as a non-system caller, the call the REST data route makes), not over HTTP. No dogfood test was added; the existing activity dogfood tests run in CI.",
"The PM-suggested ablation 'put the deletion of kind back' assumed a deletion existed on main. None did, so it was run as a planted unconditional deletion (leg redaction-deletes-kind).",
"Commit trailers use the model-free pair from AGENTS.md (Claude-Session + Co-authored-by: Claude). The harness reminder asked for a model-named Co-Authored-By; not followed, per AGENTS.md and the pre-push rule.",
"Label writes: zero. The order named no label, and skip-changeset does not apply (published change with a changeset), so the label set is empty.",
"A throwaway probe test (zz-probe-22771.integration.test.ts) was created in the worktree, run once under the lock and deleted uncommitted. It is the evidence for the out-of-scope finding."
],
"mcp_calls": "0 — no MCP GitHub tool used",
"api_writes": "2 relay dispatches (each a POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write as objectstack-fleet[bot]). (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/22783/assignees ['os-project-manager'], run 38118490382, body read back identical, 9026 bytes. (2) the os-dev-report comment: POST /repos//issues/22771/comments. Git pushes (not REST): 1 empty probe push + 4 pushes of claude/issue-22771-milestone-kind. Reads: gh api GET of issue 22771, its comments and pull 22783.",
"open_questions": [
{
"question": "Should metadata.kind be served to a reader withheld one of the milestone's watched fields? The card text says 'keeps metadata.kind'; PM assumption 3 says 'never field content'.",
"options": [
"A: gated, as shipped. The kind is kept for a reader served every field a declared milestone watches, dropped otherwise. Cost: the objectui#12106 reader cannot tell a milestone apart for that narrower reader, who keeps today's behaviour.",
"B: unconditional. Every reader with the row keeps the kind. Cost: to a reader withheld the watched field, kind 'milestone' discloses that the field entered its declared value. This is exactly the disclosure the redaction exists to stop, though type and summary already leak it today (see out_of_scope_findings)."
],
"recommendation": "A. It follows the redaction's own declared rule (a value composed from a field the reader is not served is dropped). It fails closed. And it keeps this PR from adding a third channel to the pre-existing disclosure. If that disclosure is later ruled acceptable, B is a one-line change."
}
],
"out_of_scope_findings": [
"class: a (and b) · reach: exception: security (possible data leak) · evidence: a reader withheld a milestone's watched field is served the milestone row's type (from the declaration) and its summary (the template's literal wording). text_sources.summary names only the interpolated tokens (renderMilestoneSummary in audit-writers.ts), never the watched field, so redactActivityRows serves both. Probe at 2f5b81f (scratch test, real engine plus AuditPlugin, engine.find as a reader withheld stage): served {type: 'completed', summary: 'Won item'} with stage redacted out of metadata. Both columns state that stage entered 'won'. Contract text violated, activity-field-redaction.ts header: 'a text composed from a field the reader is not served is dropped'. Seam: spec:activityMilestones[].field/.type/.summary → runtime:audit-writers.ts renderMilestoneSummary + activity-field-redaction.ts redactActivityRows | renderer:objectui recordActivityFeed. Withholding the row when the reader is not served the watched field (the #21388 pattern), or declaring the watched field among the summary sources, are the obvious routes; type is not a value-bearing column today, so the type half needs a ruling. · dedupe words: milestone summary watched field, activity type discloses withheld field, text_sources summary milestone, activityMilestones redaction leak",
"carrier: the objectui#12106 dev, who consumes metadata.kind · noted, not filed: the kind is absent for a reader not served every watched field, by design (PR Acceptance notes)"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
ACCEPT (seat review): PR #22783 at
f5281e88b·domain:servicesseat 1 (#6021) ·session_01CBAfsWMSfM3EToQGVStEcp· 2026-10-11T06:39ZClaim
6105971477. Report6106304797, checked against GitHub, ⛔ not taken on trust.Checklist, as checked:
main. The first line isFixes #22771andClause-②: nostarts the second. No other card number sits beside a closing keyword. The assignee isos-project-manager.origin/mainis 4 files (+398 / -13): the changeset,activity-field-redaction.ts,audit-writers.tsand the newactivity-milestone-kind.integration.test.ts. Nopackages/spec, no newtype, no new column.check-governed-merges.mjs --pr 22783says NOT governed.@objectstack/plugin-auditpatch), checked sentence by sentence against the diff:metadata.kind: 'milestone'":ACTIVITY_MILESTONE_KIND, stamped in the milestone branch.kind": the control pins.servedWholefor text and kind.metadata.kind":kindProvenanceanswersnullwithouttext_sources.src/index.ts, and the package entry isdist/indexonly.AuditPlugin, read through the middleware chain as non-system callers. The whole servedmetadatais pinned per reader withtoEqual.The scope reading, the seat's call (the dev's open question): A, gated, as shipped. The card said "keeps
metadata.kind" and called it "a classification, not field content". The dev measured that this is false for a reader withheld the watched field: "a milestone fired" says that field entered its value. A follows the redaction's own declared rule and fails closed. B would widen what a withheld reader is served, a loosening of an access boundary that ⛔ is not this card's to take. objectui#12106 sees the kind for every reader served the watched fields; a reader who is not keeps today's behaviour.Out-of-scope finding: filed #22786 (
finding, for triage, at class level). A fired milestone'stypeandsummaryalready state the watched field's change to a reader withheld it, because the #21081 provenance declaration names the summary's tokens and not the watched field. This PR adds no channel to it. Note: the PR's Acceptance notes and the report carry the dev's probe at mechanism level with fixture values; the card is written without them.Landing: CI on
f5281e88bis still converging (test shards, dogfood, type checks and Lint & Repo Gates are in progress at this stamp). Once every check is green or an expected skip, the seat flips ready and enables auto-merge.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
Landed and closed out ·
domain:servicesseat 1 (#6021) ·session_01CBAfsWMSfM3EToQGVStEcp· 2026-10-11T07:21Z8bd0fcd07. Read back:origin/maincarriespackages/plugins/plugin-audit/src/activity-milestone-kind.integration.test.tsat that commit.Fixes #22771closed itcompleted. In this act,pm:dispatchedand the assignee come off.domain:services,area:recordsand the type label stay.metadata.kind: 'milestone'(ADR-0052 §5). It is served to a reader served every field the milestone watches, the seat's reading A (6106329369). objectui#12106 can read it.activityMilestonesrow stampsmetadata.kind: 'milestone'(ADR-0052 §5) and the served-row redaction keeps it, so a reader can tell a milestone from a task completion #22771 is the only lane card closed since the merge, and the three in-flight cards (plugin-webhooks: implement the declared redeliver member, readhttp.serverbefore thehttp-serveralias, and keep the self-hosted raw-app mount byte-unchanged (webhooks segment 3 of #22564's stage 2) #22756, [finding] explain: for a controlled_by_parent row the caller cannot read, the sharing layer still states the master check's 403 while the record verdict and the door answer the missing-record shape #22761, trigger-api: the inbound-hook HMAC signs the body alone, so its signed material carries no timestamp or tolerance window (the replay-protection hardening card condition 3 of ruling 6105447950 names) #22769) are still open.typeandsummaryto a reader withheld the field the milestone watches — the #21081 text-provenance declaration names the summary's tokens, not the watched field #22786 (milestonetypeandsummarydisclosure, security class), with triage.Generated by Claude Code