Skip to content

docs(spec): two docblocks that promised a future which has already arrived (#16835, #15239) - #16947

Merged
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-16835-expired-until-it-lands-docblocks
Sep 8, 2026
Merged

docs(spec): two docblocks that promised a future which has already arrived (#16835, #15239)#16947
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-16835-expired-until-it-lands-docblocks

Conversation

@claude

@claude claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #16835
Fixes #15239

Clause-②: no
Comment text only. No accept set moves, no export is added or renamed, no key lands on a
published payload, no predicate changes. Both cards are one defect shape — a docblock
sentence promising a future that has already arrived — and both corrections rewrite the
sentence to what the tree does. 拉回已声明契约 ⇒ 常规档.
The dispatch's fence held: neither correction needed a behaviour change, so nothing here
required an at-tier contract review.

Two expired temporal promises, both of the form "until it lands, …" where the thing has
landed. Each card's own acceptance criterion is answered separately below, and one of the
three sentences the cards counted turned out to be still true and is deliberately
untouched.


Changeset — MEASURED, not assumed

The default guess for a comment-only diff is skip-changeset. It is wrong here, and this
was measured rather than reasoned:

$ python3 -c "json.load(open('packages/spec/package.json'))['files']"
["dist","json-schema","liveness","prompts","llms.txt","README.md",
 "src/**/*.zod.ts","CHANGELOG.md","api-surface","spec-changes.json"]

src/**/*.zod.ts is in files[], so a TSDoc comment in either edited file is a published
byte
. Confirmed against a real pack rather than against the glob:

$ cd packages/spec && npm pack --dry-run --json     → 2073 files
  SHIPPED | src/automation/control-flow.zod.ts      ← edited here
  SHIPPED | src/security/sharing.zod.ts             ← edited here
  --- positive control (the listing must discriminate) ---
  ABSENT  | src/automation/control-flow.test.ts
  ABSENT  | src/security/sharing.test.ts
  SHIPPED | package.json

Both sibling .test.ts files are absent, so the listing is discriminating and not merely
answering "yes" to everything. ⇒ a real changeset is owed:
.changeset/spec-expired-until-it-lands-docblocks.md, @objectstack/spec: patch.

The regenerated content/docs/references/automation/control-flow.mdx is not in the
package (it is the docs site), so it is not what makes the changeset owed — the two source
files are.


#16835 — the premise, re-derived rather than believed

The card asserts that #16134's landing falsifies two sentences. The claim comment explicitly
reserved that reading for me and did not verify it. It holds. Landed as 21aabbc7b
(PR #16825, merged 14:55Z), which added to FlowSchema's superRefine:

// packages/spec/src/automation/flow.zod.ts
const firstNodeLocationById = new Map();
for (const graph of collectFlowGraphs(flow)) {  ctx.addIssue({ code: 'custom',}) }

Sentence 1"The two do not overlap and cannot fight: the schema rejects undeclared
KEYS, the analysis rejects malformed STRUCTURE."
False. The schema now refuses a
duplicate node id, which is a structural fact, not a key fact. The clean division the
sentence draws is precisely what the ruling removed. They also now do overlap: within the
depth ceiling both the schema and analyzeRegion refuse the same collision.

Sentence 2"They do now meet at one seam." False. Two seams: the #4001
region-slot safeParse, and the #16134 node-id-collision refusal.

The replacement prose names both seams and the boundary between them, and it was checked
against the two in-tree comments that came through that PR's own reviews — analyzeRegion's
header and FlowSchema's superRefine header — rather than paraphrased from the card:

  • FlowSchema holds one node-id space across the top-level nodes[] and every region body,
    judged at every depth collectFlowGraphs walks;
  • that walk stops at MAX_REGION_DEPTH (32), so past the ceiling a region is left raw and
    analyzeRegion's own duplicate node id line is the only refusal of a within-region
    duplicate;
  • a cross-region collision beyond the ceiling is not judged at all — the card's
    suggested wording ("analyzeRegion remains the refusal past that ceiling") is looser than
    the tree, and the docblock now says the precise thing.

Positive control on that file while locating the sentences: validateControlFlow → 10 hits,
so the grep fires.

content/docs/references/automation/control-flow.mdx carries this module docblock verbatim
and was regenerated with gen:docs — ⛔ never hand-edited.

#15239 — two sentences, not three

Triage's executable unlock predicate reads TRUE, re-taken on this tree:

$ git grep -c "case 'field'" -- packages/plugins/plugin-sharing/src/bootstrap-declared-sharing-rules.ts
  1                                          ⇒ #15072's executor HAS landed (9fa577535)
$ git grep -c "mapRecipientType" -- …/bootstrap-declared-sharing-rules.ts
  2                                          ← positive control, same file

:91 and :262 both said a field rule is skipped at seed "until [#15072] lands". Both
now name the artefact instead of a schedule: mapRecipientType maps field through, and
SharingRuleService.expandRecipientForRecord reads the named column on each matched record.

:110 — the third sentence is STILL TRUE and is left alone

"The runtime contract additionally reserves queue (no sys_queue yet) — it is
deliberately NOT authorable until the implementation lands."

That sentence is about queue / sys_queue, not about field / #15072, and its truth
turns on a question neither card asked. Measured:

$ find packages/platform-objects/src -name "*queue*"
  packages/platform-objects/src/audit/sys-job-queue.object.ts     ← sys_job_queue, a different object
  (no sys-queue.object.ts)
$ find packages/platform-objects/src -name "*team*"               ← POSITIVE CONTROL
  packages/platform-objects/src/identity/sys-team.object.ts
  packages/platform-objects/src/identity/sys-team-member.object.ts

The control finds the definition file for a sys_* object that does exist, so the instrument
fires; there is no sys_queue definition. Every one of the 10 in-tree sys_queue mentions is
prose saying "no sys_queue yet", including three live ones —
spec/src/contracts/sharing-service.ts ("reserved: no sys_queue yet, expandRecipient
returns []"
), plugin-sharing/src/bu-tree-recompute.ts ("returns [] (no sys_queue
yet)"
) and packages/lint/src/validate-org-axis-red-lines.ts. ShareRecipientType's
z.enum does not carry queue.

:110 is still true. Untouched. This PR corrects two sentences, not the three the
card's title counted. Editing a third line because a title said "three" would have replaced a
true sentence with a false one.

⛔ No correction reintroduces the defect

Both cards exist because a sentence promised a future. Neither replacement sentence contains
another one — verified mechanically over both files:

$ grep -n "until it lands\|until that lands\|when it lands\|once it lands" \
      packages/spec/src/automation/control-flow.zod.ts packages/spec/src/security/sharing.zod.ts
  (no matches — exit 1)

The one surviving "until … lands" in the tree is :110, which is a true statement about a
future that has not arrived.


Gates

Derived by name and reconciled, never hand-listed:

$ node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands   → 93 commands
$ node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt
  ✓ 93 derived famil(ies) accounted for — 93 run, 0 NOT-MEASURED, 0 UNRUN

93 / 93 run, every one exit 0. Exit codes were captured before any pipe.

Five first returned exit 3 / PREREQUISITE NOT MET — an unbuilt workspace package, which
those gates report as "Nothing was measured … NOT a finding" rather than as a red. They were
re-run after building the prerequisites and all are green:

gate first after building the prerequisite
@objectstack/lint check:doc-formula-expressions 3 0
@objectstack/lint check:doc-security-posture 3 0
@objectstack/spec check:skill-examples 1 0
check:docs-transcript-drift 3 0
check:dual-build-cjs-loads 3 0

check:skill-examples is the materially relevant one — it type-checks the spec source
TSDoc
surface this PR edits, and reports 258 prose examples type-check across 3 surface(s).

Spec artifacts, regenerated up front per AGENTS.md:

$ pnpm --filter @objectstack/spec build                → 0
$ pnpm --filter @objectstack/spec check:generated      → 1 of 15 stale: content/docs/references/**
$ pnpm --filter @objectstack/spec check:generated --fix → regenerated exactly that one (gen:docs)
$ pnpm --filter @objectstack/spec check:generated      → ✓ All 15 generated artifacts are up to date

check:authorable-surface is green across that run, which independently confirms the diff
moved no authorable key — the reading a prose-only claim needs.

Control-byte self-scan beyond check:nul-bytes, on both edited sources:

$ grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' control-flow.zod.ts sharing.zod.ts   → no matches (exit 1)

Not owed here: no skills/** path, so no line budget; no governed surface
(docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md) is touched, so no
maintainer brief section.

验收备注

  • noted, not filed: :110 is itself a temporal promise, true today and load-bearing when
    sys_queue eventually lands — nothing goes red at that moment. It is not a defect now (the
    sentence is true), so it is an observation rather than a card. Carrier: whichever PR
    introduces sys_queue, which will be editing this enum anyway.

Docs-drift advisory came back NOT COVERED — answered by hand

The bot could derive no anchor for the two changed .zod.ts files, so it did not clear the
pages documenting them. Read as "the instrument was blind here", never as "no page is
affected"
. All readings below are on PR head c5ee37f6c with git status --porcelain empty
(the advisory computed against a merge into base 34a76c480, so a different list there is a
different tree, not a wrong row).

1. Generated pages — the generator was run, and it moved exactly one file

$ pnpm --filter @objectstack/spec build            → 0
$ pnpm --filter @objectstack/spec check:generated  → exit 1, "1 of 15 artifact(s) stale:
                                                      content/docs/references/**"
$ pnpm --filter @objectstack/spec check:generated --fix   → regenerated that one (gen:docs)
$ git status --short
  M content/docs/references/automation/control-flow.mdx     ← in this diff
$ pnpm --filter @objectstack/spec check:generated  → exit 0, "All 15 generated artifacts are up to date."

control-flow.zod.ts does feed a generated page, exactly as #16835's title says, and that
page is in the diff. ⇒ sharing.zod.ts does not. That is gate-backed rather than asserted:
check:docs is green with the sharing edits already applied, so no generated page carries
those blocks — only the @module docblock is rendered, and sharing.zod.ts's edited text sits
in the ShareRecipientType and SharingRuleSchema blocks. This corrects #15239's own flag that
gen:docs "may want" a re-run for it: measured, it does not.

2. Hand-written pages that restate the same claims — scanned, and one is a real hit

Searched content/docs/**, docs/**, skills/** and every *.md / *.mdx for paraphrases,
not just verbatim strings. Every zero below sits next to a control on the same corpus.

Claim A (cannot fight / one seam / the keys-vs-structure division):

probe files verdict
cannot fight only this PR's own changeset
rejects undeclared KEYS references/automation/control-flow.mdx + spec/CHANGELOG.md generated (in diff) / release history
malformed STRUCTURE references/automation/control-flow.mdx generated (in diff)
sibling guard references/automation/control-flow.mdx, docs/audits/2026-07-unknown-key-strictness-ledger.md, 4 × CHANGELOG ⚠️ one real hit
one seam 30 files control — the instrument fires broadly, so the zeros above are readings

⚠️ docs/audits/2026-07-unknown-key-strictness-ledger.md, the control-flow.zod.ts row,
restates BOTH claims as a paraphrase
"the two do not fight" and "They meet at exactly one
seam"
— and both are now false for exactly #16835's reason. Filed as #16948, deliberately
not fixed here: this PR's governing claim fences the file surface with "⛔ Nothing else",
and docs/audits/** carries a merge=os-regen driver plus a generated .counts.md companion
gated by check:strictness-ledger — a verification surface this fold does not carry, which is
the limb the bounded in-place-fix exemption requires.

Claim B (field rule skipped until #15072):

page says verdict
content/docs/permissions/permissions-matrix.mdx:155 "every recipient type … including the field recipient (#14103), expanded once per matched record by its executor (#15072)" ✅ already correct — left alone
content/docs/permissions/sharing-rules.mdx:146 "Expanded once per matched record by the sharing-rule executor (objectstack#15072)" ✅ already correct — left alone
content/docs/protocol/objectql/security.mdx:386, :422 "The executor (#15072) derives the rule's grants from each matched record's column" ✅ already correct — left alone
content/docs/permissions/capabilities.mdx (skipped loudly) a package-capability row collision, unrelated subject ✅ not this claim — left alone

⇒ every hand-written permissions page already describes the post-#15072 state. Nothing to fix
for claim B, and each page left alone is named above with its reason.

content/docs/releases/** was not touched and is read-only regardless: releases/v17.mdx
matched only the broad one seam / until it lands controls, not either claim.

3. Changeset, measured in BOTH halves

Half 1 — path vs files[]: the pack listing above. Half 2 — is the changed TEXT in the
published dist?
Yes, and dist is itself in files[]:

$ grep -rl "They meet at two seams"        packages/spec/dist/   → 2  (automation/index.d.ts, .d.mts)
$ grep -rl "executor half landed as #15072" packages/spec/dist/  → 2  (capabilities-*.d.ts, .d.mts)
--- controls, same corpus ---
$ grep -rl "cannot fight"      packages/spec/dist/ → 0   (dist is rebuilt, not stale)
$ grep -rl "until that lands a" packages/spec/dist/ → 0   (likewise)
$ grep -rl "ShareRecipientType" packages/spec/dist/ → 24  (instrument fires)
$ grep -rl "validateControlFlow" packages/spec/dist/ → 4   (instrument fires)

⇒ the edited TSDoc is published twice over — as src/**/*.zod.ts and as dist/*.d.ts
declarations. skip-changeset is wrong on both halves.


Generated by Claude Code

…rived (#16835, #15239)

`automation/control-flow.zod.ts` said the schema and `validateControlFlow`
"do not overlap and cannot fight" and met at "one seam". #16134 removed that
division deliberately: `FlowSchema`'s `superRefine` now refuses a duplicate
node id — a structural fact — over one node-id space spanning the top-level
`nodes[]` and every region body. The docblock now names both seams and the
boundary: the #4001 region-slot `safeParse`, and the #16134 node-id space
judged at every depth `collectFlowGraphs` walks, which stops at
`MAX_REGION_DEPTH` (32) — past the ceiling a region is left raw and
`analyzeRegion`'s own `duplicate node id` line is the only refusal of a
within-region duplicate, a cross-region collision beyond it not judged at all.

`security/sharing.zod.ts` said in two places that a `field` rule is skipped at
seed "until [#15072] lands". It landed (`9fa577535`): `mapRecipientType` maps
`field` through and `SharingRuleService.expandRecipientForRecord` reads the
named column per matched record. Both sentences now name that executor rather
than a schedule.

The third sentence the card counted is left alone and is still true: `queue`
is "NOT authorable until the implementation lands" is about `sys_queue`, not
about `field`, and no `sys_queue` object exists in the tree.

Comment text only — no schema, export, key or predicate moves.
`content/docs/references/automation/control-flow.mdx` regenerated by
`gen:docs`; `sharing.zod.ts`'s blocks do not reach the generated tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
@github-actions github-actions Bot added the size/s label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 2 changed file(s) yielded no anchor (packages/spec/src/automation/control-flow.zod.ts, packages/spec/src/security/sharing.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/src/automation/control-flow.zod.ts, packages/spec/src/security/sharing.zod.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 131 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 34a76c4800d46f2aec1c136b65ff94e4087435c8packageMentionDocs.

@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — domain:spec execution seat, 2026-09-08T18:2xZ

A two-card fold: #16835 + #15239. Reviewed against head c5ee37f6c, base 2aaed9922 (merge base — ⛔ not a two-dot diff).

⭐ The premise this seat deliberately did NOT verify, and the dev established it

The claim comment reserved this explicitly: "You must still re-derive whether #16134's landing actually falsifies them — the card asserts it, this seat has ⛔ not verified that half." The dev established it rather than inheriting it: #16134 landed as 21aabbc7b (PR #16825), and FlowSchema.superRefine now refuses a duplicate node id over collectFlowGraphs — i.e. the schema decides a structural fact. ⇒ there are now two seams, and both sentences were false.

Had that come out the other way, the correct outcome was to stop; it did not, and the reading is on the record.

Both members, verified on both trees by this seat

Every zero carries a live control, and the controls are unchanged across the trees:

card grep origin/main PR head control (main → head)
#16835 cannot fight 1 0 validateControlFlow 10 → 10
#16835 one seam 1 0 (same)
#15239 until it lands, the 1 0
#15239 until that lands a 1 0

⭐⭐ The :110 call — the sharpest thing in this dispatch, and it went the right way

The card's title counts three sentences. The dispatch required :110 to be judged on its own terms because it is about queue / sys_queue, not field / #15072. The dev's verdict: STILL TRUE, LEFT ALONE. Independently confirmed by this seat:

reading value
no \sys_queue` yetinsharing.zod.ts`, main → head 1 → 1 (untouched)
sys-queue object files in the tree 0
positive control — sys-team object files 2

sys_queue does not exist, the sentence is true, and this PR corrects two sentences rather than the three the card counted — said so in the PR rather than quietly editing a third line. ⛔ That is the difference between fixing a card and obeying its title.

⭐⭐ The docs-drift advisory was blind, the hand-scan was not, and it found a real hit

The bot reported NOT COVERED — both changed files yielded no anchor. ⛔ Read as "the instrument was blind", not "no page is affected". The hand-scan then found what it could not:

docs/audits/2026-07-unknown-key-strictness-ledger.md:738
  … **`validateControlFlow` is a sibling guard, not a key gate, and the two do not fight** …
  They meet at exactly one seam — …
                                        control: `control-flow` in that file → 4

Both retired sentences, paraphrased, in a file no anchor-based check would ever list. Verified by this seat at that line.

Filed as #16948 rather than fixed, and the reasoning is right: the claim fences the file surface to "Nothing else", and docs/audits/** carries a merge=os-regen driver plus a gated .counts.md companion — a new verification surface, which fails the in-place-fix exemption's own limb. The four hand-written permissions pages were checked too and already describe the post-#15072 state correctly; each is named in the PR with its reason for being left alone.

The generated page moved, and that answers the open question

content/docs/references/automation/control-flow.mdx is in the diff (+28/−?) — the generator was run and the page belongs there, the same shape as sibling PR #16930. ⚠️ And a correction to #15239's own text: it flagged that gen:docs "may want" a re-run for sharing.zod.ts; measured, it does not — only the @module docblock renders, and sharing's edited text sits in the ShareRecipientType / SharingRuleSchema blocks. Only control-flow.mdx regenerated.

Other readings this seat took

reading value instrument
diff shape 4 files, +60 / −21 git diff --stat
clause-② pairs exit 0 on both cards, no widening tell check-clause2-carriers.mjs --pair 16947
governed surface NOT governed check-governed-merges.mjs --test
model tier 265 harness-stamped "model":"claude-opus-5", no other value subagent transcript grep

⭐ Gates: the best-measured run of this shift

93 derived, 93 run, ALL exit 0, 0 NOT-MEASURED, 0 UNRUN. Five gates first returned PREREQUISITE NOT MET on an unbuilt workspace — read as NOT MEASURED, ⛔ never as red — and were then made green by building what they read, including whole-tree check:dual-build-cjs-loads, which every other delivery this shift declared as not-measured and handed to CI. ⛔ No narrowing was declared and none was needed. The lock wait (125s queued behind another agent, then held 100s) was spent on lock-free work rather than by ending the round.

Landing

Not enqueued yet — 31 names seen, 10 still running, 0 non-green. Enqueueing when every name closes completed with success/skipped, aggregated by name.

⚠️ One thing carried forward for whoever next touches sharing.zod.ts: the dev notes that :110 is itself a temporal promise — true today, and nothing goes red when sys_queue lands. ⛔ Not a defect under any filing class today, so correctly not filed; the carrier is whichever PR introduces sys_queue.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

1 participant