Skip to content

docs(runtime,rest): name both causes of next: null in the state-introspection comments - #11591

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-11276-next-null-comment-parity
Aug 24, 2026
Merged

docs(runtime,rest): name both causes of next: null in the state-introspection comments#11591
os-zhuang merged 1 commit into
mainfrom
claude/issue-11276-next-null-comment-parity

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#11276

Both state-introspection call sites compute:

constnext=from===undefined ? null : legalNextStates(schema,field,from);

so next: null has two causes — no state_machine rule governs the field, and the caller omitted ?from=. The comment immediately above each line named only the first.

legalNextStates (packages/objectql/src/validation/rule-validator.ts:2212) confirms the split: it returns null when no state_machine rule matches the field and transitions[currentState] ?? [] otherwise — so the from-absent case never reaches it at all, the ternary folds it onto the same null first.

Anchors re-derived — the card's line numbers were stale

Both files were rewritten hours before this branch was cut, so the sites were found by the shape of the expression, not by line number. At the commit this branched from (67ceb9ae):

sitecard citedactually at 67ceb9ae
packages/runtime/src/domains/meta.ts:233-234expression at :247, comment at :232-233
packages/rest/src/rest-server.ts:6274-6277expression at :6303, comment at :6299-6302

What changed

Comment text only, in the two declared files.

meta.ts — the comment sits above the route guard, ~14 lines above the expression, so it names the handler rather than "the line below". It now states both causes and adds the reader's remedy (re-ask with ?from=).

rest-server.ts — the sharper miss. It asserted a three-valued answer and justified the tri-state on the grounds that a UI must be able to tell the cases apart, directly above the line that folds a fourth input condition onto the same null. A maintainer consulting it to decide whether a UI can distinguish "no FSM" from "no from" got the wrong answer from the comment and the right one only from the expression beneath it. The three answer values are kept (they are correct); what is corrected is that null is overloaded across two input conditions, so a UI cannot read null as "no state machine" unless it passed a from.

The two comments are not harmonised into identical text — their surrounding context differs (a dispatcher route guard vs. an Express handler that has just degraded past a 501), and each is written to be true where it sits.

Both now match the semantics already asserted in docs/qa/platform-checklist/areas/api-backend.json ("?from omitted returns next:null (no from ⇒ no transition table), a field with no FSM returns next:null") and the prose #11049 landed in content/docs/protocol/objectql/state-machine.mdx:127.

No behaviour change — mechanically checked, not asserted

Every added and removed line in this diff is a // comment:

git diff -U0 | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | grep -vE '^[+-][[:space:]]*//'
→ no output

Nothing to pin. The behaviour is already correct and already asserted by the checklist entry above — I read it and confirmed it really does name both causes. Adding a test here would pin an unchanged expression to look thorough.

No changeset. Nothing published changes: comments are erased by the compiler and no consumer-observable surface moves. skip-changeset applied.

Verification — at final commit 73570151

Gate set derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (12 families), not a hand-built list. Union re-run after the final commit; each gate's own verdict line:

gateverdict line
check:nul-bytescheck-nul-bytes: OK (scanned 6483 text file(s) … no raw ASCII control bytes)
check:authz-resolver✓ check:authz-resolver: single shared authorization resolver intact; both entry points delegate.
check:cross-package-test-inputsOK: 16 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
check:dispatcher-error-vocabularycheck-dispatcher-error-vocabulary: OK — 21 unregistered code-stamping site(s), all classified
check:published-files✓ check:published-files — 69 publishable package(s) … declare a files whitelist
check:route-envelope✓ Route-envelope conformance — 10 route module(s) audited: 7 conformant, 2 ratcheted, 1 exempt
check:slot-lookup✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new
check:test-source-aliascheck-test-source-alias OK — 72 packages with tests scanned
check:type-source-resolutioncheck-type-source-resolution OK — 77 packages with a tsconfig.json scanned
check-ci-filter-parity.mjsOK: all 95 declared cross-package glob(s) (80 unique) are covered
check-cross-package-test-inputs.mjsOK: 16 package(s) read outside themselves, all declared
check-plugin-teardown-shape.mjs✓ check:plugin-teardown-shape: 63 Plugin implementation(s) across 4574 source(s)
check-affected-docs.mjs✓ affected-docs self-test: 381 cases pass.

No gate refused; all 13 were measured. check:route-envelope's two ⚠ ratchet #9559 lines are pre-existing entries for query-allowlist.ts / query-multiplicity.ts — files this PR does not touch — and the gate's headline verdict is green.

Typecheck, both declared packages, under the shared verify lock:

packages/rest typecheck$ tsc --noEmit
packages/rest typecheck: Done
packages/runtime typecheck$ tsc --noEmit
packages/runtime typecheck: Done
os-verify-lock: VERDICT command-exit 0 · held the lock 12s · waited 0s

Dependency closures built first (pnpm --filter '@objectstack/runtime^...' --filter '@objectstack/rest^...' buildVERDICT command-exit 0 · held the lock 253s), so tsc read rebuilt .d.ts rather than stale ones.

Declared narrowing: the two packages' pnpm test suites were not run. The diff is mechanically proven comment-only by the grep above, and TypeScript comments are erased at compile — no test can observe them. CI runs the full farm regardless.

Generated by Claude Code


Generated by Claude Code

…rospection comments
Both dispatchers compute
`const next = from === undefined ? null : legalNextStates(schema, field, from);`
so `next: null` has two causes -- no `state_machine` rule governs the field,
and the caller omitted `?from=`. The comment immediately above each line named
only the first.
The `rest-server.ts` one was the sharper miss: it asserted a three-valued
answer and justified the tri-state on the grounds that a UI must be able to
tell the cases apart, directly above the line that folds a fourth input
condition onto the same `null`.
Both comments now match the semantics already asserted in
`docs/qa/platform-checklist/areas/api-backend.json` and the prose in
`content/docs/protocol/objectql/state-machine.mdx`. Comment text only -- every
changed line is a `//` comment; no expression, return shape or observable
behaviour moves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. ✅

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/rest/src/rest-server.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 28 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 67ceb9aeff873b135e7b87954dd14cdcaf853116packageMentionDocs.

Which tree this was computed on

This run read content/docs from 02c2dc48535233bdaeeff1e7544c8fec725c33e0 — the merge of head 7357015143e0f88d9e703811caf6295167f43b85 into base 67ceb9aeff873b135e7b87954dd14cdcaf853116, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 02c2dc48535233bdaeeff1e7544c8fec725c33e0 && git checkout 02c2dc48535233bdaeeff1e7544c8fec725c33e0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 67ceb9aeff873b135e7b87954dd14cdcaf853116 7357015143e0f88d9e703811caf6295167f43b85 && git checkout -B drift-repro 67ceb9aeff873b135e7b87954dd14cdcaf853116 && git merge --no-ff 7357015143e0f88d9e703811caf6295167f43b85
node scripts/docs-audit/affected-docs.mjs --json 67ceb9aeff873b135e7b87954dd14cdcaf853116

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 24, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 24, 2026 06:19
@os-zhuang
os-zhuang added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit 2dc0a77Aug 24, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-11276-next-null-comment-parity branch August 24, 2026 06:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Both state-introspection dispatchers carry a code comment that names only one cause of next: null — directly above the line that creates the second

2 participants

@os-zhuang@claude