Skip to content

Fifth Rule 3 population (function declarations) widened, censused ids stripped, and the gate states its own scan boundary; hot-reload refusal ids stripped - #13298

Draft
os-project-manager wants to merge 4 commits into
mainfrom
claude/issue-13156-fifth-population
Draft

Fifth Rule 3 population (function declarations) widened, censused ids stripped, and the gate states its own scan boundary; hot-reload refusal ids stripped#13298
os-project-manager wants to merge 4 commits into
mainfrom
claude/issue-13156-fifth-population

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #13156
Fixes #13179

Family PR executing the twin Class-1 adjudications of 2026-08-29 (director seat, 13:50 + the operative 14:55 pair): A + C on the chain head, strip + C, root extension deferred on the member. One branch, per-member commits, independently verifiable halves. Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N

Chain head — the fifth population (commits d7e22a3d, 4dd3e5e3, afb72654)

A — the widening. Plain function DECLARATIONS join Rule 3's sink pass in scripts/check-doc-authoring.mjs, under the exact #13002/#13151 narrow clause — a declaration is transparent only when the function itself sits in a recognised customer-facing position, which for a declaration means its NAME is consumed from one (seeded or closed over by collectTextSinkConsts, the same fixed point the hoisted const-arrow spelling rides). Never an unconditional crawl: an unconsumed declaration and one consumed only by an unrecognised call stay unswept, pinned as precision negatives in --self-test.

Bucket choice + rationale (required by the adjudication): the new population gets its own functionDeclared bucket with its own blindness floor, not a fold into functionBuilt. Reasoning is #13151's per-bucket-floor principle applied one step further: the floor exists to catch exactly one clause rotting back to undefined, and the arrow/expression members (217 recognised strings on the real tree) would hold a shared floor up indefinitely while the declaration clause rotted — the precise silence this population was found by. The pre-existing options-factory clause (buildsStrictObjectOptions) is form-agnostic, already reached function declarations before this PR, and keeps functionBuilt; only the name-consumption clause is new. On the real tree the new floor sits at 133 recognised strings.

Reproduce-first, measured: widen first, then strip. At d7e22a3d the widened gate is RED on the real tree: 4 violations — 2 functionDeclared (listPositionFieldReferenceMessage in data/filter.zod.ts:296, normalizedMemberMessage at :1434) and 2 message via the new closure into declarations (SUBMIT_REDIRECT_RULING, FORM_VIEW_FEATURES_RULING in ui/view.zod.ts). After the strip commit the gate is GREEN with every bucket floored (message 1037 · strictObject 3349 · tombstone 771 · describe 8524 · functionBuilt 223 · functionDeclared 133).

Self-test teeth proven by two-leg ablation (mutate, confirm on disk by anchored grep counts, run, restore, prove restoration by HEAD-blob hash match; no build leg exists on this path — the gate runs from source): neutralising the recogniser entry branch fails the declaration REDs and the floor cases while the closure-pin case stays green; neutralising the declaration registration fails all three. Restoration verified: worktree blob equals HEAD blob.

Census re-derivation — a finding, not a blocker. The relayed 47 literals / 51 ids / 14 files (measured on the #13002 branch at c0d50d612) does NOT reproduce on current origin/main: the broad unconditional-crawl census reads 15 literals / 15 ids / 6 files (main moved in the interim — data/field.zod.ts now contributes zero), and the adjudicated narrow instrument reds on 4 of those plus the 2 hoisted consts above. The strip covers the full broad-census population anyway — all 15 in-declaration ids plus the 2 const-borne ids, 17 total across 8 files — because every site is same-audience refusal/warn prose and the ruled remedy is by population, not by count. After the strip the broad census reads 0/0/0.

Strip discipline (#12522 keep-the-ADR-id charter): ADR ids, protocol versions, error codes (INVALID_FILTER / 400) and ruling dates stay; an id that was the whole parenthetical takes the parenthetical with it; genuinely internal load-bearing references moved to adjacent code comments (filter.zod.ts, view.zod.ts). No tombstone had the issue id as its only reference, so nothing needed escalation.

Twins, red-then-green, never weakened: 42 assertions across 7 spec test files red against the stripped tree, 607/607 green after re-pinning — each re-pin a customer-resolvable anchor from the NEW text (ruling dates, ADR anchors, prescription sentences), plus negative id pins at the two ruling-const doors. Two predicate-style pins (.some(w => w.includes(...)) in compose-stacks-key-loss.test.ts) were caught by RUNNING the full mention set after the parse sweep's expect-callee filter missed them — re-pinned the same way (35/35 green). The objectql and service-analytics assertions the id sweep flagged pin messages produced OUTSIDE the scanned root (objectql/src/registry.ts, service-analytics/src/comparand-shape.ts) and are deliberately untouched — they are evidence for the member card's revival census, below.

C — the boundary output (lands once, serves both cards): the gate now prints its own Rule 3 scan boundary on every verdict — the root (packages/spec/src), the explicit sibling-packages-not-scanned statement with the deferral pointer, and the recognised sink-shape list — derived from the same constants the scan reads, pinned derived-vs-derived in --self-test (root from SPEC_SOURCE_ROOT, buckets from the seen map the floor iterates).

Member #13179 — the hot-reload strip (commit 1c749681)

The ruled strip: the plugin-registration refusal's tracker id — the id the #13151-verified twins at hot-reload.test.ts:259,302,310 pin — removed from both registration-door strings (RETIRED_STATE_STRATEGY_GUIDANCE, the distributedConfig entry). Declared bounded same-class extension: the sibling id in the same refusal table's watchPatterns entry and in the startWatching() removal notice is stripped in the same stroke. All four bounded-fix conditions hold: same defect class (same table, same door, same audience), mechanical remedy pinned by the same-day adjudicated charter, same file already on this claim's declared surface, same twin suite with no new verification face. ADR-0049 enforce-or-remove, the spec/core versions and the scheduleReload migration call remain as the customer-resolvable anchors — mirroring the spec-side parse door, which was already id-free with a negative pin.

Twins red-then-green: 8 assertions red (10/18 passing), 18/18 green after; every id pin re-pinned to a content anchor plus negative id pins at all three doors. Repo-wide section-7 sweep for the member's id set: the only remaining test literal is an expect failure-label, not a content pin.

Root extension is NOT in this PR — deferred by the adjudication with a codified revival condition, and not smuggled in via the C output: the boundary line states the limit instead of moving it. The revival condition, however, now measurably FIRES: the diff-derived twin sweep plus a targeted source sweep found same-audience id-bearing refusal/warn/lint prose outside packages/spec/src well past the adjudicated threshold (about eleven candidate sites, e.g. objectql/src/registry.ts:1036, objectql/src/having-filter.ts:186, service-analytics/src/comparand-shape.ts:598, lint/src/validate-react-page-props.ts:434, metadata-protocol/src/protocol.ts:19879). Filed for triage as the instrument card the adjudication prescribed — #13297 (which remains open; it is out of scope here). #13179 is not affected: its own strip stands regardless of how triage grades the wider census.

Verification (all at afb72654, the pushed head)

  • Gate union derived by node scripts/pm/dispatch-gates.mjs (no paths; provenance line names this repo at afb72654): 51/53 green, plus check:type-check-debt green after the full 70-package build (30 ledger entries re-measured, none above recorded) and check:nul-bytes green. The two non-green results are the gates' own designed NOT MEASURED refusals, not reds: check-test-completeness.mjs (exit 3 — wants a saved turbo run test log; instructs recording NOT MEASURED locally) and pm/check-half-states.mjs (exit 3 — the container's GitHub token is the proxy placeholder; no reading). CI measures both.
  • check:doc-authoring green including --self-test (the edited gate's own suite; no external test file names it — verified by repo grep).
  • Tests: spec twin files 607/607; compose files 35/35; core hot-reload.test.ts 18/18; objectql targeted 208/208 (including the untouched out-of-root registry twin); lint targeted 76/76; service-analytics targeted 126/126. @objectstack/spec typecheck green (check:test-typecheck: OK, test layer compiles; debt ledger unchanged).
  • Clause-2 posture: content limb no (prose text edits + instrument widening; zero accept/reject change — the widened gate's only behavioural face is its own verdict). PATH limb fires on the spec/src strips, so this PR parks at DRAFT with needs:contract-review; the review chain owns enqueue.

Generated by Claude Code


Generated by Claude Code

claude added 4 commits August 29, 2026 17:43
…le 3's sink pass, and the gate states its own scan boundary

The fifth widening (#13156's A half, adjudicated 2026-08-29): a plain
`function` DECLARATION is transparent to the climb exactly when its NAME
is consumed from a recognised customer-facing position — the same narrow
clause as the fourth population, one declaration form over, never an
unconditional crawl of function bodies. Its literals get their own
`functionDeclared` bucket with its own blindness floor, so the
declaration clause rotting cannot hide behind the arrow members.

The C half (shared with the family member card): the gate's output now
prints Rule 3's scan boundary — the root, the not-scanned siblings with
the deferral pointer, and the recognised sink-shape list — derived from
the constants the scan reads, pinned derived-vs-derived in --self-test.

At this commit the widened gate is deliberately RED on the real tree
(reproduce-first): 4 violations — 2 functionDeclared, 2 message via the
new closure into declarations. The strip is the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
… refusal prose, re-pin the twins

The strip half of the fifth-population adjudication (2026-08-29, Class-1),
under the keep-the-ADR-id charter: 17 ids across 8 spec sources — the 15
the broad declaration census finds on current origin/main (materially
fewer than the relayed 47/51/14, measured before today's main moved) plus
the 2 hoisted ruling consts the widened sink closure newly reaches
(SUBMIT_REDIRECT_RULING, FORM_VIEW_FEATURES_RULING). ADR ids, protocol
versions, error codes and ruling dates stay; ids that were the whole
parenthetical take the parenthetical with them; load-bearing internal
references move to adjacent comments.

Twins re-pinned red-then-green, never weakened: 42 assertions across 7
spec test files red before this commit, 607/607 green after, each re-pin
a customer-resolvable anchor from the NEW text plus negative id pins at
the two ruling-const doors. The objectql and service-analytics twins
flagged by the id sweep pin sources OUTSIDE the scanned root and are
deliberately untouched (evidence for the cross-package revival census).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
…fusal messages, re-pin the twins

The member half of the 2026-08-29 family adjudication: the ruled strip is
the plugin-registration refusal's tracker id (the id the #13151-verified
twins at hot-reload.test.ts:259,302,310 pin); the sibling id in the same
refusal table's watchPatterns entry and in the startWatching() removal
notice is stripped in the same stroke under the bounded same-class
extension — same defect family, same keep-the-ADR-id remedy, same file,
same twin suite, declared in the PR body. ADR-0049, the spec/core
versions and the scheduleReload migration call stay as the
customer-resolvable anchors.

Twins red-then-green, never weakened: 8 assertions red before this
commit (10/18 passing), 18/18 after; each id pin re-pinned to a content
anchor from the NEW text plus negative id pins at all three doors —
mirroring the negative pin the spec-side parse door has carried since its
own strip. Repo-wide section-7 sweep for the stripped id set: the only
other test literal is an expect failure-label, not a content pin.

Root extension of the doc-authoring gate's Rule 3 is deliberately NOT
part of this change (deferred by the adjudication; the gate's new
boundary output is what keeps that deferral visible).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
…p's callee filter missed

Found by RUNNING the full mention set rather than trusting the sweep's
expect-callee filter: both pins spell the id check inside a .some()
predicate, so the nearest enclosing call is w.includes, not expect. Same
re-pin discipline — anchored on the warning's own prescription words
(COMPOSE_KEY_DISPOSITIONS / 'cannot be composed'), red-then-green
(2 red in the consumer batch, 35/35 green after), never weakened.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/core, @objectstack/spec, touching 18 documentable anchor(s).

1 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via stripLegacyApiMethods (symbol))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (literal, 29 pages)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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 — 132 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 74049254d47bd0edd2a2fcd732dcc01c91504f10packageMentionDocs.

Which tree this was computed on

This run read content/docs from fba5b6576265dfa3f8be5d7977d274929aaa9803 — the merge of head afb72654e8e8a76fba283d60fd1c4e1700eb3cfa into base 74049254d47bd0edd2a2fcd732dcc01c91504f10, 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 fba5b6576265dfa3f8be5d7977d274929aaa9803 && git checkout fba5b6576265dfa3f8be5d7977d274929aaa9803
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 74049254d47bd0edd2a2fcd732dcc01c91504f10 afb72654e8e8a76fba283d60fd1c4e1700eb3cfa && git checkout -B drift-repro 74049254d47bd0edd2a2fcd732dcc01c91504f10 && git merge --no-ff afb72654e8e8a76fba283d60fd1c4e1700eb3cfa

node scripts/docs-audit/affected-docs.mjs --json 74049254d47bd0edd2a2fcd732dcc01c91504f10

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 74049254d47bd0edd2a2fcd732dcc01c91504f10 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

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