Repository navigation
retire the saved-report stack — sys_saved_report / sys_report_schedule, /api/v1/reports, client.reports, IReportService, the reports capability and @objectstack/plugin-reports (zero consumers; not the report metadata kind) #20102
Copy link
Copy link
Closed
Labels
Description
Ruled: maintainer 「A. 退役」 and 「你直接派发处理这个退役任务。」 · chat with session session_013RWUA7bNq5bRhehLPqXwMg, 2026-09-25
Provenance
- Who: the maintainer.
- Where: the maintainer's chat with session
session_013RWUA7bNq5bRhehLPqXwMg, 2026-09-25. It came out of the duplicate-endpoint sweep that followed objectstack#19543 door ④. - Verbatim: the maintainer asked 「sys_saved_report 和 metadata 中的报表是同一个东西吗」. The session answered that they are different things, and offered A (retire the saved-report stack), B (keep it, renamed) and C (status quo). The maintainer answered 「A. 退役」, then 「你直接派发处理这个退役任务。」.
Two different things share the word "report"
report metadata kind (stays) |
saved-report stack (retires) | |
|---|---|---|
| What it is | An analytics report bound to a dataset (ADR-0021): rows, columns and values as dimensions and measures; tabular / summary / matrix / joined; chart; drilldown | A persisted raw ObjectQL query (object_name + query_json {filter, fields, orderBy, limit, groupBy}), plus a render format (csv / json / html_table) and an owner |
| Authored by | code (*.report.ts, e.g. examples/app-todo/src/reports/task.report.ts), Studio, runtime create |
only POST /api/v1/reports |
| Read by | objectui ReportView, ReportPreview, sidebar, command palette, app nav |
IReportService.run() (CSV / email digest) and sys_report_schedule |
| Consumers | many | none (below) |
task.report.ts records that ADR-0021 removed the report kind's "legacy inline query form", and moved ad-hoc object queries to a ListView lens. The saved-report stack is that same object + raw-query shape, alive in a parallel table under the same name.
Evidence of zero consumers (read on origin/main of all three repos, 2026-09-25)
- objectstack:
saveReport/scheduleReport/unscheduleReportare called only by the REST routes inpackages/rest/src/rest-server.ts. Nothing else inpackages/**(tests aside) callsIReportService. - objectui: no call to
/reports,client.reports.*,sys_saved_reportorsys_report_schedule(the only hit is a type-comment inpackages/types/src/spec-report.ts). - cloud: no dependency on
@objectstack/plugin-reports.packages/objectos-runtime/src/capability-coverage.test.tsrecords['reports', 'no @objectstack/plugin-reports dependency exists in this repo — nothing could mount it']. - No app declares
requires: ['reports']in objectstackexamples/**or cloudapps/**.
Retire (all of it, immediately — no deprecation window, per the stage posture)
packages/plugins/plugin-reports/**: the whole package (reports-plugin.ts,report-service.ts,index.ts), its workspace entry,.changeset/config.jsonmentions, andscripts/*baseline*.json/scripts/test-shard-timings.jsonrows.packages/platform-objects/src/audit/sys-saved-report.object.tsandsys-report-schedule.object.ts: their exports inaudit/index.ts, the generated translation bundles (regenerate with the repo's generator, ⛔ never by hand), and any object-list pins.packages/rest/src/rest-server.ts: the/api/v1/reportsfamily (list, get, save, delete, run, run ad-hoc, schedules). Also itspackages/rest/src/rest-route-ledger.tsrows.packages/client/src/index.ts: thereportsnamespace (reports.list/get/save/delete/run/listSchedules/schedule/unschedule, whatever exists).packages/spec/src/contracts/report-service.ts(IReportService,SavedReport,ReportSchedule,ReportQuery,SaveReportInput,ScheduleReportInput,ReportRunResult) and its export.packages/spec/src/kernel/platform-capabilities.ts: thereportscapability (enum entry and thereports: { package: '@objectstack/plugin-reports', … }row). A manifest that still declaresrequires: ['reports']must be refused loudly with a prescription (ADR-0087 migration entry), ⛔ not silently accepted.packages/cli/src/commands/serve.ts: the@objectstack/plugin-reportsmount entry and'reports'inNEEDS_JOB_OR_QUEUE. Alsopackages/cli/package.json,packages/core/src/fallbacks/index.ts(the comment namingsys_report_schedule) andpackages/lint/src/validate-sortable-fields.ts(the comment namingsys_saved_report), where they still reason about the retired objects.- Docs:
content/docs/api/client-sdk.mdx,content/docs/plugins/packages.mdx,content/docs/permissions/system-context.mdx,content/docs/protocol/objectql/query-syntax.mdx,content/docs/ui/apps.mdx, and wherever else the saved-report API or plugin is described. ⛔content/docs/releases/is never edited in a code PR. docs/qa/platform-checklist/areas/dashboards.json: items that exercise the saved-report API.
Out of scope
- ⛔ The
reportmetadata kind (ReportSchema,/meta/report,defineReport), datasets and the analytics service: unchanged. - ⛔ Scheduled delivery for
reportmetadata is not built here. If a real need appears, it is a new card on the metadata kind. - Cross-repo follow-up, filed by the PM, not the dev: cloud
packages/objectos-runtime/src/capability-coverage.test.tscarries areportsrow that must go once a@objectstack/specrelease without the capability is installed there. - Release-time act, not the dev's: deprecating the published
@objectstack/plugin-reportson npm is a maintainer release action (AGENTS.md is authoritative).
Acceptance
git grep -n "plugin-reports\|sys_saved_report\|sys_report_schedule\|IReportService\|/api/v1/reports"on the PR head has no live hits outside CHANGELOGs, migration entries and the retirement tests. Each remaining hit is listed in the PR body with its reason.- A manifest declaring
requires: ['reports']is refused at parse or validate with a message naming the retirement and the replacement (reportmetadata / ListView). - The
/api/v1/reportspaths answer the platform's standard 404 for an unmounted route. A pin asserts it. - Physical tables: the PR body states what happens to an existing deployment's
sys_saved_report/sys_report_scheduletables under the repo's convention for a retired system object, and cites that convention. Clause-②: yes: this narrows the public surface. The PR carries the ADR-0087 entries and a**BREAKING**changeset, per the retirement playbook (spec-property-retirementskill).
Reactions are currently unavailable
Activity
objectstack-fleet commentedon Sep 25, 2026
Claim: maintainer-direct dispatch (round 1) · 2026-09-25T04:39Z
Session:
session_013RWUA7bNq5bRhehLPqXwMgBranch:
claude/issue-20102-retire-saved-report-stackWorktree:
objectstack-issue-20102Domain:
domain:specSeat: domain:spec#1
Dispatch authority: maintainer-direct — 「你直接派发处理这个退役任务。」, 2026-09-25, this session's chat (seat 1 post #6017 reads vacant; no lane seat is displaced)
File surface:
packages/plugins/plugin-reports/**(removed),packages/platform-objects/src/audit/(the two report objects + index) and its generated translation bundles,packages/rest/src/rest-server.ts+packages/rest/src/rest-route-ledger.ts(the/api/v1/reportsfamily),packages/client/src/index.ts(thereportsnamespace),packages/spec/src/contracts/report-service.ts,packages/spec/src/kernel/platform-capabilities.ts,packages/spec/src/migrations/(ADR-0087 entries),packages/cli/src/commands/serve.ts+packages/cli/package.json, the comment sites inpackages/core/src/fallbacks/index.tsandpackages/lint/src/validate-sortable-fields.ts,content/docs/**(⛔ notcontent/docs/releases/),docs/qa/platform-checklist/areas/dashboards.json, workspace / changeset config and generated baselines,.changeset/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(dispatch-gates.mjs --tier2026-09-25 ona8bcce6: no path-derived mandate; default tier; clause-② suspect surfacepackages/spec/src/**⇒ contract-review-tier REVIEW before enqueue)Clause-②: yes
Thread-read: none
Serial constraints cleared:
none — the 9 open PRs at claim time touch none of plugin-reports, the two report objects, audit/index.ts, platform-capabilities.ts, contracts/report-service.ts, rest-server.ts, rest-route-ledger.ts, client/src/index.ts or cli serve.ts; same-day main churn on the surface: c23cfb3 (spec api-assembled split) and b76aad5 (client limit emitters)Generated by Claude Code
wip(spec,rest,client,cli)!: retire the saved-report stack (#20102)
objectstack-fleet commentedon Sep 25, 2026
os-dev-report
{ "issue": 20102, "status": "done", "branch": "claude/issue-20102-retire-saved-report-stack", "pr": "https://github.com/objectstack-ai/objectstack/pull/20125", "session": "session_013RWUA7bNq5bRhehLPqXwMg", "premise_still_valid": true, "summary": "Retired the saved-report stack whole on d624002, leaving the `report` metadata kind untouched. The package, both platform objects, the spec contract (8 types), the eight /api/v1/reports routes and their 9 ledger error codes, client.reports, and the `reports` token are gone. requires: ['reports'] is REFUSED by defineStack (STACK_CAPABILITY_UNKNOWN 422) with a retirement prescription. The prescription comes from the new RETIRED_PLATFORM_CAPABILITY_GUIDANCE, and serve warns with the same text. The ADR-0087 D3 entry is 18.saved-report-stack-retired, and a BREAKING changeset carries `registered`. The RestServer constructor keeps index 10 as a retired placeholder typed `undefined`: deleting it would silently re-bind every later positional argument at 276 call sites, and the placeholder turned 4 stale test callers into compile errors. Existing sys_saved_report / sys_report_schedule tables are left in place (repo convention: unmanaged-tables.ts header, deployment/cli.mdx, precedent 18.scim-provider-object-retired). The 2 checklist items are retired, not deleted. Evidence re-verified: zero runtime consumers. objectui origin/main 2a943bf and pin f8a9d0f have only a doc-local `interface ReportSchedule`. cloud 48d7066 has two test-only readers, which are follow-ups. The PR is about 11.5k changed lines, over 5000, so it needs a human merge. The PR assignee was not set: the relay pr_create op takes no assignees, and os-dev forbids writing assignee.", "tests": "At head ecb3bb5.\nBuild: turbo build cli/client/lint closure 56/56; connectors+plugins+apps+client-react 57/57; spec build (all lock VERDICT command-exit 0).\nTests:\n- spec full: 570 files, 16371 passed.\n- rest full: 194 files, 5 failed on the first run (execctx census counts, sibling-control status). After the fix, 5 files / 88 passed plus the pin 10 passed.\n- platform-objects: 55 files / 911 passed.\n- client: 50 / 635 passed.\n- cli --project unit: 224 / 3155 passed.\n- core operation-private-keys.pin: 5 passed.\n- metadata-protocol orderby-vocabulary: 11 passed.\nTypecheck exit 0: spec, platform-objects, rest, client, cli, core, metadata-protocol, objectql, lint, driver-memory. rest was first red at exactly the 4 test callers passing a provider into the retired slot (TS2345/TS2322 'not assignable to ... undefined'). The client typecheck is green, so the @ts-expect-error on client.reports is live.\nAblation, one-off: ablation-replace swapped the retirement branch in stack.zod.ts#validateKnownCapabilities for `if (false as boolean)`. Anchor 1->0, blob 3c09282f->8bb2143f. reports-capability-retirement.test.ts went red, 2 failed / 5 passed. Restored: blob == HEAD, git diff HEAD empty.\nGates: dispatch-gates --commands derived 164, --ran 164/164 run. 161 exit 0, among them check:generated, adr-0087-registration --base d624002, platform-checklist, system-context-census, tenant-audit-census, i18n, i18n-coverage, route-envelope, skill-examples, dual-build-cjs-loads, nul-bytes and engine-double-contract. query-options-erasure raced my parallel skill-examples run on its scratch dir; alone it exits 0.\nNOT MEASURED: check-engine-split-ratio and check-plugin-teardown-shape --self-test (shallow clone refusal); check:pm-dispatch-gates (timed out at 480s/580s under box contention, no failed assertion); CLI integration tier (declared to CI; no spawn entry touched); repo-wide pnpm lint (CI-owned).", "mcp_calls": "0", "api_writes": "2 — via the fleet-write relay (objectstack-fleet[bot]): pr_create POST /repos/objectstack-ai/objectstack/pulls (draft, PR 20125); comment POST /repos/objectstack-ai/objectstack/issues/20102/comments (this report). Label write: zero (dispatch named no labels, and skip-changeset does not apply because the PR carries a changeset). Plus git push on the branch (not REST).", "open_questions": [ { "question": "docs/NORTH-STAR.md:62 (Tier H) still reads 「报表自己建、自己存、只归自己,还能按时自己发出去」 and cites dashboards.saved-report-ownership and dashboards.report-schedule-dispatch-delivery. This PR retires both items, and nothing now delivers a report on a schedule. Who rewords the row?", "options": [ "A: the maintainer edits the row in a separate Tier H PR, dropping the two ids and the scheduled-send leg", "B: keep the row as a North Star goal and file a card for scheduled delivery on `report` metadata" ], "recommendation": "A. The card rules scheduled delivery out of scope and 'no new capability without pull', so the row should stop citing retired items. B is only right if the maintainer names a real need (the card says that would be a new card)." } ], "out_of_scope_findings": [ "follow-up (card-named, PM): cloud packages/objectos-runtime/src/capability-coverage.test.ts:45 `reports` row — goes red once a spec release without the token is installed", "follow-up (PM, same trigger, NOT on the card): cloud packages/objectos-runtime/src/capability-loader.test.ts:475-497 drives requires: ['reports'] and asserts pkg '@objectstack/plugin-reports' from the spec provider map (line 492) · dedupe words: capability-loader reports unmapped plugin-reports", "follow-up (maintainer release act): npm deprecate @objectstack/plugin-reports", "carrier: PM seat (Tier S) · .claude/skills/pm-dispatch/SKILL.md:164, references/lanes/services.md:8, .claude/workflows/docs-accuracy-audit.js:220 still list plugin-reports in lane rosters · noted, not filed", "carrier: maintainer (Tier H) · docs/NORTH-STAR.md:62 cites the two retired checklist items · see open_questions", "observation, unclassified, not verified: objectui content/docs/core/report-schema.mdx (ReportComponentSchema) advertises `schedule` + email distribution for a report component; no scheduler exists on either side now. Whether the renderer drops the key was not measured, so this is not a class (c) claim · 承接者:无 · noted only" ] }Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
os-dev-report
{ "issue": 20102, "status": "done", "branch": "claude/issue-20102-retire-saved-report-stack", "pr": "https://github.com/objectstack-ai/objectstack/pull/20125", "session": "session_013RWUA7bNq5bRhehLPqXwMg", "premise_still_valid": true, "summary": "CI round 1 fixes are pushed as aab0e0f, on top of the merge commit 527030b. That merge of origin/main 6780e34 was made by scripts/pm/os-regen-merge.sh, as a plain merge: no rebase, no force-push. Main did not touch packages/spec, and check:generated is green after the merge.\n(1) ADR anchor: docs/adr/0096:194. Precedent is ADR-0015's path note, which says 'historical and deliberately unlinked'. The anchor grammar judges a #symbol anchor, but in docs/adr it does not judge a bare path (symbol-anchors.mjs: checkBarePaths is off for this corpus). So the smallest truthful edit drops the #isSystem half, keeps the path and `dispatchDue`, and adds 'historical and deliberately unlinked: the saved-report stack was retired whole by #20102'. No anchor-exempt marker was used.\n⛔ THE PR NOW TOUCHES A GOVERNED PATH (docs/adr/**, Tier H). `node scripts/pm/check-governed-merges.mjs --pr 20125` could not run here: page 2 of the file list got HTTP 403, and the tool refused rather than fall back. The local equivalent `--branch` at aab0e0f says: GOVERNED, 1 of 107 paths (docs/adr/0096); landing tier H(人合), i.e. the maintainer's hand or an authorized APPROVED review; also HUMAN MERGE for size, 11576 changed lines. The PR body predates the ADR edit and does not yet say it touches a governed path. The body is dev-write-once, so the seat owes a line: 'this PR edits docs/adr/0096 (Tier H) to unlink the retired anchor'.\n(2) Dogfood: the matrix docblock figures went 91 -> 83 rows and 19 -> 18 families. The full local suite also surfaced more of this PR's fallout. `authz-ledger-population.baseline.ts`: the stale `rest-family:rest-route-ledger.ts:reports` key was dropped, and the shrink-only ceiling lowered 34 -> 33. `authz-probe-blind-spot.census.ts`: rest-route-ledger row 83/83 with 18 keys; rest-server row 72/19/53 with registrars 16, register sites 65, enforceAuth 56; totals 67/72. Every value was read from deriveProbeFileCensus(), not typed.\n(3) lint.yml steps 104-190 (yaml index; CI step numbering is +1) were run locally in declared order: all exit 0. Steps 191/192 are `if: failure()`. origin/main has moved again since (now bc80e16); not re-merged.", "tests": "Head aab0e0f.\n\nDogfood:\n- Full suite before the census fixes: 5 failed, 1098 passed. authz-conformance failed 'every baselined key IS minted' (33 vs 34). authz-probe-blind-spot failed 4 (population 83 vs 91, 72 vs 80, plus controls).\n- After the fixes, the two files: 2 files / 88 passed.\n- Full suite at aab0e0f: `pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2` gave Test Files 137 passed | 1 skipped (138); Tests 1103 passed | 3 skipped (lock VERDICT command-exit 0, 626s). Shards 1/3 and 3/3 are covered by the one full run.\n- dogfood typecheck: exit 0.\n\nADR anchors:\n- `node scripts/check-adr-symbol-anchors.mjs` exit 0: '2123 anchors across 140 records resolve ... 6 exempt' (exempt count unchanged).\n- lint steps 9, 102 and 103 re-run: exit 0.\n\nlint.yml steps 104-190, run in declared order with each run: under bash `set -e`:\n- 86 exit 0 on the first pass.\n- 177 (plugin teardown-shape) first exited 3: its self-test fixture commit 621a487 was absent from the shallow clone. After `git fetch --depth=1 origin 621a487`: exit 0, 48 cases pass, 67 Plugin implementations.\n- After the amend, re-ran 104, 105, 107, 108, 156, 162, 169, 170, 184 and 188: all exit 0. 188 (issue citations): 25 resolve.\n- Also `pnpm --filter @objectstack/spec check:generated`: all 15 up to date.\n\nNothing is left red locally.\n\nNOT MEASURED: `check-governed-merges --pr 20125` (HTTP 403 reading page 2 of the PR file list; the `--branch` equivalent was run instead); steps 191/192 (`if: failure()` only).", "mcp_calls": "0", "api_writes": "1 this round — comment POST /repos/objectstack-ai/objectstack/issues/20102/comments via the fleet-write relay (this report). Plus git push of 527030b + aab0e0f (not REST). Round 1 had 2 (pr_create, report comment).", "open_questions": [ { "question": "The PR now carries a Tier H path (docs/adr/0096), so it lands only by the maintainer's hand (it already needed a human merge for size). Should the ADR edit be split into its own docs-only PR so the code retirement is not gated on Tier H?", "options": [ "A: keep it in this PR — it is already a human merge (11.5k lines), so Tier H adds no new blocker, and the retirement is what makes the anchor fail", "B: split — impossible without leaving this PR red: Lint step 104 fails as soon as the file is deleted, so the ADR line must land with (or before) the deletion" ], "recommendation": "A. B cannot keep CI green, and the size rule already routes this PR to a human merge." } ], "out_of_scope_findings": [ "carrier: PM seat · PR 20125 body predates the ADR edit and should state that it touches docs/adr/0096 (Tier H); dev body is write-once, so the seat writes it", "carrier: PM seat · origin/main moved to bc80e16 after the round-2 merge; not re-merged this round" ] }Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
os-dev-report
{ "issue": 20102, "status": "done", "branch": "claude/issue-20102-retire-saved-report-stack", "pr": "https://github.com/objectstack-ai/objectstack/pull/20125", "session": "session_013RWUA7bNq5bRhehLPqXwMg", "premise_still_valid": true, "summary": "Contract review round 1 (FAIL at aab0e0f) is fixed at head e4ead74, on top of a merge commit of origin/main f09d412 (4ee2885: clean, no conflicts, nothing left in os-regen-pending). Blocking 1: renderCapabilityMessage (packages/cli/src/utils/capability-preflight.ts) now returns RETIRED_PLATFORM_CAPABILITY_GUIDANCE[token] for a retired token, read as an own property, in its unknown branch. So os validate and os build over a plain-object config show the same prescription that defineStack and serve use. The posture is unchanged: still a non-fatal advisory, exit 0. Blocking 2: skills/objectstack-platform/SKILL.md drops reports from the CAPABILITY_PROVIDERS table. I measured the map at 20 entries; the table lists the 19 opt-in ones, because package-registry is always-on and was already missing from the table. The lead-in now reads \"its 19 opt-in entries\". The first spelling went 6 tokens over the ceiling (5839 against 5833, CI red on 273e529). The reword lands at 5831. Non-blocking: I fixed the three stale comments. The setInterval claim is re-measured: of 14 non-test source files that call setInterval, ExternalValidationPlugin is the only enclosing class that implements Plugin. The changeset first bullet now names the validate/build door. The PR body is NOT written. The corrected body is at scratchpad issue-20102/pr-body-after.md; see open_questions for why.", "tests": "All readings are at e4ead74 unless marked. REPRO BEFORE at 4ee2885 (scratch plain-object config, requires reports): os validate --json and os build --json both exit 0, each with warning {token:reports, message:\"requires: \\\"reports\\\" is not a known platform capability — check for a typo.\"}. AFTER: both exit 0, and the message is exactly \"requires: 'reports' was removed in @objectstack/spec 17.5.0 — ... Delete the token. ...\"; the text face prints the same line. Tests: cli capability-preflight.test.ts + vitest-tiers-partition.test.ts gave 2 files, 39 passed. requires-retired-capability.e2e.test.ts (new; nightly tier by its .e2e name, integration project; run with OS_TEST_TIERS=nightly) gave 3 passed. cli --project unit gave 224 files, 3157 passed. spec reports-capability-retirement + platform-capabilities gave 39 passed. runtime external-validation-shutdown-clears-timers gave 6 passed. platform-objects object-field-editor-panel-echo-decisions gave 11 passed. Dogfood, 3 shards (after building the dogfood^... closure): 46/354, 46/311 (1 skipped), 45/438 (1 file and 2 tests skipped). All runs under os-verify-lock, VERDICT command-exit 0. Typecheck: cli, spec, runtime and platform-objects exit 0 (VERDICT command-exit 0). ABLATION, committed first at 37a3ae6c: scripts/ablation-replace.mjs replaced the renderCapabilityMessage retired branch anchor with \"if (false) {\". The anchor went x1 to x0 and the blob 355e5a2e to 3f19d585. The unit case went red (1 failed/16 passed) and so did the e2e (2 failed/1 passed: the validate and build cases). The tool restored the file: blob == HEAD 355e5a2e, git diff HEAD empty. No dist is involved: the cli tests import src, and run-dev.js runs src through tsx. GATES: dispatch-gates --commands over this round's 9 paths derived 100. All 100 exit 0. Nine of them first exited 3 with PREREQUISITE NOT MET (stale spec dist; the CLI and 8 packages had no dist) and were re-run after the builds they named. check-skills-token-ratchet: platform SKILL.md at 5831/5833. The branch-wide --ran reconciliation found 175 derived: 100 run plus 75 unrun. I ran all 75: 73 exit 0, including check:generated, check:skill-examples, check:spec-changes, check:upgrade-guide, check:adr-anchors, check:error-code-casing and check:platform-checklist. NOT MEASURED: check-engine-split-ratio --days 90 (shallow-clone refusal, exit 2) and check:pm-dispatch-gates (timed out at 400s, exit 124); CI owns both. LINT, narrowed and proven: eslint --no-inline-config --format json over the 7 changed TS files gave 7 files, 0 errors, 0 warnings. --print-config resolves a config for all 7. Type-aware linting is off (eslint.config.mjs has no parserOptions.project), so untouched files' verdicts cannot move. The repo-wide pnpm lint is CI-owned. SKILL READINGS: objectstack-platform/SKILL.md has 487 lines before and after, and 5833 to 5831 tokens; all SKILL.md files together are 4402 to 4402 lines.", "mcp_calls": "0", "api_writes": "1: POST /repos/objectstack-ai/objectstack/issues/20102/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). All GitHub reads were REST GETs (PR 20125 comments, PR 20125, issue 20102 comments). git push is not counted.", "open_questions": [ { "question": "The dispatch says to rewrite the PR 20125 body with post-stamped --body. .claude/agents/os-dev.md says the dev writes the PR body once, at pr_create, never PATCHes it, and that the seat writes later changes; it also says os-dev.md wins over a dispatch. Who writes the body?", "options": [ "A: The seat posts the prepared body: node scripts/pm/post-stamped.mjs --body=20125 --file=/tmp/claude-0/-home-user/9ff87ff8-9891-5379-8c0a-15ca322ec9d8/scratchpad/issue-20102/pr-body-after.md --ack-through=5831670763 (5831670763 was the newest comment when I last read). It names all three doors, adds the round-1 fixes section, and moves the skill file into the governed statement (2 of 114 paths).", "B: The dev writes it next round, if the PM amends os-dev.md or grants an explicit exception." ], "recommendation": "A. It follows the standing contract (the round-2 dev did the same), and the file is ready to post as it is. I read it back for angle-bracket fragments: there are none." } ], "out_of_scope_findings": [ "class: a · classifyRequiredCapability (packages/spec/src/kernel/platform-capabilities.ts) reads PLATFORM_CAPABILITY_PROVIDERS[token] without an own-property check, so an inherited name classifies as installable with no package. Repro at 37a3ae6c: a plain-object config with requires [constructor, toString] makes os validate --json exit 0 with warning \"Capability \\\"constructor\\\" requires undefined, which is not installed. Run `pnpm add undefined`, or remove \\\"constructor\\\" from `requires`.\" (same for toString). defineStack configs are unaffected (refused as a typo). Seam: spec:classifyRequiredCapability → runtime:packages/cli/src/utils/capability-preflight.ts preflightRequiredCapabilities. It is outside this dispatch (\"fix exactly these\"), so for the seat to file. Dedupe words: classifyRequiredCapability constructor; pnpm add undefined; PLATFORM_CAPABILITY_PROVIDERS own property; requires inherited name", "carrier: none · noted, not filed. Before this round the platform skill table claimed \"all 20\" entries of a 21-entry map (package-registry was never listed). The new lead-in (\"its 19 opt-in entries\") is accurate, and there is nothing to carry.", "carrier: PM seat · noted, not filed. The door e2e requires-retired-capability.e2e.test.ts is nightly-tier by name, like its neighbour build-json-advisory-parity.e2e.test.ts. Queue-time protection for the text is the unit case in capability-preflight.test.ts. Renaming the file without .e2e would move it into the queue integration tier: 2 CLI spawns, about 6s each. That is a cost call for the seat." ] }test(spec): kernel test titles state each cited decision in words ins…