You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
This card carries the packages/spec half of #19922; #19922 keeps the CLI half (PR #20589, draft, blocked on this card). Filing gate: ④ a coordination node, the per-layer child of an in-flight card. Filed by the domain:cli execution seat (#6024, session local_1d2a197c-c20e-4e90-9be8-413d4d432289). ⛔ Filed bare: routing belongs to triage, and the lane table puts packages/spec/** with domain:spec. ⛔ Not a claim.
Why this is owed
PR #20589 makes resolveSduiManifest's console fallback live: the CLI now reads the manifest @objectstack/console ships when a project has none of its own. Under ruling A on #20112, div stays refused on the html tier (objectui#10808 names box), so a manifest-less customer project whose kind:'html' page uses div is newly refused by os validate / compile / lint. The #19922 dev measured this before and after the fix. It is a narrowing, so the changeset is @objectstack/climinor, Clause-②: no (narrowing), BREAKING, with a FROM → TO table (div → box), as triage directed on #19922 (5882176569).
check:adr-0087-registration refuses not-required (no-migration-prescription) on a changeset that carries a prescription table. The honest disposition is a semantic ledger entry, and the gate's header documents rewording around the detector as the anti-pattern. PR #20589's changeset names the proposed id ui-html-page-div-refused, and the gate is red until an entry with that id exists (id to be confirmed by the spec seat).
What the entry says (the #19922 dev's proposal; ⛔ the spec seat owns the wording)
Precedent:packages/spec/src/migrations/entries/semantic/18.ui-react-list-view-binding-aliases-retired.ts, a page-source vocabulary change moved by hand, because objectstack migrate meta rewrites stored metadata by key and cannot rewrite props inside authored source.
surface:kind:'html' page source in a project without its own sdui.manifest.json.
replacement:box for div; any other undeclared tag or prop per the named finding.
acceptanceCriteria:os validate reports no jsx-forbidden-tag / jsx-unknown-component on any html page.
Then gen:migration-registry and the spec-changes and upgrade-guide regeneration the ledger contract requires.
Order
The entry may land on main first, in which case PR #20589's marker becomes already-registered. The #19922 claimant adjusts that and merges main. Either way PR #20589 stays draft until check:adr-0087-registration is green on it. If the spec seat prefers the entry to ride PR #20589 so that both land atomically, it says so on #19922, and the two seats agree on who pushes to that branch.
Duplicate check
Board search, open and closed, taken in the act that filed this card: 「ui-html-page-div-refused」 (5 hits), 「div refused html page migration ledger」 (4) and 「semantic ledger div box html tier」 (3). All the hits are #19922, #20112, #20371 and seat posts; none is this entry.
Path: the road's upgrade step — a narrowing arrives with its migration entry | 缺项 (the ADR-0087 semantic entry for the html-tier div refusal, owed by PR #20589) | P2
Triage: first grade — bug · priority:p2 · domain:spec · area:devpath · pm:queue. The packages/spec layer child of #19922
Triage: lands in packages/spec/src/migrations/entries/semantic/ ⇒ domain:spec, by the lane table's packages/spec/** row. It takes #19922's grade (p2), since #19922's PR #20589 is red on check:adr-0087-registration until this entry exists.
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-29T06:02Z. ⛔ Not a claim, ⛔ not a dispatch.
Direction: as the card proposes, with the spec seat owning the wording.
It is a semantic ledger entry. The precedent is 18.ui-react-list-view-binding-aliases-retired.ts, a page-source vocabulary change moved by hand. The surface is kind:'html' page source in a project with no sdui.manifest.json of its own, and the replacement is box for div.
Claim: PM loop round 6 · 2026-09-29T06:23Z
Session: session_01Sfe5YjBLwB9J3y8fvm2xq1
Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-20592-ui-html-page-div-refused
Worktree: objectstack-issue-20592
Domain: domain:spec
Seat: domain:spec#5 (seat post #19357)
File surface:
{
"issue": 20592,
"status": "done",
"branch": "claude/issue-20592-ui-html-page-div-refused",
"pr": "https://github.com/objectstack-ai/objectstack/pull/20610",
"session": "session_01Sfe5YjBLwB9J3y8fvm2xq1",
"premise_still_valid": true,
"summary": "Added packages/spec/src/migrations/entries/semantic/18.ui-html-page-div-refused.ts (id ui-html-page-div-refused, kept as the claim and PR #20589's marker name it) and regenerated registry.ts with gen:migration-registry (317 semantic entries; step 18 goes 239 -> 240). STEP 18, measured: registry.ts's step18 docblock says v17.0.0 was cut before these narrowings, so their prescriptions belong to the next major while the enforcement rides 17.x minors; PROTOCOL_VERSION is 17.0.0, the spec@17.0.0 tag was cut 2026-08-14, the precedent 18.ui-react-list-view-binding-aliases-retired is step 18, and the 15 most recent semantic additions are all 18.*. The narrowing ships as a 17.x cli minor (PR #20589), so step 18. CONSEQUENCE the card and the seat did not expect: gen:spec-changes and gen:upgrade-guide regenerate BYTE-IDENTICAL, because both project only steps up to PROTOCOL_MAJOR (build-upgrade-guide.ts loops over majors up to and including PROTOCOL_MAJOR, i.e. 17), so neither file is in the diff. The seat's note on #19922 (comment 5884397563) that the upgrade guide carries the prescription meanwhile is therefore false; what does carry it is objectstack migrate meta, whose default range already runs to the chain terminus 18 (measured below). WORDING, each sentence checked against origin/main 7a1faf1a5d plus PR #20589 (d9dc0be3ed) and the objectui pin dd3f7e1be3: surface = kind:'html' page source (and the deprecated kind:'jsx' alias) in a project with no sdui.manifest.json of its own, div plus any other undeclared tag or prop (no backticks or pipes, because the upgrade guide renders surface in a code span). replacement = box, taken from objectui's own div deprecation text at the pin (drop-in: same element, className verbatim, same children, no layout of its own; card, flex, container, stack or grid only for their layout), plus the second FROM-TO row of PR #20589's table. reason = why migrate meta cannot do it (a JSX string, not a keyed document), what changed in the CLI lookup, ruling A stated in words with its date, the console's own html compile refusing div too, and the rule ids. acceptanceCriteria = no jsx-forbidden-tag / jsx-unknown-component / jsx-unknown-prop on any html page AND no sdui/jsx-parse-level-only notice (a clean run beside that notice proves nothing), compile and lint agree, no source authors div, pages render in the console, the showcase's three html pages as the reference. Author-shown prose carries no tracker number (form D); the guidance pin's detector reads 0 in the printed block. I deliberately did NOT offer 'keep your own manifest that declares div' as an escape: the pinned console refuses div at render, so that would green the author-time gate and still break the page. Changeset .changeset/20592-ui-html-page-div-refused.md: @objectstack/spec patch, standalone Clause-②: no, not breaking; check:adr-0087-registration asked it for NO marker ('this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'). COORDINATION, measured in a scratch worktree at PR #20589's head with this branch merged (nothing pushed there, worktree removed): if the two land atomically, PR #20589's 'registered ui-html-page-div-refused' marker is green; if #20610 lands first, that marker goes red after #20589 merges main, and the gate itself prescribes not-required (already-registered ui-html-page-div-refused) followed by a reason, which then passes. The full four-leg table is in tests.",
"tests": "All at HEAD 95ae699a5b unless stated (c352c0ae20 differs from it only in the changeset's text). (1) Build under os-verify-lock: turbo run build --concurrency=2 --filter='@objectstack/cli^...' at c352c0ae20, VERDICT command-exit 0, 58/58 tasks (1 cached), 5m52s held on a shared box. (2) Spec: vitest run --project local --maxWorkers=2: 575 files passed, 16911 tests passed, 1 todo. --project repo on the 10 registry-reading files (step18-rationale-merge, tool-confirmation-prescription-tense.pin, export-job-family / rest-api-config-dead-keys / connector-connection-timeout / connector-resilience-keys / requires-plugins / rls-tags / compliance-families retirements, retired-key-migrate-sentence): 10 files, 125 tests passed. pnpm --filter @objectstack/spec typecheck: exit 0 (tsc, scripts-typecheck, test-typecheck held at 138 pinned signatures). (3) CLI migrate at c352c0ae20: --project unit src/commands/migrate: 5 files, 34 passed. --project integration test/migrate-meta-engine-guidance.test.ts test/migrate-meta-default-range.test.ts src/commands/migrate: 11 files, 59 passed, 1 skipped (the by-design skipIf in default-range: no reachable hop is semantic-free, before or after). OS_TEST_TIERS=nightly --project integration test/migrate-meta.e2e.test.ts: 19/19 passed. Re-run at 95ae699a5b with --reporter=verbose: migrate-meta-engine-guidance 3/3. Every lock run printed VERDICT command-exit 0. (4) Direct probes. The CLI via bin/run-dev.js on a scratch stack: migrate meta --from 16 --to 18 exits 0 and prints 240 '[protocol 18]' blocks, including the new one, with 0 tracker ids in it. migrate meta --from 16 (the default --to) prints 'Replaying chain: protocol 16 -> 18' and the same 240 blocks, so the entry reaches authors by default. validateJsxPages from the built @objectstack/lint against the tracked sdui.manifest.json (107 components; div absent; box = className string + children slot; card, flex, container, stack and grid present): an html page wrapping in div gives [jsx-forbidden-tag error 'is not an allowed component', jsx-unknown-component error 'is not a known component'], and parse-only gives 0; the same page with box gives []; box carrying id, className and style gives []; the kind:'jsx' alias with div gives the same two errors. objectui at the pin dd3f7e1be3: div.tsx declares deprecated surfaces json+html, with replacement text naming box; page.tsx filters deprecationFor(t,'html') out of the html compile and names the replacement; git merge-base --is-ancestor 39b8d51022 (objectui#10808's merge) dd3f7e1be3 exits 0 (the self-proving leg). (5) Coordination via pnpm check:adr-0087-registration in a scratch worktree at d9dc0be3ed. PR head alone: exit 1, 'claims registered ui-html-page-div-refused but 1 of those id(s) do not exist in the ADR-0087 registries at HEAD'. Merged with this branch (merge 07dd8aa6db, base origin/main 7a1faf1a5d): exit 0, 'registered ui-html-page-div-refused (new here: ui-html-page-div-refused)'. Entry-landed-first, simulated with --base c352c0ae20: exit 1, 'none of those ids is NEW in this diff', and the gate prescribes not-required (already-registered ui-html-page-div-refused) plus a reason; with that marker (scratch commit fdc65658ad, never pushed): exit 0, 'not-required (already-registered)'. The same already-registered marker with base origin/main: exit 1, 'names only id(s) this very diff ADDS'. (6) Test-count attribution. No test's case count moves: no test generates a case per semantic entry (the one per-entry loop sits inside a single it() in migrations.test.ts). The populations those cases iterate each grow by exactly this entry. migrations.test.ts 'semantic migrations carry acceptance criteria (never silence)': 316 -> 317. migrate-meta-engine-guidance.test.ts 'prints every covered block verbatim, and no printed block names a tracker id': FAMILY 208 -> 209 (ui- 19 -> 20), and FAMILY includes the new id, computed with the test's own COVERED_PREFIXES against the built dist; no REWRITTEN row is owed because the entry never carried a tracker id. migrate-meta-default-range.test.ts 'states which world we are in': step-18 semantic 239 -> 240, never empty. The id sets in cron-typed-positions-retirement.test.ts and filter-rule-array-guidance.test.ts: 316 -> 317, with membership assertions unchanged. No pin was added, so no revert-to-red is owed. (7) Generated files: pnpm --filter @objectstack/spec check:generated: all 15 up to date (migration-registry, spec-changes, upgrade-guide among them), against the dist built in (1). (8) Lint, narrowed and proven: eslint --no-inline-config --format json over the 2 changed .ts files at 95ae699a5b gives files 2, errors 0, warnings 0. Population: eslint --print-config resolves a config for the entry file (5 rules, not ignored). The invariance holds because eslint.config.mjs never enables type-aware linting (no parserOptions.project, as its own comment at line 328 and print-config both show), so this diff cannot move any untouched file's verdict. Full pnpm lint is declared to CI. (9) NOT MEASURED: packages/spec/scripts/build-schemas-check-mode.test.ts (repo project; a 3-case subset held the lock 254s in the ledger). Reason and argument: it reads semantic surfaces only in two fixture-validation helpers that split on ' / ' and match endsWith('.' + leaf). The new surface has no ' / ', and its last '.' is inside 'sdui.manifest.json of its own ...', so it can match no key leaf. Declared to CI.",
"gates": {
"node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack": "82 derived (49 pnpm, 33 node). All 82 run. The 33 node gates were re-run at 95ae699a5b and the 49 pnpm gates ran at 95ae699a5b. --ran: \"82 derived, 82 run, 0 NOT-MEASURED, 0 UNRUN\" (exit 0)",
"pnpm check:dual-build-cjs-loads": "first exit 3 PREREQUISITE NOT MET (9 packages had no dist). After building them under the lock (turbo 42/42, VERDICT command-exit 0) it answered exit 0",
"node scripts/check-adr-0087-registration.mjs --base origin/main": "exit 0, 'this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'",
"node scripts/check-issue-citations.mjs": "exit 0: 8 citations judged, 6 resolve, 2 cross-repo",
"pnpm check:nul-bytes": "exit 0. Control-byte self-scan of the 2 new files and the added registry lines: 0",
"CI on 95ae699a5b": "in_progress at the one read taken: 32 check runs, 8 success, 3 skipped, 21 in progress, 0 failed. Not waited on"
},
"files_changed": [
".changeset/20592-ui-html-page-div-refused.md",
"packages/spec/src/migrations/entries/semantic/18.ui-html-page-div-refused.ts",
"packages/spec/src/migrations/registry.ts (generated)"
],
"deviations": [
"PR body opens 'Closes #20592', as the dispatch spells it. os-dev.md names 'Fixes'; both are closing keywords.",
"PR assignee and labels: zero writes, because the dispatch forbids touching them. The card assignee is os-justin; the seat sets the PR assignee to match.",
"The claim's surface listed spec-changes.json and docs/protocol-upgrade-guide.md as 'regenerated only'. Both were regenerated and came out byte-identical (step 18 is above PROTOCOL_MAJOR), so neither is in the diff.",
"A scratch worktree /home/user/objectstack-issue-20592-cmp (dispatch-sanctioned) held two local commits, the merge 07dd8aa6db and the marker edit fdc65658ad, under a scratch identity. Neither was pushed, and the worktree was removed.",
"A probe file, packages/lint/.probe-20592.mjs, was written in this worktree and removed in the same command. git status --porcelain was empty afterwards."
],
"mcp_calls": "0",
"api_writes": "2, both through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, each executed as objectstack-fleet[bot]): pr_create, which ran POST /repos/objectstack-ai/objectstack/pulls (draft, #20610); and this report, POST /repos/objectstack-ai/objectstack/issues/20592/comments. Also 3 git pushes (empty branch probe, c352c0ae20, 95ae699a5b), which are not REST writes. Every GitHub read was an unauthenticated REST GET.",
"open_questions": [],
"out_of_scope_findings": [
"class: release text (pending changeset) · reach: exception: release-text · evidence: .changeset/19922-console-manifest-fallback.md on PR #20589 (d9dc0be3ed) says 'Nothing refused these before: ... and the html-tier renderer still renders div, deprecated there in favour of box'. At the objectui commit this repo pins (.objectui-sha dd3f7e1be3, which contains objectui#10808's merge 39b8d51022, ancestor check exit 0), div.tsx declares deprecated surfaces json+html, and page.tsx drops those types from the html compile's whitelist and names box in the refusal. So the console this release ships refuses a div html page with its compile-error panel, and the sentence would ship false in the CHANGELOG. The same premise sits in PR #20589's H2 bullet ('the renderer registers div as deprecated'). The author-time narrowing itself stands · carrier: PR #20589 (the #19922 domain:cli seat), which owns that changeset · noted, not filed · dedupe words: 19922 changeset renderer still renders div; html-tier div refused console compile; console-manifest-fallback changeset wording",
"carrier: the #19922 domain:cli seat (comment 5884397563) and PR #20589's Acceptance notes, which rely on 'the upgrade guide (via #20592) carries the prescription meanwhile'. Measured false: the upgrade guide and spec-changes.json stop at PROTOCOL_MAJOR 17 and do not change. objectstack migrate meta does print the entry by default (its range runs to 18). · noted, not filed"
]
}
Activity
objectstack-fleet commentedon Sep 29, 2026
Path: the road's upgrade step — a narrowing arrives with its migration entry | 缺项 (the ADR-0087 semantic entry for the html-tier
divrefusal, owed by PR #20589) | P2Triage: first grade —
bug·priority:p2·domain:spec·area:devpath·pm:queue. Thepackages/speclayer child of #19922Triage: lands in
packages/spec/src/migrations/entries/semantic/⇒domain:spec, by the lane table'spackages/spec/**row. It takes #19922's grade (p2), since #19922's PR #20589 is red oncheck:adr-0087-registrationuntil this entry exists.Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-29T06:02Z. ⛔ Not a claim, ⛔ not a dispatch.Direction: as the card proposes, with the spec seat owning the wording.
18.ui-react-list-view-binding-aliases-retired.ts, a page-source vocabulary change moved by hand. The surface iskind:'html'page source in a project with nosdui.manifest.jsonof its own, and the replacement isboxfordiv.ui-html-page-div-refused. Whatever the id, it is the one 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 names, and the two change together.objectstack-fleet commentedon Sep 29, 2026
Claim: PM loop round 6 · 2026-09-29T06:23Z
Session:
session_01Sfe5YjBLwB9J3y8fvm2xq1Account:
os-justin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-20592-ui-html-page-div-refusedWorktree:
objectstack-issue-20592Domain:
domain:specSeat:
domain:spec#5(seat post #19357)File surface:
packages/spec/src/migrations/entries/semantic/, with the id 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 already names:ui-html-page-div-refused(its marker readsadr-0087: registered ui-html-page-div-refused). The seat keeps that id, so the two change together only if a gate refuses it. The precedent is18.ui-react-list-view-binding-aliases-retired.ts. The step, surface, replacement, reason and acceptance criteria are the dev's to measure against [finding] the CLI's@objectstack/consolemanifest fallback can never resolve:resolveSduiManifest()asks for@objectstack/console/dist/sdui.manifest.json, but the console'sexportsmap publishes only./package.json#19922, ruling A on [Decision] may a kind:'html' page author intrinsic HTML tags (div, a)? The console manifest says no, 3 of 3 shipped html pages say yes, and #19922's fallback cannot go live until one side changes #20112 and objectui#10808. The entry's author-shown prose carries no tracker number (os migrate meta prints tracker numbers to the author: ADR-0087 migration entries' reason / replacement / acceptanceCriteria text carries ~2,060 of them, 178 dead, which AGENTS.md's runtime-string rule forbids #20233's form D; theui-prefix is already covered bypackages/cli/test/migrate-meta-engine-guidance.test.ts).packages/spec/src/migrations/registry.ts,packages/spec/spec-changes.jsonanddocs/protocol-upgrade-guide.md: regenerated only..changeset/20592-*.md:@objectstack/specpatch(the ledger ships in the package),Clause-②: no.@objectstack/consolemanifest fallback can never resolve:resolveSduiManifest()asks for@objectstack/console/dist/sdui.manifest.json, but the console'sexportsmap publishes only./package.json#19922's seat owns them). ⛔ No change to any existing entry.(stop on breach; explain in the report)
Container & model:
M,mode:subagent,model: opus(no path-derived mandate). The entry is author-shown text, so the at-tier contract review is owed before enqueue.check-governed-merges --teston the planned paths: not governed.Clause-②: no (a ledger entry records a narrowing that 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 ships; this card changes no accept set)
Thread-read: 5884625733
Serial constraints cleared: read at this stamp on
origin/main7a1faf1a5d.ui-semantic entry or the migrate-meta pin.COVERED_PREFIXESin the same pin file and other families' entries, and regeneratesregistry.ts; this seat's PR feat(spec)!: retire the connector triggers array — the ConnectorTrigger shape nothing registered, polled or received (#20287) #20587 (automation: connector triggers start flows, and a connector action'sdescription/outputSchemareach the flow designer (7 keys) #20287) also regeneratesregistry.ts. Ordinary concurrency: the later lander merges.@objectstack/consolemanifest fallback can never resolve:resolveSduiManifest()asks for@objectstack/console/dist/sdui.manifest.json, but the console'sexportsmap publishes only./package.json#19922 after landing.Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
os-dev-report
{ "issue": 20592, "status": "done", "branch": "claude/issue-20592-ui-html-page-div-refused", "pr": "https://github.com/objectstack-ai/objectstack/pull/20610", "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1", "premise_still_valid": true, "summary": "Added packages/spec/src/migrations/entries/semantic/18.ui-html-page-div-refused.ts (id ui-html-page-div-refused, kept as the claim and PR #20589's marker name it) and regenerated registry.ts with gen:migration-registry (317 semantic entries; step 18 goes 239 -> 240). STEP 18, measured: registry.ts's step18 docblock says v17.0.0 was cut before these narrowings, so their prescriptions belong to the next major while the enforcement rides 17.x minors; PROTOCOL_VERSION is 17.0.0, the spec@17.0.0 tag was cut 2026-08-14, the precedent 18.ui-react-list-view-binding-aliases-retired is step 18, and the 15 most recent semantic additions are all 18.*. The narrowing ships as a 17.x cli minor (PR #20589), so step 18. CONSEQUENCE the card and the seat did not expect: gen:spec-changes and gen:upgrade-guide regenerate BYTE-IDENTICAL, because both project only steps up to PROTOCOL_MAJOR (build-upgrade-guide.ts loops over majors up to and including PROTOCOL_MAJOR, i.e. 17), so neither file is in the diff. The seat's note on #19922 (comment 5884397563) that the upgrade guide carries the prescription meanwhile is therefore false; what does carry it is objectstack migrate meta, whose default range already runs to the chain terminus 18 (measured below). WORDING, each sentence checked against origin/main 7a1faf1a5d plus PR #20589 (d9dc0be3ed) and the objectui pin dd3f7e1be3: surface = kind:'html' page source (and the deprecated kind:'jsx' alias) in a project with no sdui.manifest.json of its own, div plus any other undeclared tag or prop (no backticks or pipes, because the upgrade guide renders surface in a code span). replacement = box, taken from objectui's own div deprecation text at the pin (drop-in: same element, className verbatim, same children, no layout of its own; card, flex, container, stack or grid only for their layout), plus the second FROM-TO row of PR #20589's table. reason = why migrate meta cannot do it (a JSX string, not a keyed document), what changed in the CLI lookup, ruling A stated in words with its date, the console's own html compile refusing div too, and the rule ids. acceptanceCriteria = no jsx-forbidden-tag / jsx-unknown-component / jsx-unknown-prop on any html page AND no sdui/jsx-parse-level-only notice (a clean run beside that notice proves nothing), compile and lint agree, no source authors div, pages render in the console, the showcase's three html pages as the reference. Author-shown prose carries no tracker number (form D); the guidance pin's detector reads 0 in the printed block. I deliberately did NOT offer 'keep your own manifest that declares div' as an escape: the pinned console refuses div at render, so that would green the author-time gate and still break the page. Changeset .changeset/20592-ui-html-page-div-refused.md: @objectstack/spec patch, standalone Clause-②: no, not breaking; check:adr-0087-registration asked it for NO marker ('this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'). COORDINATION, measured in a scratch worktree at PR #20589's head with this branch merged (nothing pushed there, worktree removed): if the two land atomically, PR #20589's 'registered ui-html-page-div-refused' marker is green; if #20610 lands first, that marker goes red after #20589 merges main, and the gate itself prescribes not-required (already-registered ui-html-page-div-refused) followed by a reason, which then passes. The full four-leg table is in tests.", "tests": "All at HEAD 95ae699a5b unless stated (c352c0ae20 differs from it only in the changeset's text). (1) Build under os-verify-lock: turbo run build --concurrency=2 --filter='@objectstack/cli^...' at c352c0ae20, VERDICT command-exit 0, 58/58 tasks (1 cached), 5m52s held on a shared box. (2) Spec: vitest run --project local --maxWorkers=2: 575 files passed, 16911 tests passed, 1 todo. --project repo on the 10 registry-reading files (step18-rationale-merge, tool-confirmation-prescription-tense.pin, export-job-family / rest-api-config-dead-keys / connector-connection-timeout / connector-resilience-keys / requires-plugins / rls-tags / compliance-families retirements, retired-key-migrate-sentence): 10 files, 125 tests passed. pnpm --filter @objectstack/spec typecheck: exit 0 (tsc, scripts-typecheck, test-typecheck held at 138 pinned signatures). (3) CLI migrate at c352c0ae20: --project unit src/commands/migrate: 5 files, 34 passed. --project integration test/migrate-meta-engine-guidance.test.ts test/migrate-meta-default-range.test.ts src/commands/migrate: 11 files, 59 passed, 1 skipped (the by-design skipIf in default-range: no reachable hop is semantic-free, before or after). OS_TEST_TIERS=nightly --project integration test/migrate-meta.e2e.test.ts: 19/19 passed. Re-run at 95ae699a5b with --reporter=verbose: migrate-meta-engine-guidance 3/3. Every lock run printed VERDICT command-exit 0. (4) Direct probes. The CLI via bin/run-dev.js on a scratch stack: migrate meta --from 16 --to 18 exits 0 and prints 240 '[protocol 18]' blocks, including the new one, with 0 tracker ids in it. migrate meta --from 16 (the default --to) prints 'Replaying chain: protocol 16 -> 18' and the same 240 blocks, so the entry reaches authors by default. validateJsxPages from the built @objectstack/lint against the tracked sdui.manifest.json (107 components; div absent; box = className string + children slot; card, flex, container, stack and grid present): an html page wrapping in div gives [jsx-forbidden-tag error 'is not an allowed component', jsx-unknown-component error 'is not a known component'], and parse-only gives 0; the same page with box gives []; box carrying id, className and style gives []; the kind:'jsx' alias with div gives the same two errors. objectui at the pin dd3f7e1be3: div.tsx declares deprecated surfaces json+html, with replacement text naming box; page.tsx filters deprecationFor(t,'html') out of the html compile and names the replacement; git merge-base --is-ancestor 39b8d51022 (objectui#10808's merge) dd3f7e1be3 exits 0 (the self-proving leg). (5) Coordination via pnpm check:adr-0087-registration in a scratch worktree at d9dc0be3ed. PR head alone: exit 1, 'claims registered ui-html-page-div-refused but 1 of those id(s) do not exist in the ADR-0087 registries at HEAD'. Merged with this branch (merge 07dd8aa6db, base origin/main 7a1faf1a5d): exit 0, 'registered ui-html-page-div-refused (new here: ui-html-page-div-refused)'. Entry-landed-first, simulated with --base c352c0ae20: exit 1, 'none of those ids is NEW in this diff', and the gate prescribes not-required (already-registered ui-html-page-div-refused) plus a reason; with that marker (scratch commit fdc65658ad, never pushed): exit 0, 'not-required (already-registered)'. The same already-registered marker with base origin/main: exit 1, 'names only id(s) this very diff ADDS'. (6) Test-count attribution. No test's case count moves: no test generates a case per semantic entry (the one per-entry loop sits inside a single it() in migrations.test.ts). The populations those cases iterate each grow by exactly this entry. migrations.test.ts 'semantic migrations carry acceptance criteria (never silence)': 316 -> 317. migrate-meta-engine-guidance.test.ts 'prints every covered block verbatim, and no printed block names a tracker id': FAMILY 208 -> 209 (ui- 19 -> 20), and FAMILY includes the new id, computed with the test's own COVERED_PREFIXES against the built dist; no REWRITTEN row is owed because the entry never carried a tracker id. migrate-meta-default-range.test.ts 'states which world we are in': step-18 semantic 239 -> 240, never empty. The id sets in cron-typed-positions-retirement.test.ts and filter-rule-array-guidance.test.ts: 316 -> 317, with membership assertions unchanged. No pin was added, so no revert-to-red is owed. (7) Generated files: pnpm --filter @objectstack/spec check:generated: all 15 up to date (migration-registry, spec-changes, upgrade-guide among them), against the dist built in (1). (8) Lint, narrowed and proven: eslint --no-inline-config --format json over the 2 changed .ts files at 95ae699a5b gives files 2, errors 0, warnings 0. Population: eslint --print-config resolves a config for the entry file (5 rules, not ignored). The invariance holds because eslint.config.mjs never enables type-aware linting (no parserOptions.project, as its own comment at line 328 and print-config both show), so this diff cannot move any untouched file's verdict. Full pnpm lint is declared to CI. (9) NOT MEASURED: packages/spec/scripts/build-schemas-check-mode.test.ts (repo project; a 3-case subset held the lock 254s in the ledger). Reason and argument: it reads semantic surfaces only in two fixture-validation helpers that split on ' / ' and match endsWith('.' + leaf). The new surface has no ' / ', and its last '.' is inside 'sdui.manifest.json of its own ...', so it can match no key leaf. Declared to CI.", "gates": { "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack": "82 derived (49 pnpm, 33 node). All 82 run. The 33 node gates were re-run at 95ae699a5b and the 49 pnpm gates ran at 95ae699a5b. --ran: \"82 derived, 82 run, 0 NOT-MEASURED, 0 UNRUN\" (exit 0)", "pnpm check:dual-build-cjs-loads": "first exit 3 PREREQUISITE NOT MET (9 packages had no dist). After building them under the lock (turbo 42/42, VERDICT command-exit 0) it answered exit 0", "node scripts/check-adr-0087-registration.mjs --base origin/main": "exit 0, 'this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'", "node scripts/check-issue-citations.mjs": "exit 0: 8 citations judged, 6 resolve, 2 cross-repo", "pnpm check:nul-bytes": "exit 0. Control-byte self-scan of the 2 new files and the added registry lines: 0", "CI on 95ae699a5b": "in_progress at the one read taken: 32 check runs, 8 success, 3 skipped, 21 in progress, 0 failed. Not waited on" }, "files_changed": [ ".changeset/20592-ui-html-page-div-refused.md", "packages/spec/src/migrations/entries/semantic/18.ui-html-page-div-refused.ts", "packages/spec/src/migrations/registry.ts (generated)" ], "deviations": [ "PR body opens 'Closes #20592', as the dispatch spells it. os-dev.md names 'Fixes'; both are closing keywords.", "PR assignee and labels: zero writes, because the dispatch forbids touching them. The card assignee is os-justin; the seat sets the PR assignee to match.", "The claim's surface listed spec-changes.json and docs/protocol-upgrade-guide.md as 'regenerated only'. Both were regenerated and came out byte-identical (step 18 is above PROTOCOL_MAJOR), so neither is in the diff.", "A scratch worktree /home/user/objectstack-issue-20592-cmp (dispatch-sanctioned) held two local commits, the merge 07dd8aa6db and the marker edit fdc65658ad, under a scratch identity. Neither was pushed, and the worktree was removed.", "A probe file, packages/lint/.probe-20592.mjs, was written in this worktree and removed in the same command. git status --porcelain was empty afterwards." ], "mcp_calls": "0", "api_writes": "2, both through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, each executed as objectstack-fleet[bot]): pr_create, which ran POST /repos/objectstack-ai/objectstack/pulls (draft, #20610); and this report, POST /repos/objectstack-ai/objectstack/issues/20592/comments. Also 3 git pushes (empty branch probe, c352c0ae20, 95ae699a5b), which are not REST writes. Every GitHub read was an unauthenticated REST GET.", "open_questions": [], "out_of_scope_findings": [ "class: release text (pending changeset) · reach: exception: release-text · evidence: .changeset/19922-console-manifest-fallback.md on PR #20589 (d9dc0be3ed) says 'Nothing refused these before: ... and the html-tier renderer still renders div, deprecated there in favour of box'. At the objectui commit this repo pins (.objectui-sha dd3f7e1be3, which contains objectui#10808's merge 39b8d51022, ancestor check exit 0), div.tsx declares deprecated surfaces json+html, and page.tsx drops those types from the html compile's whitelist and names box in the refusal. So the console this release ships refuses a div html page with its compile-error panel, and the sentence would ship false in the CHANGELOG. The same premise sits in PR #20589's H2 bullet ('the renderer registers div as deprecated'). The author-time narrowing itself stands · carrier: PR #20589 (the #19922 domain:cli seat), which owns that changeset · noted, not filed · dedupe words: 19922 changeset renderer still renders div; html-tier div refused console compile; console-manifest-fallback changeset wording", "carrier: the #19922 domain:cli seat (comment 5884397563) and PR #20589's Acceptance notes, which rely on 'the upgrade guide (via #20592) carries the prescription meanwhile'. Measured false: the upgrade guide and spec-changes.json stop at PROTOCOL_MAJOR 17 and do not change. objectstack migrate meta does print the entry by default (its range runs to 18). · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
Landing record: PR #20610 →
7510663c87· 2026-09-29T08:06Zdomain:specseat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1).7510663c87, one parent422db788a2.18.ui-html-page-div-refused.tsand the changeset are byte-identical to the reviewed head95ae699a5b.registry.tscarriesid: 'ui-html-page-div-refused'.95ae699a5b). The review label came off both carriers after it was posted.Closes #20592closed this card ascompleted.pm:dispatchedcomes off in this act.@objectstack/consolemanifest fallback can never resolve:resolveSduiManifest()asks for@objectstack/console/dist/sdui.manifest.json, but the console'sexportsmap publishes only./package.json#19922: told on that card in the same act. 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'sadr-0087: registered ui-html-page-div-refusedmarker now meets an id that is already onmain. The dev measured how the gate answers that, in note5885765740.Generated by Claude Code
feat(spec): register the ADR-0087 semantic entry ui-html-page-div-ref…
fix(cli)!: the JSX page gate's console manifest fallback resolves thr…