Repository navigation
feat(rest, client): a tenant-admin REST door for the record-currency code backfill - #30809
Conversation
…code backfill POST /meta/_migrate-record-currency-codes runs backfillRecordCurrencyCodes for the caller's current organization only, gated on setup.write and mounted behind metadata.endpoints.maintenance; preview unless apply is true. Adds the SDK method, the REST route-ledger row, the objectql barrel re-export of the two organization report types, the 409 remedy naming the door, and a truncated walk that no longer claims every row was examined. Claude-Session: https://claude.ai/code/session_016WV4cyrhYJgbkZHbbqud3F Co-authored-by: Claude <noreply@anthropic.com>
…the truncated walk text Claude-Session: https://claude.ai/code/session_016WV4cyrhYJgbkZHbbqud3F Co-authored-by: Claude <noreply@anthropic.com>
… a refused row reads the hook's sentence The new `POST /meta/_migrate-record-currency-codes` write door joins the censuses that enumerate every REST write door and registrar module: - the `/meta` write-door enumeration (a DATA door, gated on `setup.write`, with its own refusal and control cases), the #8497 record-echo tripwire (no record echo), the #22694 hook-refusal ledger (driven per row), the registrar file lists, the objectQLProvider consumer census (7 sites), the query-slot census (no slot) and the docblock floors. Driving it through the hook-refusal ledger found a real leak: a row whose code write a sandboxed hook refused reported the `hook '<name>' threw:` debug wrapper as its failure text, and a crashed hook's row its native error. The backfill now reads the throw as the import row does: the hook's own sentence for a refusal, the `/data` door's withheld sentence for a 5xx. The operator's log line keeps the whole text. Measured by ablation: without the objectql change, 12 rows of the ledger go red. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyrhYJgbkZHbbqud3F
…cy backfill door `check:system-context-census` reds on an elevation read no row owns: the new door's capability gate lets `isSystem` through. Row 50b names it, and the generated register is rewritten with `--write`. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyrhYJgbkZHbbqud3F
…cy backfill door `metadata.endpoints.maintenance` now also mounts `POST /meta/_migrate-record-currency-codes`, and each endpoints switch's describe() enumerates every mount it gates. The reference page is regenerated from the schema. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyrhYJgbkZHbbqud3F
📓 Docs Drift CheckThis PR changes 6 package(s): 38 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 12 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 182 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5f552fc5951bf2322833ea08f9b87d053ee2e045 && git checkout 5f552fc5951bf2322833ea08f9b87d053ee2e045
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7da99b79f5fcf3fa1dfc143f39d73fb702dfb271 12af728fa8a19542811c866268a2bbb27cd105c4 && git checkout -B drift-repro 7da99b79f5fcf3fa1dfc143f39d73fb702dfb271 && git merge --no-ff 12af728fa8a19542811c866268a2bbb27cd105c4
node scripts/docs-audit/affected-docs.mjs --json 7da99b79f5fcf3fa1dfc143f39d73fb702dfb271
|
|
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW Contract reviewServed-tier: Read: card #30675 (body, all 7 comments: claim 6675312689, amendments 6675340866 and 6675450053, dev report 6677009085), ruling 6670707546 item B, A card #30674 and its ACCEPT 6673657336, domain:spec#2's consent 6675431663, the PR body, and ① Derived judgmentsShape.
Observed, not blocking.
② Semver level
The spec hunk against consent 6675431663, line by line. One file under File surface against the claim and its amendments. Inside: Check-runs on the head at 2026-11-06T04:51Z: 46 runs: 25 completed (15 success, 9 skipped, 1 failure), 21 in progress, 0 queued. Of the ten required contexts, ③ Boundary flags
Implemented-by: VERDICT: PASS Must-fix: none. Should-fix (for the ACCEPT or a follow-up card, none blocking this head):
Generated by Claude Code |
|
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW 席位核对 · ACCEPT · 2026-11-06T05:29Z · #30675Head-sha: 12af728
|
…ng under D5, with a pointer from D2 (objectstack-ai#31986) <!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} --> _Requested by **Steedos** · [project thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbRRWh7f8hbG46SXpVKnf2GM)_ Fixes objectstack-ai#31977 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:只改 ADR-0144 的文字(一个文件,+37/−0)。D5 下加一条 2026-11-09 注:保存对象元数据时,如果把已存数据的 record 模式金额字段的 `currencyField` 改指到别的币种字段(含经 master_detail 指向主记录币种的两段式写法),保存会返回 409 `DESTRUCTIVE_CHANGE`(发现码 `currency_field_repointed`),除非调用方带 `force`。D2 下加一行指针,并说明它和 D2 的「主记录币种锁」不是一回事。 - **为什么改**:这条 409 已随 PR objectstack-ai#31969 上线,代码里引用的是「ADR-0144 D2, D5」,但 ADR 里没有这条规则,维护者顺着引用查不到。现在 ADR 写明了:改指针不换算金额,每一行已存的金额会被当成另一种币种来读,已存的本位币金额和汇率也不会重算。 - **风险与回滚**:不改代码、不发版,只改 ADR 文字。回滚就是 revert 这一个提交。 - **先看**:`docs/adr/0144-…md` D5 末尾的 2026-11-09 注(触发条件、409 与 force、补救命令);D2 末尾的一行指针。 - **两分钟验证**:`git grep -n "currency_field_repointed" -- docs/adr/` 应命中 2 处(D2、D5);Open item 13 下新增一条 2026-11-09 注,说明 objectstack-ai#30675 已落地;`pnpm check:adr-symbol-anchors` 应为绿。 - **需要你做的**:批准(或合并)本 PR。这是 Tier H,只能由你落地。 ## What changes One file, `docs/adr/0144-multi-currency-record-currency-dated-rates-write-time-home-projection.md`, two dated notes. No ruled sentence or quote is edited. A third dated note under Open item 13 records that its B half (objectstack-ai#30675) has landed. - **D5 note** (`*Note (2026-11-09):*`, after D5's last bullet). It records the gate PR objectstack-ai#31969 (`33ab84f9`, card objectstack-ai#31949) landed, read from the merged code (`packages/metadata-protocol/src/protocol-write-face.ts#detectDestructiveObjectChanges` and `packages/metadata-protocol/src/protocol-save.ts`): - **Trigger:** an `object` save that changes a stored `record`-mode amount's non-blank `currencyField` to another non-blank pointer, in all four directions between a sibling code field and the two-segment `master_detail` path of the D2 amendment. Pointers compare as written. - **Answer:** refused before anything is written, 409 `DESTRUCTIVE_CHANGE` with the finding in `issues`, unless the caller sets `force`. `?force=true` on the metadata `PUT`; the doors that accept no `force` name their own way in the message (`#destructiveChangeRemedy`). - **Family:** its own finding code in the Phase 3a destructive gate, the sibling of `currency_mode_to_record` (Open item 13). It is not a `field_type_change`: that finding fires only when a field's `type` changes, and a re-point keeps `type: 'currency'`. - **Reason:** kept to D5's subject. D5 converts at write and stores the result "with the rate used"; a stored home amount changes only when a write of its row projects again or through "an explicit recompute operation", and a metadata save is neither. An amount is a bare number (ADR-0104 D1), so a re-point converts nothing. - **Remedies, by the merged spelling:** `os migrate record-currency-codes` or `POST /api/v1/meta/_migrate-record-currency-codes` for a sibling pointer whose new code is blank; no backfill for a path pointer; `os migrate recompute-home-amounts` when `homeAmount` is declared before and after the save (a shell command, no REST door); for a path pointer that command fails on `main` (`INVALID_FIELD` / 400 on the dotted projection) until objectstack-ai#31911. When the save drops `homeAmount`, the message says nothing re-rates the stored home amounts. - **D2 note**: one pointer to the D5 note, because D2 defines `currencyField`. It also separates the finding from the parent-code lock above it: the lock refuses a record write that changes a parent's code value (`VALIDATION_FAILED` / 400); the finding answers a metadata save that changes which field holds an amount's code. No code, schema, test or changeset. `skip-changeset`: `docs/adr/**` publishes nothing. The code already cites `[ADR-0144 D2, D5]` and is not edited. ## Tests None. This PR is ADR prose only. ## Acceptance notes - **Status line not amended.** Measured on `ca3629fe`: the record holds 16 dated `*Note (DATE):*` entries before this PR (10 of them 2026-11-04, the rest 2026-10-28 to 2026-11-09) and none has a Status entry; the one Status `Amended` entry belongs to the objectstack-ai#31743 ruling. This note records the merged gate under existing decisions and no new ruling, so it follows the dated-note form with no Status entry. - **Family differs from the card.** The card names `field_type_change`. The merged code raises `currency_field_repointed` as its own finding, documented in the code as the sibling of `currency_mode_to_record`; `field_type_change` is raised only on `prevType !== nextType`. The note names the code's placement. - **Reason worded from D5's own sentences.** The brief's "a stored amount is not relabelled without being re-rated" is not a sentence in D5, so the note quotes the two D5 sentences the rule rests on instead of coining a principle. - **One code-comment clause left out.** The finding's code comment says a field that was a formula raises nothing. The pointer reader requires `type: 'currency'`, which the stored-field predicate (`isVirtualSearchField`, a type set) never counts as virtual, so that conjunct cannot fire for this finding. The note omits it rather than state an unreachable case. - **Contract review fixes (record 6754116140, then delta record 6754361348; final head 9165d3b).** S1: the recompute sentence now says `os migrate recompute-home-amounts` re-derives the home amounts for a sibling pointer only; the no-backfill edge points at Open item 14. S3 (delta review record 6754361348, head 9165d3b) corrected the path-pointer mechanism: `recomputeHomeAmounts` (`home-amount-recompute.ts`) puts the dotted pointer into the `fields` projection of its first page read, and `ObjectQL#find` refuses a dotted projection `INVALID_FIELD` / 400 (`engine-top-level.ts#assertProjectionHasNoDottedPaths`, called from `engine-find.ts`), so the command fails before any row is read and no home amount is re-derived until objectstack-ai#31911. S2: the reason keeps D5's subject: a stored home amount changes when a write of its row projects again or through "an explicit recompute operation", and a metadata save is neither (`home-amount-projection.ts` re-projects on a write touching the amount, its code, its document date, the home field or the rate field; wording per N4). N1: a dated note under Open item 13 settles its B half: objectstack-ai#30675 closed as completed with PR objectstack-ai#30809 (`0cba0ce2`), and `POST /api/v1/meta/_migrate-record-currency-codes` is on `main`. Diff vs main now +37 / -0, still one file. ## Verification <details><summary>dispatch-gates: 21 derived, 21 run, all exit 0 (head 9165d3b on origin/main c924627; also green at 5e1bf7e, a2da710 and 383d12a)</summary> ``` node scripts/check-adr-links.mjs :: exit 0 node scripts/check-adr-links.mjs --self-test :: 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-corpus.mjs :: exit 0 node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0 pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 (after building @objectstack/lint and its deps under the verify lock, 84 s) pnpm check:adr-anchors :: exit 0 pnpm check:adr-index :: exit 0 pnpm check:adr-symbol-anchors :: exit 0 (3391 anchors resolve, 872 declaration; origin/main ca3629f reads 3388 / 870: the two new symbol anchors and one file anchor resolve) pnpm check:ci-filter-parity :: exit 0 pnpm check:closed-ceremony-text :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:doc-authoring :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:gitlink-declared :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:pm-governed-merges :: exit 0 pnpm check:pm-prior-rulings :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:watch-hint-literal :: exit 0 dispatch-gates --ran: 21 derived famil(ies) accounted for — 21 run, 0 NOT-MEASURED ``` </details> `dispatch-gates --tier` derives MANDATORY claude-fable-5-1 for `docs/adr/**`; built at the default tier under the maintainer's standing rule 「后续不再派 fable 卡」, as recorded in the claim. Tier H (`docs/adr/**`, Prime Directive objectstack-ai#14): this lands by an authorised approval or the maintainer's merge. The contract review at tier is the seat's. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01QsR2jUfbR3DhK4kBEC6qWR --- _Generated by [Claude Code](https://claude.ai/code/session_01QsR2jUfbR3DhK4kBEC6qWR)_ <!-- signed: domain:spec#2 · zhuangjianguo · session_01QsR2jUe9c6YdtNkrzUnMUd --> --------- Co-authored-by: Claude <noreply@anthropic.com>
Requested by Elon · project thread
domain:server#1 · os-elon-musk · session_016WV4cyrhYJgbkZHbbqud3F
Fixes #30675
Clause-②: yes (widening) — a new route
POST /api/v1/meta/_migrate-record-currency-codes(behindmetadata.endpoints.maintenance), a new SDK methodclient.meta.migrateRecordCurrencyCodes, and two type exports on the@objectstack/objectqlentry (RecordCurrencyBackfillOrganization,RecordCurrencyBackfillOrganizationsRefusal).Metadata-protocol: none
Claim: 6675312689 (amended 6675340866, 6675450053) · Branch:
claude/project-thread-1lyw0v· Ruling-ref: 6670707546 (letter C, item B)维护者速读
POST /api/v1/meta/_migrate-record-currency-codes,请求体{ object, field, code, apply? },只补调用者当前组织的无码行;不带apply: true是预览、不写;SDK 加client.meta.migrateRecordCurrencyCodes。409destructive_change的补救文字现在同时点名 CLI 和这个门。currencyMode: 'record'后,老行有金额没币种,补救只有os migrate record-currency-codes,云上租户管理员碰不到数据库 shell(裁决 6670707546 C 的 B 项)。setup.write,请求体不收任何组织参数(organizationId等一律 400),无当前组织 403,maintenance开关关掉就不挂载。顺带修了一处泄漏:被沙箱钩子拒写的行,报告里原来是hook '<name>' threw:调试包装,现在是钩子自己的那句话。回滚即撤 PR。packages/rest/src/meta-record-currency-backfill-route.ts(门禁顺序、组织取自ctx.tenantId、请求体白名单),packages/objectql/src/record-currency-backfill.ts的rowFailureSentence,packages/rest/src/meta-write-door-capability-enumeration.test.ts新增的DATA_DOORS一节。pnpm --filter @objectstack/rest exec vitest run src/meta-record-currency-backfill-route.test.ts,预期 12 条全绿,其中「the other organization's admin sees and writes only its own row」断言 B 组织管理员只碰到b1、A 组织的行一行未动。What changes
@objectstack/rest: the door (meta-record-currency-backfill-route.ts, new; registered fromregisterMetadataEndpointsInnerbeside the object state read)POST {metaPath}/_migrate-record-currency-codes, on every mount base the/metafamily is mounted on, behindmetadata.endpoints.maintenance(the_migrate-storedswitch). It sits inside the/metaumbrella, so an anonymous caller gets the umbrella's 401.setup.writeinsystemPermissions(resolved for the current organization), orisSystem. Otherwise403 FORBIDDEN. This is asked first, before the body or the engine, so a refused caller learns nothing about objects or kernels.ctx.tenantId. None means403 FORBIDDEN("no current organization … Nothing was read.").{ object, field, code, apply? }. Any other key is400 VALIDATION_ERRORnaming it.organizationIdandallOrganizationsget their own sentence ("this door fills the rows of your current organization only"). Blank names and a non-booleanapplyare refused the same way.wiredEngineOrLoud.@objectstack/objectqlstays a devDependency and is loaded by a guarded dynamic import. No engine, or no function, means501 NOT_IMPLEMENTED.backfillRecordCurrencyCodes(ql, logger, { object, field, code, apply, organizationId }). The answer is{ success: true, data: <report> }, the report the CLI's--jsonprints.INVALID_FIELDwithdetails.fields,OBJECT_NOT_FOUND404); anything else goes tosendServerFault.REST_ROUTE_LEDGER(disposition: 'sdk', noauthzfield, because there is no conformance coverage yet),REST_SERVER_REGISTRAR_FILESandREST_SERVER_META_ROUTE_FILES, and thescripts/check-undeclared-dep-imports.mjsLEDGER (oneoptional-runtime-proberow, seat amendment 6675450053).@objectstack/client:client.meta.migrateRecordCurrencyCodes({ object, field, code, apply? })returnsunknown(see Acceptance notes).@objectstack/metadata-protocol: thecurrency_mode_to_recordfinding of the 409destructive_changenow names the route and its body beside the CLI command.@objectstack/objectqlRecordCurrencyBackfillOrganizationandRecordCurrencyBackfillOrganizationsRefusal(carried item 1 from the cli + objectql: record-currency backfill scoped per organization —--org, refuse multi-org runs without it,--all-organizationsexplicit; recompute takes the same--org(multi-currency, epic #8345, ruling C item A) #30674 ACCEPT).truncated. The text no longer prints "✓ Every … nothing to backfill" for rows it never examined. An unscoped run adds that its organization check saw only the rows examined. The CLI prints aWalk incompletewarning. REST and CLI answer the same report.errorreads a sandboxed hook as the write doors do (rowFailureSentence, the import row's rule). A refusal gives the hook's own sentence. A crash, a declared 5xx or the sandbox's own fault gives the/datadoor's withheld sentence. Any other throw keeps its message. The operator'slogger.warnline keeps the whole text.Docs
content/docs/deployment/cli.mdx: a truncation paragraph, then "Without shell access, use the route" with the HTTP and SDK forms. It covers the caller's organization only, the refusals, the capability and the switch, and says there is no Setup button yet.content/docs/data-modeling/currency.mdx: one sentence.content/docs/permissions/system-context.mdx: row 50b and the regenerated register.Tests
packages/rest/src/meta-record-currency-backfill-route.test.ts(new, 12 cases, realObjectQLover an in-memory driver, two organizations):b1.isSystemwith a current organization passes the gate and stays in that organization.setup.writegives 403 with the provider never called; no current organization gives 403;organizationIdorallOrganizationsin the body gives 400; a malformed body gives 400; a non-record-mode field givesINVALID_FIELDwithdetails.fields; an unknown object gives 404; no engine gives 501.maintenanceswitch off unmounts the route and the other switches leave it; the scoped base mounts it.meta-write-door-capability-enumeration.test.ts: the door joins the closed set of/metawrite doors as aDATA_DOORSentry. It is not amanage_metadatadoor: it reaches no protocol method. It has its own cases. Anonymous gets 401. A capability-less caller, amanage_metadataholder andsetup.accessalone each get 403.setup.writeandisSystempass the gate on both bases, reaching 501 with no engine.rest-write-route-hook-refusal-sentence.ledger.test.ts: the door is driven per row (§2 and §6, 12 shapes), with the failure entry as the answer. The harness caller gainedsetup.writeand an organization, and its engine double gainedgetObject. The whole file is green.no-record-echo), theobjectQLProviderconsumer census (6 → 7 sites, all throughwiredEngineOrLoud), the query-slot census (0 slots), the docblock floors (7 blocks), andrest-config-mount-table.pin.test.ts(the maintenance row gates four routes).packages/objectql/src/record-currency-backfill.test.ts: a walk cut short bymaxRecordssays so, and the new describe "a failed row reads a sandboxed hook as the write doors do" (a refusal, a crash and an own fault, and a plain-throw control).packages/cli/src/commands/migrate/record-currency-codes.test.ts:--org org_a --max-records 2prints the incomplete-walk warning.packages/client: the method POSTs the body and unwrapsdata, andapplyis sent only when true; its return type is pinned asunknown.protocol-destructive-currency-mode.test.ts: the remedy names the route.Acceptance notes
domain:specconsent.packages/spec/src/api/rest-server.zod.ts(L538–541) said the switch mountsGET /meta/diagnostics,GET /meta/_draftsandPOST /meta/_migrate-stored, and did not name this door. Commit12af728faddsPOST /meta/_migrate-record-currency-codesin the sentence's own list shape and nothing else underpackages/spec/src. It also carries the regeneratedcontent/docs/references/api/rest-server.mdxrow and'@objectstack/spec': patchin the card changeset, underdomain:spec#2's consent 6675431663 (mailbox PR pm:mailbox — domain:spec#2 · os-sales #30557). The hunk raises this PR's review to CONTRACT_REVIEW_TIER.unknown, not the report type. Ruling 2C and the seat's amendment keep it untyped likemigrateStored. Ananyreturn would red the shrink-onlypackages/client/exported-any-returns.json, so it isunknown. Callers who want the type import it from@objectstack/objectql._migrate-storedanswers bare. The new module is born onsendOk/sendError/sendServerFault(check:route-envelopeasks new modules for the shared pair). So the report isdata, and refusals are{ success: false, error: { code, message } }.{ isSystem: true, tenantId: <current organization> }as the CLI's do, somodified_byis not the admin's id. The door's gate is the attribution point.organization_id) is refusedINVALID_FIELD/ 400 by the backfill. Its sentence says to run it without an organization, which is the CLI's form; over REST there is no such form, by design.Co-authored-by: ClaudeplusClaude-Session.Verification
Local tests and type-checks (head b56a12d)
pnpm --filter @objectstack/rest exec vitest run(whole package): 396 files passed; 7710 tests passed, 354 skipped.pnpm --filter @objectstack/objectql exec vitest run(whole package): 557 files, 11089 tests passed.pnpm --filter @objectstack/client exec vitest run: 54 files, 703 tests passed.pnpm --filter @objectstack/metadata-protocol exec vitest run: 319 files passed (4 skipped); 22480 tests passed.pnpm --filter @objectstack/cli exec vitest run --project integration src/commands/migrate/: 49 files passed (1 skipped); 375 tests passed. The migrate tests live inintegration.pnpm --filter @objectstack/dogfood exec vitest run test/authz-probe-blind-spot.test.ts test/authz-conformance.test.ts(they read the registrar lists): 70 passed.tsc --noEmitfor rest, client, metadata-protocol and objectql: exit 0.turbo run buildunder the verify lock: 119/119 tasks, VERDICT command-exit 0.pnpm lint: exit 0.Ablation
The failure-sentence fix was measured as follows.
rowFailureSentence(err, object, message)was replaced bymessageinrecord-currency-backfill.ts, and@objectstack/objectqlwas rebuilt, because rest resolves it throughdist.rest-write-route-hook-refusal-sentence.ledger.test.tsthen gave 12 failed and 613 passed. After restoring the file and rebuilding, it gave 625 passed.Derived gates (dispatch-gates --commands, reconciled with --ran)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 116 families on headb56a12d6. Each was run with its exit code captured before any pipe.The first pass, at an earlier head, had one finding:
check:system-context-censusexited 1, because the door'sisSystemcapability bypass was a read no row owned. Row 50b now owns it, and the generated register was rewritten with--write.The second pass had one prerequisite miss:
pnpm --filter @objectstack/spec run check:docsexited 1 with "packages/spec/json-schema is older than packages/spec/src". That is a gitignored local artifact, made stale by preparing the held spec hunk. Afterpnpm --filter @objectstack/spec gen:schema, it exited 0 ("241 generated files in sync"). The CI families outside the derivation are read from the PR's check-runs, not rerun here.Human Minimum
packages/specholds no runtime logic. (packages/specgains one describe string only, at12af728f.)pnpm lintpasses.main, one pull request.packages/spectouched: one describe hunk, underdomain:specconsent 6675431663; the generated docs row comes fromgen:docs, andcheck:generatedpasses all 14 artifacts.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code