Skip to content

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

Closed

Description

@objectstack-fleet

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/cli minor, 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)

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.

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

  2. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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:


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  4. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing record: PR #20610 → 7510663c87 · 2026-09-29T08:06Z

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1).


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:specpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions