Repository navigation
spec: 1 duration key(s) in kernel/plugin-security-advanced.zod.ts name their unit only in JSDoc — #15939 Ruling A remediation (1 of the 21-row delta) #17781
Description
Activity
zhuangjianguo commented
on Sep 13, 2026 CollaboratorMore actions⚠️ Pre-dispatch measurement: this card's key is the one the rest of the epic wrote prose about, and a pin test currently asserts it stays bareEpic PM for #15939,
session_015c5G6TmpMKgnusmTpD7Ntt, 2026-09-13T08:30Z. Measured onorigin/main@84e6b05. ⛔ Not a re-scoping of the card — the row it owns is unchanged. This is the collision surface a round would otherwise discover halfway through.packages/spec/src/kernel/plugin-security-advanced.zod.tsresourceLimits.timeoutisRuntimeConfig.resourceLimits.timeout— the key the whole of #15939 was originally filed about. Five live sites onmainname it in prose:site what it says src/kernel/plugin-security-advanced.test.ts:401a pin test asserting the key parses bare src/migrations/entries/semantic/18.kernel-plugin-security-durations-unit-in-key.ts:35"One key deliberately left alone: RuntimeConfig.resourceLimits.timeout"src/migrations/entries/retired-keys/18.kernel__SandboxConfig__process.timeout.ts:7calls it "a DIFFERENT key … outside the gate's population" src/migrations/registry.ts:8606,:12662generated mirrors of the two above packages/spec/CHANGELOG.md:1892published release history 1. A pin test will go red, and that is the test working — ⛔ not a defect to silence
it('leaves `RuntimeConfig.resourceLimits.timeout` bare — its describe names no unit', () => { const parsed = RuntimeConfigSchema.parse({ engine: 'process' as const, resourceLimits: { maxMemory: 1073741824, timeout: 60000 }, }); expect(parsed.resourceLimits?.timeout).toBe(60000); });
Its own comment states its purpose: "Without this test, a later sweep reads the four renames above as 'every timeout on this file'." This card is that later sweep. The guard was built to make exactly this moment explicit, and it succeeds by failing.
⇒ The round replaces the pin's meaning — from "stays bare" to "the old spelling is now refused with the rename prescription, and the suffixed spelling parses at the same magnitude". ⛔ Never delete it, ⛔ never weaken it, ⛔ never
.skipit. Its comment must be rewritten too, or it will explain the opposite of what it now pins.2. ⛔ Do NOT rewrite #15678's semantic entry — it is history and it stays true
18.kernel-plugin-security-durations-unit-in-key.ts:35reads "One key deliberately left alone … it is outside this rename; that JSDoc-channel gap is #15939." That is a scoped, past-tense statement about what #15678 did, and it remains accurate after this card lands. It is also published through the upgrade guide as "Why not automatic".⇒ ⛔ Do not amend it. Do make this card's own new semantic entry say explicitly that it completes what #15678 deliberately left alone, citing #15678 and #15939, so the two entries read as a sequence rather than a contradiction. That is the honest repair, and it is the opposite of the failure this whole epic exists to correct — where a round wrote confident wrong prose and pinned it.
3.
⚠️ Consequence for PR #17635, which Ruling A sequences to land LAST#17635's five changed files include
retired-keys/18.kernel__SandboxConfig__process.timeout.tsandregistry.ts, and its prose correction is about this very key — rewriting "outside the gate's population" to "inside the census, outside the verdict".Once this card lands, that corrected sentence is stale in a new way: the key is no longer an unjudged census row, it is remediated. ⇒ #17635's prose must be re-read against the tree after all six renames land and before it merges — the ruling's "merge
maininto #17635 and confirm the gate reads 0 offenders" step is necessary but not sufficient, because a green gate says nothing about whether that sentence is still true. Logged against the epic's landing checklist; ⛔ not this card's to fix.File-face note
This card and PR #17635 both touch
retired-keys/18.kernel__SandboxConfig__process.timeout.tsand the generatedregistry.ts. That file is deliberately not aSINGLE_CLAIM_PATHSentry, so this is ordinary concurrency: whichever lands second mergesmainand regenerates viabash scripts/pm/os-regen-merge.sh, ⛔ never by hand. Ruling A already fixes the order — #17635 is second, by construction.epic PM for #15939 ·
session_015c5G6TmpMKgnusmTpD7Ntt· 2026-09-13T08:30Z
Generated by Claude Code
zhuangjianguo commented
on Sep 13, 2026 CollaboratorMore actionsClaim: PM loop round 1 — epic PM for the #15939 subtree
Session:session_015c5G6TmpMKgnusmTpD7Ntt
Branch:claude/issue-17781-plugin-security-timeout-unit
Worktree:objectstack-issue-17781
Domain:domain:spec
File surface:packages/spec/src/kernel/plugin-security-advanced.zod.ts,packages/spec/src/kernel/plugin-security-advanced.test.ts,packages/spec/src/migrations/entries/(semantic + retired-keys),packages/spec/src/migrations/registry.ts(generated — regenerate, ⛔ never hand-merge),content/docs/references/kernel/**(generated bygen:docs, ⛔ never hand-edited),.changeset/(stop on breach; explain in the report)
Container & model:S rename + full migration chain + a pin-test replacement,mode:subagent,model: opus— quoting this dispatch's owndispatch-gates.mjs --tieroutput for this surface: "no path-derived mandate … floor sonnet · default opus · ceiling fable", and "a card changing contract accept/reject behaviour or widening the public surface is reviewed at claude-fable-5-1 in the spec seat, built at the default tier".
Clause-②: yes
Thread-read: 5652191103
Serial constraints cleared:packages/spec/src/kernel/plugin-security-advanced.zod.tsand.test.ts— zero open PRs touch either (every open PR enumerated and its file list read, 2026-09-13T08:37Z). The generatedmigrations/registry.tsis shared with PR #17954 (sibling #17784, in flight), PR #17635, #17914, #17835 and #17638 — that file is deliberately not aSINGLE_CLAIM_PATHSentry (scripts/check-single-claim-paths.mjsdeclares only.objectui-sha, and its header states a shared registry is "ordinary concurrent work"), so parallel authoring is legitimate and only landing serialises, viabash scripts/pm/os-regen-merge.sh. Sibling #17786 / PR #17953 is enqueued (added_to_merge_queue08:34:36Z) and touches none of this face. Lock readstate: lock is free,queue: empty⇒ arrival depth 1, belowLOCK_DEPTH_HOLD.Clause-②: yes, and the changeset isminor— settled before dispatch, ⛔ not for the round to rediscoverThis declaration follows the correction recorded for all six rename cards on #17784 and #15939: a rename puts a spelling on a published payload that no author could write before, which
references/contract-review.md's mechanical floor reads as a mandatory affirmative, and this lane's #16126 precedent reaches the same value through the conformance limb.⇒ Two consequences the round must build in from the start, both measured on the sibling rather than predicted:
- The changeset is
minorwith afeat(spec)!summary line,**BREAKING**and anadr-0087: registereddisposition — the shape all four landed siblings used (CHANGELOG.md:1804,:2764,:124,:675, every one under## 17.4.0). Apatchhere failsCheck Changeset; PR feat(spec)!: tenantschemaCacheTTLcarries its unit in the key name (#17784) #17954 proved it. needs:contract-reviewis hung on this card now and belongs on the PR once it exists.⚠️ That label is read bycheck-changeset-no-majoras a clause-② affirmative carrier that overrides the PR body line, so the body line must read the affirmative too, or the two disagree and the gate reds.
⭐ This card is the sharp end of #15939, and it carries three collisions already measured
Full detail in the pre-dispatch measurement above (
5652191103). In short: this key isRuntimeConfig.resourceLimits.timeout, the key the whole finding was filed about, and five live sites onmainname it in prose.- A pin test at
plugin-security-advanced.test.ts:401asserts this key stays bare and will go red. That is the test doing its job — its own comment says it exists so "a later sweep" cannot read the earlier renames as covering every timeout on the file, and this card is that sweep. Its meaning gets replaced, ⛔ never deleted, weakened or skipped, and its comment rewritten with it. - ⛔ Do not amend [#14478 stack 3/6]
kernel/: the 14 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers (runtime-emitted measurements included) #15678's semantic entry (entries/semantic/18.kernel-plugin-security-durations-unit-in-key.ts:35). Its "one key deliberately left alone" is a scoped past-tense statement about [#14478 stack 3/6]kernel/: the 14 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers (runtime-emitted measurements included) #15678 and stays true. This card's own new entry says it completes what [#14478 stack 3/6]kernel/: the 14 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers (runtime-emitted measurements included) #15678 left alone. - PR feat(spec): refuse a duration key whose JSDoc names a unit its describe does not #17635's prose correction is about this key and will need re-reading after all six land — logged against the epic, ⛔ not this card's to fix.
epic PM for #15939 ·
session_015c5G6TmpMKgnusmTpD7Ntt· 2026-09-13T08:37Z
Generated by Claude Code
- The changeset is
os-dev-report
{ "issue": 17781, "status": "done", "branch": "claude/issue-17781-plugin-security-timeout-unit", "pr": "https://github.com/objectstack-ai/objectstack/pull/17983", "premise_still_valid": true, "summary": "Renamed RuntimeConfig.resourceLimits.timeout to timeoutMs in packages/spec/src/kernel/plugin-security-advanced.zod.ts, executing ruling A on #15939 for this file's one row. Re-located by symbol, not by line: `timeout:` appears twice in the file — at the resourceLimits row and at SandboxConfig.process's #15678 tombstone — and it is declared exactly once inside RuntimeConfigSchema. Unit confirmed milliseconds (JSDoc 'Execution timeout in milliseconds'); the describe now reads 'Maximum execution time in milliseconds'. The kit is the #15678 / #15679 shape: a retiredKey() tombstone (the nested resourceLimits object is not strict), the ADR-0087 D3 semantic entry kernel-runtime-config-timeout-unit-in-key plus the RETIRED_KEYS_BY_MAJOR[18] row kernel/RuntimeConfig:resourceLimits.timeout as migrations/entries/ files with registry.ts regenerated by gen:migration-registry (never hand-merged), no D2 conversion, the reference page regenerated by gen:docs, and a minor / feat(spec)! changeset carrying BREAKING and the adr-0087 registered disposition. One implementation detail the reviewer should see: the tombstone string const is declared ABOVE RuntimeConfigSchema rather than with the file's four other RETIRED consts below it, because gen:schema and check:authorable-surface run with OS_EAGER_SCHEMAS=1, which makes lazySchema evaluate its factory at module load — a const declared after the schema would be read from its temporal dead zone. The assignee was already set by the PM and is untouched; the newest Claim: (5652254351) names this branch. PR is draft, not ready, not enqueued, no auto-merge.", "premise_verification": { "row_relocated_by_symbol": "RuntimeConfigSchema (declared at :179 on the base tree) contains exactly one `timeout:` key; the other `timeout:` in the file is SandboxConfig.process's retiredKey tombstone from #15678. Line 294 on origin/main did match, but the identifier is what was used.", "unit": "milliseconds — JSDoc 'Execution timeout in milliseconds'; corroborated by the sibling SandboxConfig.process.timeoutMs ('Process timeout in ms') on the same file and by the 30000 / 60000 magnitudes in the existing tests.", "spelling_derived_from_the_tree_not_the_order": "Ms. Counted on this tree: 29 key-position `timeoutMs` declarations across packages/spec/src (all .zod.ts), 40 distinct keys ending in Ms, 363 `timeoutMs` occurrences in packages/spec/src as the lit control; `timeoutMillis` / `timeout_ms` / `timeoutMS` = 0; dark control `qqzzxxnotakey` = 0.", "registry_is_generated": "regenerated twice with `pnpm --filter @objectstack/spec gen:migration-registry` (208 semantic / 169 retired-key / 178 retired-def). Never hand-edited. `main` merged with `bash scripts/pm/os-regen-merge.sh` — the merge brought only ADR / skill / gate-script changes, step 2 explicitly KEPT the branch's bytes of the regenerated reference page, and check:generated is green afterwards.", "sibling_entry_survival_after_regen": "exact-name grep in registry.ts: kernel-plugin-security-durations-unit-in-key 1, tenant-timeouts-unit-in-key 1, kernel-runtime-config-timeout-unit-in-key 1, kernel/SandboxConfig:process.timeout 1, kernel/RuntimeConfig:resourceLimits.timeout 1; dark control 0." }, "pin_test_replacement": { "file": "packages/spec/src/kernel/plugin-security-advanced.test.ts", "before": "A NEGATIVE control (the old :401): RuntimeConfigSchema.parse with resourceLimits { maxMemory: 1073741824, timeout: 60000 } SUCCEEDS and parsed.resourceLimits.timeout is 60000. Its comment said the key's describe names no unit, that the gate lists it without judging it, and that the JSDoc-channel gap is #15939 — it existed so 'a later sweep' could not read the four #15678 renames as every timeout on the file.", "after": "Four assertions in a new top-level describe 'RuntimeConfig.resourceLimits.timeout to timeoutMs (#15939 ruling A, #14478)': (1) the bare spelling is REFUSED — an issue exists at path resourceLimits.timeout, its code is NOT unrecognized_keys, and its message matches the rename prescription naming timeoutMs and contains 'the value (milliseconds) is unchanged'; (2) timeoutMs parses at the same 60000 magnitude beside maxMemory and maxCpu and the output carries no `timeout` property; (3) the describe publishes the unit — 'Maximum execution time in milliseconds', the text the reference pages render; (4) the two same-named `timeout` retirements on this one file name their own shapes apart, so an upgrading author edits the right block.", "comment_rewritten": "yes — the new comment records what the slot used to pin, why the control was written, that #15939 IS the sweep it was written for, and that the guard succeeded by failing. Not deleted, not weakened, not skipped, not fixed green.", "moved_out_of_the_15678_describe": "yes — it now sits in its own top-level describe rather than inside describe('Plugin security durations carry their unit (#15678)'), so that block's header ('FOUR durations ... all four old spellings are retiredKey() tombstones') stays exactly true about #15678 instead of silently becoming a five-key claim.", "proof_the_new_assertion_fails_against_the_pre_change_schema": "Ablation from the committed state. Deleted the tombstone line, proved the deletion on disk (anchor count via grep -o piped to wc -l went 1 to 0; blob hash 160ab1868dcf14e2c36a9d46877a61f748cbf14e to 50abe71cd7be66cc3352bcd3f073971e060eb0f1), re-ran the file: exit 1, 2 failed / 22 passed — exactly the two refusal assertions, with the acceptance and describe assertions still green because the ablation removed only the tombstone. Restored with `git checkout HEAD -- PATH` under an EXIT INT TERM trap and proved byte-identical (hash back to 160ab186, `git diff HEAD` empty, `git status --porcelain` empty); re-ran: exit 0, 24 passed. Predicted direction was 'turns red' and that is what was observed. No dist leg: the test imports the schema by relative path from src, so the built package is not on the resolution path for this ablation." }, "collision_2_15678_semantic_entry": { "amended": false, "why": "entries/semantic/18.kernel-plugin-security-durations-unit-in-key.ts is untouched (0 lines in the diff). Its 'One key deliberately left alone: RuntimeConfig.resourceLimits.timeout ... it is outside this rename' is a scoped past-tense statement about what #15678 did, it stays true after this card lands, and it is published through the upgrade guide.", "what_this_cards_own_entry_says": "kernel-runtime-config-timeout-unit-in-key opens with 'This entry COMPLETES what #15678 deliberately left alone, and the two are meant to be read as a sequence', then restates #15678's scope accurately, cites #15939 as the card that filed the JSDoc-channel gap and ruling A as its remediation, and cites #15678 / #15939 / #14478 / ADR-0087 by number. So the two entries read as a sequence rather than a contradiction." }, "clause2_and_changeset": { "declaration": "Clause-②: yes — on the PR body's declaration line, matching the claim and the PM corrections on #17784 (5652094166) and #15939 (5652120294).", "label": "needs:contract-review added to PR #17983 with the additive REST endpoint POST /issues/17983/labels; read back: documentation, size/m, tests, tooling, needs:contract-review — the target label is present and no concurrent label was stripped.", "check_clause2_carriers_pair_17983": "exit 0 — 'the clause-② declaration is readable in the fixed spelling and both carriers agree'.", "check_widening_tells": "with --declaration yes and the PR's own diff: exit 0 ('a yes already routes to contract review, so a tell on top of it decides nothing'). Re-run with --declaration no to surface the tells: exit 4, EXACTLY ONE tell — T1 at packages/spec/src/kernel/plugin-security-advanced.zod.ts:320 on the added line `timeout: retiredKey(RUNTIME_RESOURCE_LIMITS_TIMEOUT_RETIRED),`. The tell is on the TOMBSTONE, not on the rename: the #16943 REPLACEMENT budget absorbs the `timeout:` to `timeoutMs:` swap, and the line it fires on is the one that makes the accept set strictly NARROWER (the key becomes never). That inversion is #17955 and was expected by the dispatch. The diff was NOT reshaped to silence it and no checker was weakened.", "changeset": ".changeset/17781-runtime-config-resource-limits-timeout-ms.md — '@objectstack/spec': minor, summary line 'feat(spec)!: ...', a bold BREAKING marker, the adr-0087 registered disposition naming kernel-runtime-config-timeout-unit-in-key, a FROM to TO migration diff and the one-line fix. check-changeset-no-major --base origin/main exit 0; check-adr-0087-registration --base origin/main exit 0; check-empty-changeset --base origin/main exit 0." }, "objectui_pin": { "pin_read_from_this_tree": ".objectui-sha = 53ded82bf7a494f54e344e19099dbf00854b8694 (read from the worktree, not from the dispatch); the commit is reachable in the local clone despite the shallow checkout — git cat-file -t answers 'commit' and git ls-tree lists 6409 tracked files.", "reading": "resourceLimits.timeout = 0 occurrences at the pin.", "lit_controls_same_corpus": "timeout 832, RuntimeConfig 236, sandbox 106, resourceLimits 2; dark control qqzzxxnotakey 0. So the zero is a reading.", "what_the_two_resourceLimits_hits_are": "both are prose in packages/app-shell (runtime-config.ts and __tests__/spec-symbol-parity.test.ts) recording that objectui's own AppShellRuntimeConfig 'share not one key' with the spec's RuntimeConfig, and both name only the top-level keys (engine, engineConfig, resourceLimits) — they stay accurate after a rename one level below. Nothing in the pinned sibling authors this key, so no objectui-side fix and no pin bump are owed (AGENTS.md post-task step 4).", "citation_spelling_used": "the ASSERTING form, backticked .objectui-sha then an equals sign then the backticked sha, because the grep was taken against the sha read out of THIS tree's .objectui-sha — the pin we build against — which is exactly what that form claims and what the gate checks against the pin file. The 'pin' form would have been the false-cheap option: nothing ever checks it.", "gate": "pnpm --filter @objectstack/spec check:objectui-pin-citations — exit 0, '16 asserting objectui pin citation(s) match .objectui-sha (53ded82bf), 26 historical citation(s) recorded and not checked, across 1402 spec source(s)'." }, "tests": "Every exit code captured before any pipe (redirect stdout and stderr to a log file, then read the status into a variable on the next statement). Heavy runs through bash scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-17781, verdicts read from its VERDICT command-exit line. (1) pnpm --filter '@objectstack/spec^...' build — EMPTY CLOSURE: packages/spec has no workspace dependencies, so step 1 is a documented no-op. (2) pnpm --filter @objectstack/spec build — VERDICT command-exit 0, re-run after every source edit and after the main merge. (3) pnpm --filter @objectstack/spec test — VERDICT command-exit 0, 475 test files / 13515 tests passed. (4) pnpm --filter @objectstack/spec typecheck — VERDICT command-exit 0 (tsc --noEmit plus check:scripts-typecheck plus check:test-typecheck). (5) pnpm --filter @objectstack/spec check:generated — VERDICT command-exit 0, all 15 generated artifacts up to date after gen:migration-registry and gen:docs, re-run clean after the main merge and after the pin-citation fix. check:authorable-surface is green WITHOUT regeneration and that is correct: the ratchet records top-level keys per def and kernel/RuntimeConfig: carries exactly engine, engineConfig and resourceLimits — this key is nested one level below (0 hits for resourceLimits.timeout across authorable-surface/ and authorable-surface.base.json, against 18 for the kernel/SandboxConfig: lit control on the same corpus). (6) targeted file: pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/kernel/plugin-security-advanced.test.ts — exit 0, 24 passed. (7) ABLATION, from the committed state: tombstone deleted, deletion proved on disk (anchor count 1 to 0, blob 160ab186 to 50abe71c), suite exit 1 with 2 failed / 22 passed — exactly the two refusal assertions; restored under an EXIT INT TERM trap with git checkout HEAD naming the path, and proved byte-identical (hash back to 160ab186, git diff HEAD empty, git status --porcelain empty); suite exit 0, 24 passed. No ablation artefact left in the tree. (8) LINT — the full repo-wide run, not a narrowing: pnpm exec eslint --no-inline-config --format json over the repo root — exit 0, 6698 files in eslint's own population, 0 errors, 0 warnings, measured at final head 65dae07b9. The five changed lintable files also lint clean on their own. (9) Remote CI on head 65dae07b90: 35 check runs, 33 success and 2 skipped, 0 pending, 0 red.", "gates": { "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths passed — the script derives the change set itself from the merge base. 7 changed paths, 109 runnable commands. Derived twice: once on the pre-merge tree and once at final head 65dae07b9; the two sorted lists are identical (empty diff). The first derivation warned STALE TREE, which is why main was merged before the second.", "reconciliation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE with every line recorded as 'command :: exit N' — 109 derived, 108 run, 1 NOT-MEASURED (derived from a recorded exit 3), 0 UNRUN. A first pass recorded bare command lines and the tool refused to let its own zero stand ('the NOT-MEASURED count is the RUNNER'S CLAIM'), so the codes were added and it re-derived the classification.", "all_green": "108 of 109 at exit 0.", "not_measured": [ "pnpm check:type-check-debt :: exit 3 — PREREQUISITE NOT MET, twice, for two different reasons. First run refused because 31 workspace dependencies of the ledgered packages had no built type entry point. I then built the whole closure (pnpm exec turbo run build over ./packages/* and ./packages/*/* at --concurrency=2 — VERDICT command-exit 0, 72 of 72 tasks, 9m11s) and re-ran it; it refused again with 'tsc exited null ... but printed no recognisable diagnostics -- refusing to record 0'. That is the gate protecting its own ledger from a bogus zero, not a finding about this diff, and it is NOT a pass. Its sibling pnpm check:type-check-coverage reached a real verdict at exit 0. CI runs this step after a full build and will measure it." ], "refused_then_cleared": "Six gates first exited on an unmet prerequisite rather than a finding, and would have been silently banked as 'run' if only the command list were recorded: pnpm --filter @objectstack/lint check:doc-formula-expressions (exit 3), the same package's check:doc-security-posture (3), pnpm --filter @objectstack/spec check:skill-examples (exit 1 — a refusal spelled 1, not 3, so the reconciler cannot classify it and it is named here by hand), pnpm check:docs-transcript-drift (3), pnpm check:dual-build-cjs-loads (3), pnpm check:lean-entry-closure (3). All six were re-run after the full closure build and ALL SIX exit 0. Every number quoted in this report comes from the second run.", "one_red_found_and_fixed_before_the_final_push": "pnpm --filter @objectstack/spec check:objectui-pin-citations exited 1 at two sites (the semantic entry and its generated registry.ts mirror). Cause: the citation broke across a TypeScript string concatenation, so the sha landed on a source line the .objectui-sha mention could not reach — an unrecognised spelling, which this gate treats as a hard red precisely because it leaves the citation outside every check. Fixed by putting the mention and the sha on one source line in the asserting form, regenerating the registry, and re-running: exit 0. That is the same two-site failure sibling PR #17954 lost a cycle to.", "outside_this_reconciliation": "The tool states its own bound and it is repeated here rather than glossed: the 48 artifact-roster families, the 11 declared wide-population families, the 6 path-scheduled CI jobs and the always-runs tail are each OUTSIDE the 109, and their silence is not a clearance. Five of the 48 keep their roster in a directory one of my paths is in (check-changeset-fixed under .changeset, check:meta-url-spelling under packages/spec/src, and check:authz-resolver / check:error-code-casing / check:filter-alias-parity under packages). check:meta-url-spelling ran green inside check:generated and check:engine-double-contract ran green in the derived list. Remote CI on the head is the complete account: 33 success, 2 skipped, 0 red." }, "line_budget": "not applicable — skills/** is 0 files in this diff, so no published-skill line or token ratchet applies. Diff totals: 7 files, 333 insertions, 16 deletions, of which registry.ts (+74) and content/docs/references/kernel/plugin-security-advanced.mdx (+5 / -2) are generated.", "files_changed": [ "packages/spec/src/kernel/plugin-security-advanced.zod.ts — the rename, the describe correction, the retiredKey tombstone and its prescription const (declared above RuntimeConfigSchema for the OS_EAGER_SCHEMAS=1 reason)", "packages/spec/src/kernel/plugin-security-advanced.test.ts — the pin replacement plus two pre-existing RuntimeConfig fixtures moved to the suffixed key", "packages/spec/src/migrations/entries/retired-keys/18.kernel__RuntimeConfig__resourceLimits.timeout.ts — new", "packages/spec/src/migrations/entries/semantic/18.kernel-runtime-config-timeout-unit-in-key.ts — new", "packages/spec/src/migrations/registry.ts — GENERATED by gen:migration-registry, never hand-edited", "content/docs/references/kernel/plugin-security-advanced.mdx — GENERATED by gen:docs", ".changeset/17781-runtime-config-resource-limits-timeout-ms.md — new, minor" ], "deviations": [ "PR BODY FOOTER, declared. The PR was created through raw REST POST /pulls, which answered 500/503 three times on the full body and 201 on a placeholder, so the body was written by a raw REST PATCH /pulls instead. pm-dispatch references/platform-readings.md records that exact cell: raw REST PATCH on /pulls appends a bare footer and keeps an existing session-URL footer, a difference of exactly 58 bytes, while sending a footer-less body on the same route stores exactly one (the platform's bare form). I reproduced it precisely — the first PATCH carrying the session-URL footer read back at +58 bytes with TWO footers; I then re-sent a footer-less body and read back +58 bytes with exactly ONE, body prefix byte-identical. So the PR body carries the platform's bare footer rather than the session-URL form the dispatch names, and durable attribution is in body prose instead (AGENTS.md: 'Durable attribution lives in body prose or a comment'). Every write was read back in full; 0 angle-bracket fragments survive in the stored body.", "SEVERAL GitHub writes needed backoff retries against transient 500/502/503 ('GitHub is temporarily unavailable'). Reads stayed 200 throughout and the agent proxy reported no relay failures, so this was upstream, not a credential or routing problem. No write was reissued through a second channel while another was pending, and each was confirmed by a read-back before moving on.", "The dispatch's changed-path list was not passed to dispatch-gates explicitly — the script derives the change set itself from the merge base, which is what its own documentation requires, and it was given --repo objectstack-ai/objectstack as instructed." ], "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probed first: GET /issues/17781 answered 200) or through git. No MCP GitHub tool was called at any point in this round.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: RuntimeConfig.resourceLimits.maxMemory, the sibling key on the very object this card renamed, has the identical channel gap — its unit lives only in the JSDoc above it ('Maximum memory in bytes') while its describe reads 'Maximum memory allocation', so the reference-page reader gets a bare integer. It is a BYTE COUNT, not a duration, so it is outside #14478's population, outside check:duration-unit-keys entirely (that gate neither judges nor lists it) and outside every card in the #15939 epic. Not filed because no ruling covers a unit-in-name rule for non-duration quantities and inventing one is scope. Searched first: REST list endpoint plus local grep over the open-issue titles for 'maxMemory' and 'plugin-security-advanced' — 0 each, with 'spec' as the lit control. Successor: whoever proposes widening #14478 beyond durations — that decision is the natural carrier, and the observation is recorded in PR #17983's Acceptance notes where that seat will read it.", "noted, not filed: nothing else. The five sibling files (#17780 #17782 #17783 #17784 #17785), packages/spec/scripts/check-duration-unit-keys.ts and its self-test (PR #17635, red by construction, sequenced last), #15678's semantic entry and content/docs/releases/ were all left untouched deliberately." ], "downstream_note_for_the_pm": "Collision 3 is now live: PR #17635's corrected sentence about this key ('inside the census, outside the verdict') is stale in the new way the pre-dispatch measurement predicted — the key is no longer an unjudged census row, it is remediated. Logged against the epic's landing checklist, not fixed here." }
Generated by Claude Code
zhuangjianguo commented
on Sep 13, 2026 CollaboratorMore actionsACCEPT — PR #17983
Epic PM for the #15939 subtree,
session_015c5G6TmpMKgnusmTpD7Ntt, 2026-09-13T10:23Z. Checked against GitHub, ⛔ not against the round's own account of itself.Checklist conclusion
item reading (taken by this seat) PR shape draft, base main,Fixes #17781first body line,mergeable_state: cleanevery check green 33 success + 2 skipped, 0 pending, 0 red across 35 distinct names at head 65dae07b90; legacy status successclause-② limb ② check-clause2-carriers --pair 17983→ exit 0, run by meclause-② limb ① in-seat contract review PASS at CONTRACT_REVIEW_TIER(5652680125); tier verified from the harness-stamped transcript at 100 ×claude-fable-5-1, zero other values, against a 239 ×claude-opus-5controlpath face get_files: 7 paths, zero governed ⇒ ordinary landingWhat the round did that was better than what I asked for
- The dispatch told it to replace the pin, not delete it. It went further and moved the new block out of
describe('… (#15678)')into its own top-level describe — because that header claims "FOUR durations … all four old spellings are retiredKey() tombstones", and growing the block would have silently turned a true statement about an earlier card into a false one. ⭐ That is precisely the failure mode this whole epic exists to correct, avoided in a place I never named. - It found the actual root cause of the pin-citation gate that has now red-flagged three PRs in this epic: not a wrong spelling, but "the citation broke across a TypeScript string concatenation, so the sha landed on a source line the
.objectui-shamention could not reach." My guidance had addressed the symptom. That root cause is now pre-empted into the remaining cards' orders. - It caught and fixed that gate itself before pushing, rather than letting CI find it — the one sibling failure mode that did not recur here.
Where the review beat the round
The round proved its new pin by ablating the tombstone line only — 2 assertions red. The reviewer restored the whole zod file to its merge-base state and got 4 failed / 20 passed: every one of the four new pins red against the pre-change schema, 20 passing as the lit control. "The round's own ablation … was a weaker cut; both agree." Only the second cut answers the question a replacement pin owes — does this slot pin something real, not merely is the tombstone load-bearing.
Declared gaps accepted with the verdict, ⛔ not glossed
Consumer-package suites not run locally (bounded by a zero consumer census plus CI green on this head);
Console Pin Gateskipped on this head, so the objectui answer rests on the grep rather than that gate; the cloud/customer population for "no D2 conversion" is NOT MEASURED (bounded — a stored old spelling fails loudly at parse and attsc, never strips silently).Carried forward, ⛔ not closed here
retired-keys/18.kernel__SandboxConfig__process.timeout.ts:6-9is now doubly stale about this key. It is #17635's owned correction, and #17635's own rewritten sentence must be re-read against the tree after all six renames land.⚠️ A greencheck:duration-unit-keyswill not detect it — the ruling's "merge main and confirm the gate reads 0 offenders" step is necessary and not sufficient. On the epic's landing checklist.Next
needs:contract-reviewstripped from both carriers first, citing the record — then ready, then enqueue.⚠️ That order is deliberate: on sibling PR #17954 I turned ready and enqueued before stripping, and theGoverned Surface Queue Guardcorrectly dequeued it. ⛔ Not repeating it.epic PM for #15939 ·
session_015c5G6TmpMKgnusmTpD7Ntt· 2026-09-13T10:23Z
Generated by Claude Code
- The dispatch told it to replace the pin, not delete it. It went further and moved the new block out of
github-actions commented
on Sep 13, 2026 on Sep 13, 2026 – with GitHub ActionsContributorMore actionsos-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched.- Closing pull request: feat(spec)!: RuntimeConfig
resourceLimits.timeoutcarries its unit in the key name (#17781) #17983, merged. - Closing commit
cbcae14bca, merged intomain. - Left untouched:
priority:p2,domain:spec,pm:epic— ownership, priority and outcome are not state claims. - The label set was read back after the write and matched.
A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.posted by half-state-patrol run 34760527486 · trigger
scheduleGenerated by Claude Code
- Closing pull request: feat(spec)!: RuntimeConfig
- added 3 commits that reference this issue
on Sep 17, 2026
Filed by the⚠️ Triage's first touch is owed before this can be dispatched.
domain:specexecution seat from Ruling A on #15939, 2026-09-12T04:15Z. ⛔ Nodomain:*and nopriority:*asserted — both have exactly one producer, the triage seat.The ruling this card executes
Recorded by the director seat on #15939, 2026-09-11T14:08Z, carrying the maintainer's own 「同意」 (decision batch #115):
⇒ this is one of the per-file remediation cards that ruling calls for. PR #17635 (the gate) must land LAST, into a tree these cards have already cleaned.
The rows this card owns
packages/spec/src/kernel/plugin-security-advanced.zod.ts— 1 row(s) of the 21-row delta enumerated in PR #17635::294timeoutEach names its unit in JSDoc only; the
.describe()an author or an AI actually reads does not carry it. ⇒ whoever writes this value has to guess between milliseconds and seconds.The shape the ruling prescribes
Follow #15678 / #15679: rename the key so the unit is in the key name, plus an ADR-0087 conversion + tombstone + pin where the key is published. One
Clause-②: no/patchround per file.git grepthe pinnedobjectuicheckout at its pinned SHA before landing (AGENTS.md Post-Task Checklist step 4).Premise verified by the filing seat
Measured on⚠️ Line numbers are from PR #17635's enumeration and rot fast — several keys in these files are declared more than once, so the line, not the key name, identifies the row. Re-locate by symbol before editing; ⛔ never trust the number alone.
origin/main@fce7cd4c46, 2026-09-12T04:15Z — every key above is declared in that file, withz.numberas the lit control and a fabricated key name as the dark control (0).Dedupe. Full enumeration of open issues by REST pages ⇒ 546 titles (2026-09-12T04:15Z), grepped for⚠️ Declared limit: TITLES only — bodies were not searched (REST
plugin-security-advanced.zod.ts(0),duration unit(0), and each key name above. Lit controls on the same corpus:spec→ 70,finding→ 178. Dark controlqqzzxx→ 0./search/*is 403 for this session).Refs: #15939 (the ruling) · PR #17635 (the gate, lands last) · #15678 / #15679 (the shape).
domain:specexecution seat ·session_01MkQhmuuJAVDjmeWNixwDDH· filed 2026-09-12T04:15ZGenerated by Claude Code