Skip to content

feat(spec): register the ADR-0087 semantic entry ui-html-page-div-refused (#20592) - #20610

Merged
os-justin merged 2 commits into
mainfrom
claude/issue-20592-ui-html-page-div-refused
Sep 29, 2026
Merged

os-justin merged 2 commits into
mainfrom
claude/issue-20592-ui-html-page-div-refused

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Closes #20592

Registers ui-html-page-div-refused, the protocol-18 ADR-0087 semantic entry for the html-tier div refusal that PR #20589 brings to projects with no sdui.manifest.json of their own (div → box).

Clause-②: no


Generated by Claude Code

…used

The protocol-18 step records the html-tier div refusal a manifest-less
project meets once the CLI's JSX page gate reaches the manifest
@objectstack/console ships: box for a plain wrapper, and how to prove
the rewrite done. registry.ts regenerated by gen:migration-registry;
spec-changes.json and the upgrade guide regenerate byte-identical,
because step 18 is above the current protocol major.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
Its default range runs to the chain terminus, protocol 18, so the
entry reaches authors through migrate meta now; only the upgrade guide
and spec-changes.json wait for the protocol major.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 137 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 0f6dcac5e99d0c6211f0d8a0e150a112d78a776f → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 0f6dcac5e99d0c6211f0d8a0e150a112d78a776f

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 95ae699a5bb78a0cf6e11f687b08d63c459d484b
Local-runs: none

① Derived judgments

Diff read as main 0f6dcac5e9 … 95ae699a5b (merge-base 7a1faf1a5d; two commits; 3 files, +132/−0): the new semantic entry packages/spec/src/migrations/entries/semantic/18.ui-html-page-div-refused.ts, the regenerated registry.ts (the same entry inside step18.semantic), and .changeset/20592-ui-html-page-div-refused.md. The pinned objectui .objectui-sha is dd3f7e1be3 on both main and the head, and objectui#10808's merge 39b8d51022 is its ancestor.

Accept-set changes implied: none — right. No Zod schema, tombstone, guidance map or D2 conversion moves. Public-surface changes implied: one, additive data — right. MIGRATIONS_BY_MAJOR[18].semantic gains one element (317 semantic entries; the SemanticMigration type is unchanged), so objectstack migrate meta prints one more [protocol 18] block for any range whose --to reaches 18 — which the default does: CHAIN_TERMINUS_MAJOR = max(PROTOCOL_MAJOR, ...MIGRATION_MAJORS) = 18 (packages/cli/src/commands/migrate/meta.ts:88). spec-changes.json and docs/protocol-upgrade-guide.md are rightly absent from the diff: both generators loop major from the floor to PROTOCOL_MAJOR = 17 (build-upgrade-guide.ts:78, build-spec-changes.ts:254), so a step-18 entry renders nowhere yet. Step 18 is the right step: PROTOCOL_VERSION is 17.0.0, the precedent 18.ui-react-list-view-binding-aliases-retired and every recent narrowing sit under 18. The id equals the marker PR #20589's changeset carries at d9dc0be3ed (adr-0087: registered ui-html-page-div-refused).

Moment key: [now] = true on main the moment this PR lands; [after #20589] = true only once PR #20589 (branch claude/issue-19922-console-manifest-fallback, draft) lands.

surface — right.

  • "kind:'html' page source (and its deprecated kind:'jsx' alias)": right [now]. page.zod.ts:839-843 names 'jsx' the deprecated alias; the CLI gate's checkedPageKeys reads both kinds (packages/cli/src/utils/sdui-manifest.ts).
  • "in a project with no sdui.manifest.json of its own": right [now] as the scope — resolveSduiManifest reads cwd/sdui.manifest.json first and falls to the console copy only when it is absent.
  • "the div tag, and any other tag or prop the SDUI component manifest shipped in @objectstack/console does not declare": right [now] as the governed scope. The tracked sdui.manifest.json (107 components; scripts/sdui-manifest.record.json names objectuiSha = the pin) has no div; build-console.sh:241,294 copies it to packages/console/dist/sdui.manifest.json, and packages/console publishes dist. One precision an author should not over-read: an undeclared PROP draws unknown-prop at severity warning (packages/sdui-parser/src/validate.ts:141-143), which fails objectstack validate only under --strict; the entry never claims a prop fails the command, so the surface is a scope statement, not over-broad.

replacement — right, sourced from the pin.

  • "box for a plain wrapper — the one drop-in swap: the same element, your className verbatim, the same children, and no layout of its own": right [now]. objectui packages/components/src/renderers/layout/box.tsx at the pin renders a div element, passes className through with zero injected classes and reads children; basic/div.tsx's own notice and its deprecated.replacement say the same words. The manifest's box declares exactly className and children.
  • "Reach for card, flex, container, stack or grid only where you want their layout": right [now] — the five names in div.tsx's replacement text, all five declared in the manifest.
  • "For any other tag or prop the command names, a component and prop the manifest declares; the file is dist/sdui.manifest.json inside @objectstack/console": right [now] for the file's location (files: ["dist", ...], written by build-console.sh); PR fix(cli)!: the JSX page gate's console manifest fallback resolves through @objectstack/console/package.json, so a project without its own manifest gets full component checking #20589's own npm pack listing confirms it ships. @objectstack/console is this repo's packages/console (objectui's app is @object-ui/console), so the package name is the right one.

reason — right in substance; three sentences are true only after #20589, one is over-broad by a condition.

acceptanceCriteria — right.

  • The three rule ids and sdui/jsx-parse-level-only (JSX_PARSE_LEVEL_ONLY_RULE, sdui-manifest.ts:79) exist [now]; the notice fires only when a checked page exists, which is the criterion's reading ("a clean result beside it proves nothing").
  • "objectstack compile and objectstack lint agree": right [now] — one resolver, three callers.
  • "in the console each rewritten page renders with no compile error": right [now] at the pin (the box control case in objectui#10808's pin).
  • "the showcase app's three html pages, which author their wrappers as box and pass with zero findings": right [now] — exactly three kind:'html' pages under examples/app-showcase/src/ui/pages/ (capability-map, command-center-jsx, start-here), no div tag in any, and packages/lint/src/validate-jsx-pages.production-witness.test.ts:115 pins the wired run clean.

Form D: the four author-shown fields carry no tracker number; the tracker ids sit in the file's header comment (and its generated copy) only. ui- is a COVERED_PREFIXES family in migrate-meta-engine-guidance.test.ts.

Changeset text (.changeset/20592-ui-html-page-div-refused.md): every sentence right [now] except the enforcement clause "so objectstack validate, compile and lint refuse a div there", which is framed as the narrowing the CLI "takes on when its JSX page gate reaches" the manifest and is true [after #20589]. "MIGRATIONS_BY_MAJOR[18].semantic gains one entry" — right; migrate meta prints it by default — right (terminus 18); spec-changes and the upgrade guide unchanged — right; "no schema accepts or refuses anything it did not before" — right.

The landing window, named: the card's Order lets the entry land first, and the dev measured PR #20589's marker flipping to not-required (already-registered ui-html-page-div-refused) plus a reason in that case. Between the two landings, migrate meta on main prints a prescription for a refusal the CLI does not yet perform; a release cut inside that window would ship the ledger row ahead of the behaviour (spec and cli are one fixed group, both 17.4.0). Sanctioned by the card and by triage 5884625733; carried to ③ as an escalation, not a fault.

② Semver level

'@objectstack/spec': patch, Clause-②: no (standalone, no arm), on both the changeset and the PR body — matches what the diff publishes. Nothing an author can write is added, removed or renamed; no export or type changes (SemanticMigration and the type of MIGRATIONS_BY_MAJOR are untouched); the package gains one data row in its ledger. The precedents are patch: .changeset/17594-step18-element-node-todo.md (a D3 TODO added to the 17→18 chain) and .changeset/20379-currency-precision-d3-text.md (two step-18 D3 entries reworded, #20488). skip-changeset would be wrong — the ledger ships in the package and migrate meta prints it. Not breaking, so no adr-0087: marker is owed; the dev's check-adr-0087-registration --base origin/main read "adds no declared-breaking changeset (1 non-breaking changeset(s) seen)", and the head's Check Changeset run is green. The feat(spec): prefix beside patch is read by no gate and the 17594 precedent used fix(spec):; cosmetic, not a level error. The narrowing itself stays where triage put it: @objectstack/cli minor, Clause-②: no (narrowing), on PR #20589.

③ Boundary flags

Dev report (os-dev-report, comment 5885694074): open_questions: []; five deviations, two out_of_scope_findings, each answered:

  1. "PR body opens Closes #20592, os-dev.md names Fixes" — answered: both are closing keywords; the Part-of PR must not also close its card and The card this PR closes must claim this branch runs are green. No action.
  2. "PR assignee and labels: zero writes" — answered: the seat's act. PR labels at read are documentation, size/m, tooling, needs:contract-review; the card assignee is os-justin. The seat sets the PR assignee when it posts.
  3. "spec-changes.json and docs/protocol-upgrade-guide.md regenerated byte-identical, so absent from the diff" — answered and verified above (both generators stop at PROTOCOL_MAJOR = 17). The claim's "regenerated only" is satisfied with no bytes moving; the head's check:generated family runs inside Lint & Repo Gates, still running at read.
  4. "Scratch worktree held two local commits (merge 07dd8aa6db, marker edit fdc65658ad), never pushed, worktree removed" — answered: the branch carries exactly two commits over the merge-base (c352c0ae20, 95ae699a5b); neither scratch sha is on it.
  5. "Probe file packages/lint/.probe-20592.mjs written and removed in one command" — answered: the diff has three files and no probe.

Out-of-scope findings, both verified true here and escalated to the #19922 domain:cli seat (PR #20589's same-head review is the carrier; nothing in this PR changes):

  1. PR fix(cli)!: the JSX page gate's console manifest fallback resolves through @objectstack/console/package.json, so a project without its own manifest gets full component checking #20589's changeset says "the html-tier renderer still renders div, deprecated there in favour of box". At the pin dd3f7e1be3 this is false: page.tsx refuses the tag at compile with a forbidden-tag error that names box. That sentence would ship false in @objectstack/cli's CHANGELOG.
  2. Seat comment 5884397563 on [finding] the CLI's @objectstack/console manifest fallback can never resolve: resolveSduiManifest() asks for @objectstack/console/dist/sdui.manifest.json, but the console's exports map publishes only ./package.json #19922 says "the upgrade guide (via spec(migrations): register the ADR-0087 semantic ledger entry for the html-tier div refusal that #19922's live console fallback brings to manifest-less projects (div → box) #20592) carries the prescription meanwhile". False: the guide projects steps up to protocol 17 and does not change here; objectstack migrate meta (default --to 18) is what carries it. PR fix(cli)!: the JSX page gate's console manifest fallback resolves through @objectstack/console/package.json, so a project without its own manifest gets full component checking #20589's Acceptance notes should say so.

Reviewer's escalations:

  1. The landing window (① above): the seat lands PR fix(cli)!: the JSX page gate's console manifest fallback resolves through @objectstack/console/package.json, so a project without its own manifest gets full component checking #20589 before the next release cut, or accepts that a cut inside the window ships the ledger row ahead of the refusal. If feat(spec): register the ADR-0087 semantic entry ui-html-page-div-refused (#20592) #20610 lands first, PR fix(cli)!: the JSX page gate's console manifest fallback resolves through @objectstack/console/package.json, so a project without its own manifest gets full component checking #20589's marker must read not-required (already-registered ui-html-page-div-refused) plus a reason, as the dev measured.
  2. "objectstack compile (which dev and start run first)" is conditional in the code (auto-compile only when the artifact is missing or --compile is passed). Shared verbatim with PR fix(cli)!: the JSX page gate's console manifest fallback resolves through @objectstack/console/package.json, so a project without its own manifest gets full component checking #20589's changeset; for the CLI seat to tighten or keep.

Check-runs on 95ae699a5b, 38 raw, 34 after dedupe by name on newest started_at, read after 2026-09-29T07:35Z: 24 success, 5 skipped, 0 failure, 5 still running — Lint & Repo Gates, Test Core (1/6), Test Core (3/6), Test Core (5/6), Test Core (6/6). Green and load-bearing here: Check Changeset, Governed Surface Queue Guard, Spec property liveness, the four Type Check · runs and TypeScript Type Check, Build Core, Dogfood Regression Gate (3 shards and the rollup), Dogfood Verify CLI, Temporal Conformance (live PG + MySQL), Test Core (2/6), Test Core (4/6), the three PR-claim runs, Check Documentation Links, Flag docs affected by code changes, filter. Skipped by design: Auto Label, Build Docs, Check PR Size, Console Pin Gate (no pin move), Packed-tarball smoke (opt-in). The five running runs carry the spec test files and the repo gates (check:generated for registry.ts, check-issue-citations, check-adr-0087-registration, pnpm lint); this record's PASS is on the contract, and a red among those five supersedes it — the seat holds landing until all five complete green.

Implemented-by: claude/issue-20592-ui-html-page-div-refused
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: PASS

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-09-29T07:43Z · rendered by the seat's at-tier review subagent on this head; its served tier family was read from the subagent transcript before posting. ③ items 6 and 7 went to the #19922 seat as 5885765740 before this record. Item 8: the card's order stands (this lands first); the seat tells #19922 at landing, and a release cut is the maintainer's. Item 9 stays as written: the reviewer rates it not load-bearing, and a new head would reopen the review for one parenthetical while this week's usage reads at its warning level. Landing waits for the five running check-runs to finish green.


Generated by Claude Code

@os-justin
os-justin marked this pull request as ready for review September 29, 2026 07:47
@os-justin
os-justin enabled auto-merge September 29, 2026 07:47
@os-justin
os-justin added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 7510663 Sep 29, 2026
44 checks passed
@os-justin
os-justin deleted the claude/issue-20592-ui-html-page-div-refused branch September 29, 2026 08:05
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…data/analytics.zod.ts to the commits that decided them (stage 7) (objectstack-ai#20616)

Part of objectstack-ai#20234
Clause-②: no

Stage 7 of the staged sweep: `packages/spec/src/stack.zod.ts` and
`packages/spec/src/data/analytics.zod.ts`, both freed by landings (PR
objectstack-ai#20579 and PR objectstack-ai#20458). Its claim is `5885635758`. Every comment or
docblock line in those two files that cited a tracker number answering
404 now cites the commit on `main` that decided its rule, in ruling
C+D's form C, and says in its own words what was decided. Comments only:
12 lines out, 12 in, across 2 files. No code token, string literal or
`describe()` text moves. No dead site stays: none of the 12 is read by
literal.

The census is the gate's own `node scripts/check-issue-citations.mjs
--census --json`, filtered to the two paths. Before: base `0f6dcac5e9`,
board enumerated (185 pages, frontier objectstack-ai#20611). After: head `cc0580d404`,
board enumerated (185 pages, frontier objectstack-ai#20615).

## Measurement

| file (under `packages/spec/src/`) | dead before | after | numbers,
then anchor |
|---|---:|---:|---|
| `stack.zod.ts` | 9 | 0 | objectstack-ai#10485 ×2 (`:415`, `:1023`) to `35ad101bc`;
objectstack-ai#6238 (`:633`) to `c8d6f6e08`; objectstack-ai#14192 (`:1233`) to `4d0d9445a`; objectstack-ai#14686
×2 (`:3037`, `:3194`) to `279431e7a`; objectstack-ai#14662 ×3 (`:4510`, `:5070`,
`:5293`) to `35dffeace` |
| `data/analytics.zod.ts` | 3 | 0 | objectstack-ai#10194 ×3 (`:404`, `:407`, `:485`)
to `2306a765c` |
| **2 files** | **12** | **0** | 6 numbers removed, 6 distinct shas |

Per-file counts at base equal the claim's (9 and 3, from stage 6's
census). A second instrument agrees site for site: every `#N` in the two
files, classified by the TypeScript parser, and each of the 84 distinct
numbers of 100 or more probed by REST `issues/N` without following
redirects (the other 3 are the ordinals `Prime Directive objectstack-ai#12`, `batch
objectstack-ai#23`, `batch objectstack-ai#57`).
- Base: 244 sites, all in comments (0 strings, 0 code). 78 numbers
answer 200 and 6 answer 404: the same 6 numbers and the same 12 sites as
the gate.
- Its string-class positive control found 11 string sites in
`kernel/manifest-unknown-keys.test.ts` and
`packages/cli/src/utils/lower-callables.test.ts`.
- Head: 232 sites, 78 numbers, all 78 answer 200 (the same 78), none
answers 404.
- Lit controls objectstack-ai#16862, objectstack-ai#16847 and objectstack-ai#17698 answered 200 at every
checkpoint (3 at base, 3 at head); dead controls objectstack-ai#16714, objectstack-ai#16715 and
objectstack-ai#16697 answered 404 at every checkpoint.

## Why each anchor decides its line

Each sha resolves uniquely, is an ancestor of `origin/main` (and of the
base), and has one parent. No file under `docs/adr/**`,
`docs/NORTH-STAR.md` or `scripts/adr-anchors/` names any of the six
numbers or records these rules, so each takes the commit rung, as stages
1–6 did.

- **objectstack-ai#10485 to `35ad101bc`** (`:415`, `:1023`): retires the `themes`
carrier key and `ThemeSchema` under ADR-0049. Its message records the
ruling, "Ruled B (退役授权面, 2026-08-21)", and its own `stack.zod.ts` diff
wrote both lines. `:415` keeps ADR-0049 and the ruling in its words; the
D3 entry `stack-themes-carrier-retired` it names on `:423` is unchanged.
This is the anchor stages 1, 5 and 6 used for the same retirement.
- **objectstack-ai#6238 to `c8d6f6e08`** (`:633`): widens the array member of
`functions` so its `handler` also takes the lowered string ref, which is
the fix for `objectstack build` refusing its own array output. Its
message names objectstack-ai#6238, and its own diff wrote the line. objectstack-ai#4343 and objectstack-ai#4976 on
the same line stay (both 200).
- **objectstack-ai#14192 to `4d0d9445a`** (`:1233`): turns `ManifestSchema` and its
nested blocks into `strictObject` and flips the assembled-body strip pin
to a refusal pin; each of its sub-commits names objectstack-ai#14192. The line itself
was written later by `c78c9180de`, whose own message says "objectstack-ai#14192 closed
ManifestSchema with strictObject", so the commit that closed it is the
anchor.
- **objectstack-ai#14686 to `279431e7a`** (`:3037`, `:3194`): "defineStack refuses two
actions that resolve to one scope-qualified runtime key". Its subject
names objectstack-ai#14686, and its diff adds `collectDuplicateActionKeyErrors` and
the changeset for that refusal. Both lines were written later by
`773a99960a` (PR objectstack-ai#15022), whose message describes the same "same-key
rule, which runs before the merge".
- **objectstack-ai#14662 to `35dffeace`** (`:4510`, `:5070`, `:5293`): "composeStacks
refuses two stacks whose actions resolve to one scope-qualified runtime
key". It checks the composed set with the rule `defineStack` applies
within one stack, with no `actionConflict` option (maintainer ruling
2026-09-03). Its message does not name objectstack-ai#14662; its own `stack.zod.ts`
diff wrote all three `(objectstack-ai#14662)` lines.
- **objectstack-ai#10194 to `2306a765c`** (`analytics.zod.ts:404`, `:407`, `:485`):
binds `analytics_cube` (and `theme`) in `UNREGISTERED_KIND_SCHEMAS`, so
`PUT /meta/analytics_cube/:name` parses through `CubeSchema`, and gives
`CubeSchema` the `...MetadataProtectionFields` spread. Its message names
objectstack-ai#10194, and its own diff wrote all three lines. The `[objectstack-ai#10194]` markers
become `[commit 2306a76]`, the spelling stages 1 and 5 already use in
`kernel/metadata-type-schemas.ts`.

## Mechanical proof

- **Token guard** (my `tokcmp.mjs`: TypeScript 6.0.3 leaf tokens, JSDoc
kinds excluded, controls mutate the head text in memory only). Base
`0f6dcac5e9` against the head, 2 files, 17,249 base tokens:
  - Real run: 0 files with a token change (exit 0).
  - Comment-insertion control (`data/analytics.zod.ts`): 0 (exit 0).
- Code-insertion positive control (`stack.zod.ts`, a declaration
appended): DIFFER at token 15388 (exit 1).
- String positive control (the first `StringLiteral` the parser locates
in each file): DIFFER at token 5 (exit 1), once per file.
- `describe()` positive control (the first `.describe()` string argument
the parser locates: `stack.zod.ts:133`, `analytics.zod.ts:244`): DIFFER
at tokens 507 and 442 (exit 1).
- **Line balance**: `stack.zod.ts` +9/−9, `data/analytics.zod.ts` +3/−3;
line counts equal at base and head (5344 and 853).
- **Tracker numbers**: added-not-removed is empty in both files, and no
`PR #N` is on an added line. Net-removed: 12 sites, 6 numbers. The only
numbers on added lines are objectstack-ai#4343 and objectstack-ai#4976, which stay on `:633`.
- **Shas**: 6 distinct on added lines, 0 on removed lines.
  - `rev-parse --disambiguate` answers 1 object for each.
- `merge-base --is-ancestor` exits 0 for each, against `origin/main`
`7510663c87` and against the base; each is single-parent; the repository
is not shallow.
- **Literal readers**: all 26 string, template and regex literals in the
repository that carry one of the six numbers (42 code files) were
matched against the two files' base text: 0 occur there. Each removed
line was also cut into 4-word windows (96) and searched across the tree:
the 9 hits inside string literals are other files' own test titles
sharing a phrase ("the ADR-0010 protection envelope", "an assembled body
is"), and none reads either file. The source-text readers of the two
files read code, not these comments:
`compose-stacks-refusal-envelopes.test.ts` counts `throw new Error(`,
and `check-stack-collection-maps.mjs` and
`check-skill-top-level-keys.mjs` read the declared collections and keys.

## Tests and gates (at head `cc0580d404`)

- `pnpm exec turbo run build --concurrency=2 --filter=./packages/*
--filter=./packages/*/*` under `os-verify-lock`: Tasks 71 successful, 71
total, VERDICT command-exit 0.
- `pnpm --filter @objectstack/spec check:generated` under the lock: all
15 generated artifacts up to date, `check:docs` over
`content/docs/references/**` included; VERDICT command-exit 0. No
reference page projects any of the 12 lines, so none is regenerated.
- `vitest run --maxWorkers=2` under the lock over the two files' own
suites (`src/stack*`, `src/compose-stacks*`, `src/define-stack*`,
`src/assembled-package-body`, `src/data/analytics*`, `src/data/cube*`):
Test Files 35 passed (35), Tests 976 passed (976).
- The 37 spec suites that read source text across `src/`, or carry one
of these numbers, under the lock: Test Files 37 passed (37), Tests 759
passed (759).
-
`scripts/{category-title,dist-freshness,dist-freshness-adoption,file-description,strictness-ledger,strictness-ledger-doc,root-index,skill-map-guards,export-origins,split-entries,root-entry-type-nameability.pin}`,
`scripts/liveness/{evidence,tombstoned-row-status}`;
- `src/type-alias-convention.pin`, `src/eager-entry-import`,
`src/api/{api-entry-graph.pin,auth,export-job-family-retirement}`,
`src/ai/tool-confirmation-prescription-tense.pin`,
`src/data/{currency-mode-family-closure.pin,external-lookup-retirement}`,
`src/identity/position-delegatable-enforcer.pin`,
`src/integration/{connector-connection-timeout-retirement,connector-resilience-keys-retirement}`,
`src/security/rls-tags-retirement`,
`src/shared/{alias-integrity,retired-key-migrate-sentence}`,
`src/system/{compliance-families-retirement,constants/platform-object-names,email-template-floor-locale-parity.pin,message-queue-retirement}`,
`src/ui/{action-requires-confirmation-docblock.pin,i18n,interaction-config-retirement,strictness-batch14}`,
`src/kernel/{manifest-unknown-keys,metadata-type-schemas}`.
- Left to CI:
`scripts/{build-schemas-check-mode,def-key-collisions,openapi-self-consistency}`
(each rebuilds artifacts in a temp tree) and
`scripts/{check-generated-ledger,check-generated-fix-rebuild.pin}` (read
the ledger and `dist`). None reads comment text.
- `pnpm --filter @objectstack/spec typecheck` under the lock: exit 0;
`check:test-typecheck` OK (53 files / 251 errors / 138 pinned signatures
held).
- Lint, a proven narrowing: `eslint --no-inline-config --format json`
over the 2 files gives 2 files, 0 errors, 0 warnings.
  - `isPathIgnored` is false for both, read through eslint's API.
- `eslint.config.mjs:327-328` says type-aware linting is never enabled,
so a comment edit cannot move an untouched file's verdict.
  - The repo-wide `pnpm lint` is CI's.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`: 79 families derived and run, every one exit 0. `--ran`
reads "79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN". Among them:
- `pnpm check:issue-citations` (self-test, 114 cases in 8 batteries) and
the live diff-scoped `node scripts/check-issue-citations.mjs`: it judged
the 2 citations on added lines, objectstack-ai#4343 and objectstack-ai#4976, and both are live
issues.
- `pnpm check:doc-authoring`: 16,804 customer-facing strings across
1,179 spec sources clean; the sibling baseline holds.
- `pnpm check:stack-collection-maps`: 8 enumerations reconciled against
31 declared collections.
- Changeset: `patch` for `@objectstack/spec`. Both files are
`src/**/*.zod.ts`, which `files[]` ships verbatim, and the rewritten
docblocks reach `dist`: "posture: commit 4d0d944 closed" and "[commit
2306a76] This docblock used to say" are each in 2 `.d.ts`, their old
spellings in 0. Positive control: the unchanged neighbouring sentence
"BY INHERITANCE — an undeclared key on one is REFUSED" is in the same 2
`.d.ts`.
- Merge probe: a no-driver `merge-tree` of the head onto `origin/main`
`7510663c87`, from a bare shared clone, exits 0. The 3 commits `main`
gained since the base touch neither file nor the citation or derivation
scripts, and a re-derivation prints the same 79 commands. No merge was
made.
- No ablation or reverse verification: the change is comment-only, so
there is no behaviour to invert.

## Hypotheses (measured first)

1. **Holds.** 12 dead sites at the tip, 9 in `stack.zod.ts` and 3 in
`data/analytics.zod.ts`, equal per file to stage 6's census.
2. **Holds.** Read at 2026-09-29T07:36Z and again at 08:16Z, after the
last push and before this PR was opened: all open PRs' full file lists
(9 PRs, 166 files at the second read) and the newest `Claim:` on all 11
`pm:dispatched` cards. None names either file, except this card's own
claim.
3. **Holds, with nothing to keep.** All 12 sites are comments. No test
string, exported string or `describe()` text carries one, and no test or
script reads any of them by literal.
4. **Holds.** No generated reference page projects these lines;
`check:docs` is green with no regeneration.

## Deviations

- None to the file surface: the 12 claimed lines and one changeset, no
generated page needed.
- Commit trailers follow AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`); the pre-push trailer check passed on
every push.

## Acceptance notes

**What stays for later stages.** The gate's census at this PR's head
(base `0f6dcac5e9` plus this PR) reads **248** dead sites (29 numbers)
in `packages/spec/src`. The only `packages/spec/src` change `main` has
made since the base (objectstack-ai#20610's migrations entry and registry) adds four
live numbers and removes none, so 248 also stands at the tip
`7510663c87` plus this PR:
- `migrations/` **233**: objectstack-ai#20233 edits the same entry files (PR objectstack-ai#20607
holds `migrations/registry.ts`).
- `conversions/registry.ts` **12**: PRs objectstack-ai#20570 and objectstack-ai#20587 hold it.
- `integration/connector.zod.ts` **1**: PR objectstack-ai#20587 (objectstack-ai#20287).
- `data/api-derivation.ts:163` (objectstack-ai#6259) and
`identity/identity.zod.ts:230` (objectstack-ai#8715), **1** each: kept because tests
read them by literal, so removing them is form D.

**Outside the gate's census: test files.** The gate defers `*.test.ts`.
The same six dead numbers still stand at 15 comment sites and 10
test-title strings in `packages/spec/src` test files:
- `data/analytics-strictness-batchd.test.ts:96` (comment, objectstack-ai#10194) and
its title `:93`. This file is in the `analytics*` set stages 3 and 4
excluded while PR objectstack-ai#20458 held it;
`analytics-date-range-two-bound-window.test.ts` and
`cube-member-inner-name-retirement.test.ts` were in that set too and are
not re-measured here.
- The package root: `compose-stacks-action-echo.test.ts:20`, `:34`,
`:200` (objectstack-ai#14686) and titles `:176`, `:224`;
`compose-stacks-action-key-collision.test.ts:3` (objectstack-ai#14662);
`stack-top-level-strict.test.ts:103` (objectstack-ai#10485) and title `:128`;
`type-alias-convention.pin.test.ts:257`, `:1572`, `:1937` (objectstack-ai#10485).
- `shared/`: `metadata-collection.test.ts:250`,
`metadata-url-spelling.test.ts:51`, `:72`, `:168` (objectstack-ai#10485), `:257`
(objectstack-ai#10194), title `:254`. `automation/sync-retirement.test.ts:207`
(objectstack-ai#10485).
- `kernel/`: `manifest-unknown-keys.test.ts`, four titles (objectstack-ai#14192);
`metadata-type-schemas.test.ts:422`, a title (objectstack-ai#10194).
- Stage 6 took the package root, `shared/` and `automation/` through the
gate's census, which never lists a test file, so test-file comment lines
there may carry other dead numbers as well. That wider population is not
measured here.

**Outside `packages/spec/src`.** The same six numbers stand at 44 more
sites
(`packages/{metadata-protocol,objectql,rest,runtime,cli,core,metadata,qa}`,
`examples/`, `scripts/`, `packages/spec/scripts/`), and at 19 sites in
`migrations/` (the objectstack-ai#20233 area).

**Rung.** The objectstack-ai#10485 retirement also has the ADR-0087 D3 entry
`stack-themes-carrier-retired`, which `:423` already names. This PR
takes the commit rung, as stages 1–6 did.

**Wording, each true of its commit.** `:3037` and `:3194` now read
"commit 279431e's same-key refusal": the refusal that commit added, in
lines `773a99960a` wrote. `:1233` reads "commit 4d0d944 closed
`ManifestSchema`", in a line `c78c9180de` wrote.

---
_Generated by [Claude
Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…ough @objectstack/console/package.json, so a project without its own manifest gets full component checking (objectstack-ai#20589)

Fixes objectstack-ai#19922
Clause-②: no

## What this changes

`resolveSduiManifest()` (`packages/cli/src/utils/sdui-manifest.ts`) is
the one resolver `os validate`, `os compile` / `os build` and `os lint`
use to arm the JSX page gate. `os dev` and `os start` run `compile`
before they boot when `dist/objectstack.json` is missing or `--compile`
is passed, and `dev`'s default watch mode reruns it when a watched file
changes. The resolver's second place to look, the copy
`@objectstack/console` ships as `dist/sdui.manifest.json`, asked Node
for that file by its own subpath. The console's `exports` map publishes
`./package.json` alone, so the resolve threw
`ERR_PACKAGE_PATH_NOT_EXPORTED`, a `catch` swallowed it, and every
project with no `sdui.manifest.json` of its own had its `kind: 'html'`
pages checked at parse level only.

The fallback now resolves `@objectstack/console/package.json` from the
CLI's own location (`import.meta.url`, the CLI's declared dependency in
the same fixed release group) and joins `dist/sdui.manifest.json` to its
directory, through a new `consoleSduiManifestPath(origin)`. The
console's `exports` stays closed. `resolveSduiManifest(cwd,
consoleOrigin)` gains an optional origin, used only by the pins.

The old module header handed one decision to whoever made this leg
reachable: what a broken shipped copy should do. It now gets the project
leg's rule. A shipped copy that is present but cannot be read or parsed
is `unusable` (new `source: 'console'`), and the command is refused with
exit 1, naming the file, with the remedy "reinstall
@objectstack/console". It is never read as "not found". With no page to
check it is read by nothing and not refused, the same as the project
leg.

## This round (the seat's unlock record `5890591366` on objectstack-ai#19922)

The ledger entry `ui-html-page-div-refused` landed on `main` (objectstack-ai#20592, PR
objectstack-ai#20610), and Version Packages objectstack-ai#17076 consumed
`.changeset/sdui-manifest-one-producer.md`. This round:

1. **Merged `origin/main` at `f1e921ab8e`** (a merge, not a rebase;
merge commit `f9cb969f44`). One conflict:
`.changeset/sdui-manifest-one-producer.md`, modify/delete, resolved in
favour of `main`'s deletion.
`build-json-failure-conversions.e2e.test.ts` and
`validate-json-failure-conversions.e2e.test.ts` auto-merged: `main`
changed other regions of both, and the `box` fixture line and its
docblock sentence survived. The branch's delta against `main` is exactly
the 7 intended files. `main` has since gained one commit (`cd901d7a5f`),
which touches none of them.
2. **The correction moved into this PR's changeset.** The released note
is `@objectstack/console` 17.5.0, patch entry `28ce612`. A new paragraph
goes through its closing paragraph one sentence at a time:
- two sentences stop being true with this release: the file is no longer
"only present in the tarball", and the CLI fallback no longer "keeps
parse-level validation";
   - two still hold: `exports` is unchanged.

No `CHANGELOG.md`, no `content/docs/releases/` and nothing under
`packages/spec/` is edited.
3. **The ADR-0087 marker** now reads `not-required (already-registered
ui-html-page-div-refused)` with its reason. The gate's verdict:
"check-adr-0087-registration: 1 declared-breaking changeset(s), each
carrying an ADR-0087 disposition … not-required (already-registered)",
exit 0.
4. **Two sentences re-measured and corrected:**
- **"the html-tier renderer still renders `div`" was false.** At the
pinned objectui `dd3f7e1be3`, read with `git show` from the sibling
checkout (nothing checked out, nothing edited):
- `packages/components/src/renderers/layout/page.tsx:487-488` builds the
html compile's whitelist from `getKnownTypes()` minus `deprecationFor(t,
'html')`;
- `packages/components/src/renderers/basic/div.tsx` registers `div` with
`deprecated.surfaces: ['json', 'html']`;
- `nameHtmlTierReplacement` turns the resulting `forbidden-tag` into a
refusal naming the replacement.

That pin shipped in `@objectstack/console` 17.5.0: its CHANGELOG entry
`3cf6449` says a `kind:'html'` page that authors a `div` "is refused at
compile time, and the error names `box`". The changeset now says the
console has refused `div` since 17.5.0, and that what is new is every
other tag the manifest does not declare. The console's html compile
accepts every non-deprecated registered component, while the manifest
declares the public contract plus the html intrinsics. Measured below
with `avatar`.
- **"`objectstack compile` (which `dev` and `start` run first)" was
inexact.** It now says exactly when they run it: `dev.ts:319` compiles
on `flags.compile` or a missing artifact, and `dev.ts:383` re-runs it in
watch mode; `start.ts:228-232` has the same condition.

## Premise and hypotheses, measured

Round-1 readings (on `f11b5f20a2`) are kept where they still hold.
Round-2 readings are on `77338a7186`: Node v26.7.0, macOS.

- **H0 (premise holds).** On unmodified `f11b5f20a2`, a real `os init`
project with a `kind: 'html'` page rooted in `div` passes `os validate`,
`os compile` and `os lint` at exit 0. Each prints only the parse-level
notice, and it does so even with a `cmp`-identical copy of the tracked
manifest at `packages/console/dist/sdui.manifest.json`. From
`packages/cli/dist`, the old subpath throws
`ERR_PACKAGE_PATH_NOT_EXPORTED`.
- **H1 (the route finds the file in both layouts).**
- Workspace: `consoleSduiManifestPath()` answers
`packages/console/dist/sdui.manifest.json`.
- Installed package: `npm pack` of `packages/console` with a stand-in
dist lists `dist/sdui.manifest.json`. Extracted under a scratch
`node_modules`, the old subpath throws `ERR_PACKAGE_PATH_NOT_EXPORTED`
from a sibling CLI origin, while `resolveSduiManifest` answers
`resolved`.
- **H2 (Clause-② arm: narrowing).** Round 2, merged tree, with the
console copy present (`cmp`-identical stand-in):
- a `div` page gives exit 1 (`jsx-forbidden-tag`,
`jsx-unknown-component`);
  - a `box` page gives exit 0, with no findings;
- an `avatar` page gives exit 1 (`jsx-forbidden-tag`,
`jsx-unknown-component`).

With no copy, all three exit 0 with the notice only. `avatar` is
registered at the pin (`renderers/data-display/avatar.tsx:17`) and not
deprecated, so the console's html compile renders it and nothing refused
it before this change. That is the narrowing the `(narrowing)` arm and
BREAKING rest on.
- ⚠️ **Round 1 misread this half.** It took "the renderer still renders
`div`" from ruling A's reading, which predates objectui#10757, instead
of reading the pin. The pin had landed on `main` (objectstack-ai#20436) before round 1
ran. For `div`, this change moves a refusal the 17.5.0 console already
gives at render time to author time. The arm still holds because of the
undeclared tags.
- **CLI fixtures the live fallback newly refuses** (round 1, with the
console copy present): 23 tests went red across
`build-json-failure-conversions.e2e` (5),
`validate-json-failure-conversions.e2e` (4) and
`jsx-gate-manifest-notice.e2e` (14). `lint-conversion-notices.e2e`
stayed green, but its page is refused too. The three conversion fixtures
moved from `div` to `box`. The notice file's 14 manifest-less cases are
skipped by name where the CLI's own console copy exists. They run in the
CI job, which builds no console, and their rules are pinned hermetically
in `src/utils/sdui-manifest.test.ts`.
- **Examples:** only `examples/app-showcase` carries html pages (three).
- **H3 (shipped pages stay clean)**, round 2, merged tree. `main`
brought a regenerated `sdui.manifest.json` carrying `tier: 'html'` marks
(objectstack-ai#20582).
  - `validate-jsx-pages.production-witness.test.ts`: 5/5 pass.
- `examples/app-showcase` with the console copy present: exit 0 on `os
validate` / `os compile` / `os lint`, with zero `jsx-*` / `sdui/*`
findings.
- **H4 (ablation, round 1)**, through `node
scripts/ablation-replace.mjs` in WRAP mode:
- the anchor `resolve(CONSOLE_PACKAGE_JSON)` went 1 → 0, and
`resolve(CONSOLE_SDUI_MANIFEST)` (the old subpath) 0 → 1;
  - 4 console-leg pins went red ("expected undefined to be defined");
- restore: the blob is back at the HEAD blob `be1f8d4ad33e`, and `git
diff HEAD` is empty.

The pins import the subject from `src/`, so no `dist/` sits on that
path. This round changed no source or test file.

## Tests, at `77338a7186`

- The whole CLI `unit` project (`--project unit --maxWorkers=2`) with a
real-path `TMPDIR`: 234/234 files, 3347/3347 tests. With the default
macOS `TMPDIR`: 232/234. The 2 files are `published-subpath-console.pin`
and `published-subpath-hook-body.pin`, 5 cases comparing `/var` against
`/private/var`. They are host-only and untouched here.
- `pnpm --filter @objectstack/cli typecheck` (`tsc --noEmit` plus
`check:test-typecheck`): exit 0.
- The four touched nightly `*.e2e` files (`OS_TEST_TIERS=nightly
--project integration`):
  - with the console copy present: 53 passed, 14 skipped;
  - without it (the CI state): 67/67 passed.

  The rest of the integration layer is declared to CI.

## Gates, at `77338a7186`

- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`: 63 derived, the same 63 as round 1. All 63 exit 0, and
`--ran` reconciled "63 run, 0 NOT-MEASURED (a DERIVED zero — all 63
recorded an exit code and none of them is 3)".
`check:dual-build-cjs-loads` first answered PREREQUISITE NOT MET, then
exit 0 after building its eight missing packages.
- `check-adr-0087-registration --base origin/main`,
`check-empty-changeset --base origin/main` (it now reads "No changeset
from the merge base modified or deleted by this diff") and
`check-changeset-no-major --base origin/main`: all exit 0.
- Roster rows that could apply, all exit 0: `check-changeset-fixed`,
`check-sdui-manifest` (plus `--self-test`), `check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`,
`check:cli-examples-parity`, `check:published-readme-exports`,
`check:scaffold-emission-policy`, `check:console-injection`.
- `pnpm lint` (full repo, not narrowed): exit 0, no output.
- `node scripts/check-issue-citations.mjs --base origin/main`: exit 0.
- `check:nul-bytes`: exit 0, plus a control-byte scan of the 7 changed
files: 0.

**Declared narrowing — verification ran UNLOCKED.**
`scripts/pm/os-verify-lock.sh`
could not take the shared verify lock on this host: no usable `flock`.
The shared
verify lock is declared Linux-only (`flock` is util-linux, and a stock
macOS does
not ship it), so the command below was run directly, without the lock —
a declared narrowing, not a silent one. No serialization guarantee held
for this
run, nor for any sibling agent in this container while it ran.

every build, test, typecheck, ablation and `pnpm lint` command named
above

## Acceptance notes

- **Where the `div` → `box` prescription reaches an upgrader.** The
CLI's refusal text does not carry it: the gate answers "is not an
allowed component" / "is not a known component", from
`@objectstack/sdui-parser` (`parse.ts`). The upgrade guide does not
carry it either: `packages/spec/scripts/build-upgrade-guide.ts:78` loops
majors up to `PROTOCOL_MAJOR`, `PROTOCOL_VERSION` is `17.0.0`, and
`docs/protocol-upgrade-guide.md` does not name
`ui-html-page-div-refused`. What does carry it:
  - this changeset's FROM → TO table;
  - the console's own render-time refusal, which names `box`;
- `objectstack migrate meta --from 17`. Measured on a stack with a `div`
page, it lists the entry as one of 242 "manual change(s) require your
judgment", headed "⚠ [protocol 18] kind:'html' page source …", with
`box` as the replacement, and exits 0.
- The ledger entry's own `why` text
(`packages/spec/src/migrations/entries/semantic/18.ui-html-page-div-refused.ts`,
the spec seat's file) still says "`objectstack compile` (which `dev` and
`start` run first)", the phrasing corrected here. Noted, not edited.
- Release order, flagged by the seat in `5890591366`: Version Packages
PR objectstack-ai#20639 carries the ledger entry's changeset. If it merges before this
PR, the ledger row ships one release ahead of the CLI refusal it
describes.
- `packages/cli/src/utils/scaffold-validate.ts` (the note at :128-:133)
was re-read. It is true now, so it is not edited. A pre-existing
imprecision stays as it was: `os init` reads the invoker's directory,
which may carry its own `sdui.manifest.json` (this repository's root
does).
- Comment drift outside this claim, noted only:
- `.github/workflows/lint.yml` (:899) and
`scripts/check-sdui-manifest.mjs` (:28-30, :240) still say
`resolveSduiManifest()` degrades to parse-only silently;
- the header of `packages/lint/src/validate-jsx-pages.ts` still calls
manifest validation "not wired";
- `docs/qa/platform-checklist/areas/studio-authoring.json` describes the
showcase tree as flex/div/a.
- The five macOS-only `published-subpath-*` failures come from a
`tmpdir()` path compared with the real path that module resolution
returns. They are host-specific.
- Measurement scaffolding was all in scratch, or in this worktree's
gitignored `packages/console/dist/`, with each stand-in trap-removed.
`git status --porcelain` printed 0 lines after every run.

---
_Generated by [Claude
Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_

---------

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
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/m tooling

Projects

None yet

2 participants