Skip to content

docs(core,rest): the closed-set pin headers stop resting on a typecheck-script premise that is false - #16738

Merged
os-musk merged 2 commits into
mainfrom
claude/issue-16602-plugin-type-pin-header-drift
Sep 8, 2026
Merged

docs(core,rest): the closed-set pin headers stop resting on a typecheck-script premise that is false#16738
os-musk merged 2 commits into
mainfrom
claude/issue-16602-plugin-type-pin-header-drift

Conversation

@os-musk

@os-musk os-musk commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16602

Clause-②: no

Comment drift only. Two test-file headers explained the closed-set pins' placement with a
sentence that is false on this tree, and the rest header still carried a refusal wording
that #16049 measured false. No pin moves, no @ts-expect-error is added or deleted, no
case is touched, and the ratchet and ablation paragraphs are unchanged.

What was false, and what replaced it

1. "@objectstack/core has no typecheck script (it is a type-check DEBT ledger entry)"
— carried by both headers as the reason the compile-time pins live in packages/rest.

Measured on this tree: packages/core/package.json declares
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.examples.json && pnpm check:test-typecheck",
and @objectstack/core appears in neither DEBT nor EXEMPT nor TEST_DEBT in
scripts/check-type-check-coverage.mjs. #14613 split packages/core/tsconfig.test.json
out of the build config and the typecheck script names it via
check:test-typecheck --project, so tsc --listFiles -p packages/core/tsconfig.test.json
puts packages/core/src/plugin-type-closed-set.test.ts in the program: a directive there
would be compiled, not the phantom pin check:type-check-coverage refuses.

The false premise goes; the conclusion stays, on the reason that is true. The placement
argument now rests only on what the rest program reads, which is what the card asked for and
what the rest header already said two sentences later. Measured, same run:
tsc --listFiles -p packages/rest/tsconfig.test.json contains
packages/core/dist/index.d.ts and zero files under packages/core/src/, while core's
own program contains packages/core/src/types.ts. So the rest pins read the BUILT .d.ts
consumers resolve and a core-side pin would read the declaration instead — a reason that
does not move when a package's script list does.

2. "type?: string ... while the Zod gate (PluginSchema.type) refused it at parse"
— rest header only.

Measured false by #16049 (from #15638): PluginSchema had no runtime caller and kernel
plugin objects were never parsed. On this tree the refusal is on the BOOT path —
PluginLoader.validatePluginContract (packages/core/src/plugin-loader.ts) runs
PluginSchema.safeParse over every plugin object, called from loadPlugin, which
kernel.use() invokes (packages/core/src/kernel.ts). packages/core/src/types.ts was
corrected when that was measured; this header carried the same wording and was missed.
Both corrections are recorded in the headers themselves, in the form types.ts already
uses, so the next revision cannot reintroduce them silently.

Verification

The whole subject of this card is a claim about which tsc program evaluates these
directives, so the measurement that matters is that the directives still behave. Both
packages' typecheck scripts were read out of their package.json and run whole:

  • @objectstack/core — 3 legs: tsc --noEmit, tsc --noEmit -p tsconfig.examples.json,
    pnpm check:test-typecheck (--self-test, then
    --package packages/core --project tsconfig.test.json). Exit 0.
    check:test-typecheck: OK — 4 file(s) / 4 error(s) / 4 pinned signature(s), exactly the
    four ledgered files; plugin-type-closed-set.test.ts is unlisted and reports zero.
  • @objectstack/rest — 2 legs: tsc --noEmit, pnpm check:test-typecheck (--self-test,
    then --package packages/rest --project tsconfig.test.json). Exit 0.
    check:test-typecheck: OK — 0 file(s) / 0 error(s), so all three @ts-expect-error
    directives in this file are still USED — an unused one is TS2578 and reds an unlisted
    file.

Both changed test files run green (3 passed core, 4 passed rest). Gates: the 47
families scripts/pm/dispatch-gates.mjs derives for these two paths were all run and all
exit 0, reconciled with --ran:
"dispatch-gates --ran: 47 derived famil(ies) accounted for — 47 run, 0 NOT-MEASURED."

Lint is CI's repo-wide run, and the narrowing here is a measurement rather than a skip:
eslint --format json over the two touched files reports 2 files, 0 errors, 0 warnings,
and eslint.config.mjs states at lines 327-328 that this repo "runs one
eslint.config.mjs, which never enables type-aware linting (no parserOptions.project,
no typed @typescript-eslint rules) for ANY file, test or not"
— so this diff cannot
move the verdict on any file it does not touch.

Every number above was taken on fb15b31e69, this branch's head, with the workspace
closure built (turbo run build --filter='./packages/*' --filter='./packages/*/*').

Changeset — skip-changeset, and the text it comes from

Governing text: .github/workflows/pr-automation.yml, the Require a changeset (or the
skip-changeset label)
step, route 2 at lines 684-690: "It releases nothing (.github/,
.claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> apply the
'skip-changeset' label."
— the workflow marks that route PREFERRED with a triple
left-angle marker this body cannot carry literally — "The label is a gate-level exemption.
It produces NO input for changesets/action, so it cannot affect a release."
This diff is
comment-only prose inside two *.test.ts files. Both packages publish
files: ["dist","README.md","CHANGELOG.md"], both build configs exclude **/*.test.ts,
and — measured with a control that does fire — neither header's prose appears anywhere
under any dist/, while a types.ts docblock sentence appears in two. It releases nothing.

The WHICH LEVEL ruling above it (lines 667-682, maintainer, 2026-09-04, decision batch
#35 on #15294) governs the level if route 1 applied; it does not, because this act
changes no public surface at all — additively or otherwise.

Rejected: AGENTS.md:1028-1030 read alone — "A bug fix in a released package takes a
patch changeset — never none, and never skip-changeset"
. That is the floor against
none for a fix that publishes; its own next clause is the predicate that decides this
case — "that label is for a diff that publishes nothing from any released package"
and this diff was measured to be exactly that. Citing it and stopping would invert it.

验收备注

Read the whole of both headers, as the card asked, and the rest of each still describes the
tree — with two things reported and deliberately not fixed here:

  1. The same false sentence lives in three more files, all outside this card's two
    headers. Found by reading, after a comment-prose scanner that strips // prefixes and
    flattens wrapping (a plain grep cannot match it: backticks sit inside the phrase and it
    wraps across a comment prefix); the scanner's control was that it must find the two known
    carriers, which it did.
    • packages/rest/src/plugin-metadata-retired-fields.pin.test.ts and
      packages/core/src/plugin-loader.retired-fields.pin.test.ts — same claim about
      @objectstack/core, same ADR-0049 pin pair, both false for the same reason.
    • packages/runtime/src/sandbox/quickjs-runner.test.ts says @objectstack/runtime "has
      no typecheck script (it sits in the DEBT table)"; runtime declares
      "typecheck": "tsc --noEmit && pnpm check:test-typecheck" and is not in that table.
    • packages/services/service-automation/src/flow-trigger-kind-shared-resolver.test.ts
      says "This package has no typecheck script"; it declares one too.
  2. plugin-type-closed-set.pin.test.ts line ~54, inside a case body rather than the
    header, still reads "the case the old type?: string admitted and the Zod gate could
    only catch at parse". Left alone: the card scopes this PR to the two headers and rules
    the cases out.

Also noted, not a defect: both headers describe the pinned surface as type?: PluginType,
which since #16334 is inherited from PluginDefinition rather than spelled on the
interface. The sentence is true of the resulting published surface, and the file's own
positive control already pins the inherited-key identity, so nothing here is stale — only
less precise than the case comment beneath it.

🤖 Generated with Claude Code


Generated by Claude Code

…cript core has

Both headers explained the pins' placement with "`@objectstack/core` has no
`typecheck` script (it is a type-check DEBT ledger entry)". False on this tree:
#14613 split a `tsconfig.test.json` out of core's build config and core's
`typecheck` names it via `check:test-typecheck --project`, so a directive in
core's test layer is compiled, and core carries no DEBT entry either.

The pins do not move. The placement conclusion stands on the reason that is
true and does not depend on any package's script list: the rest package's
test program resolves `@objectstack/core` to the BUILT `dist/index.d.ts`,
the contract consumers resolve, while core's own program compiles core's
`src` and would read `./types.ts` instead.

The rest header also still said the old `type?: string` was refused by the
Zod gate "at parse". Measured false (#16049, from #15638): `PluginSchema` had
no runtime caller and kernel plugin objects were never parsed. The refusal is
on the boot path now -- `PluginLoader.validatePluginContract` runs
`PluginSchema` over every plugin object `kernel.use()` loads --
which `packages/core/src/types.ts` already records and this header did not.

Comment-only: no pin moves, no `@ts-expect-error` added or deleted, no case
touched, and the ratchet and ablation paragraphs are unchanged.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 1ea349f0ebc4b9a0090af03ecbdea9b1abcef871packageMentionDocs.

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

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants