Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(types): MarkdownSchema.sanitize and .components are declared and mirrored but read by no renderer — and sanitize implies a switch that does not exist #6972

Closed

Description

@claude

Measured on main = 8f1d995ef447 while converging MarkdownSchema for #6172.

The fact

MarkdownSchema declares two members that no renderer reads:

member declared at read by
sanitize?: boolean (@default true) packages/types/src/data-display.ts nothing
components?: Record<string, any> same nothing

The Zod mirror carries both as well (packages/types/src/zod/data-display.zod.ts).

The registered markdown renderer is MarkdownRenderer in
packages/plugin-markdown/src/index.tsx. It forwards exactly two values to the
implementation — content and className:

LazyMarkdown content={schema.content || ''} className={schema.className}

MarkdownImplProps accepts only those two. Nothing anywhere destructures or
indexes schema.sanitize or schema.components.

sanitize is the more pointed of the two, because the key implies a switch that
does not exist: sanitization is unconditional. rehypeSanitize is a fixed
link in the rehype chain in MarkdownImpl.tsx, documented there as "the final
gate". Authoring sanitize: false is accepted by the type and by the Zod mirror,
changes nothing, and reads as a security-relevant control.

Why it is worth a card

ADR-0049 enforce-or-remove: declared = enforced. This is the same class as #6942
(TextSchema.variant) and #6910 (EventHandlersSchema), and it is the shape that
makes AI-authored metadata wrong in a way nothing catches: the key is offered by
the type, accepted by the validator, and silently inert.

⚠️ The population grew slightly as a side effect of #6172's convergence: with
@object-ui/plugin-markdown now re-exporting the one authority instead of
declaring its own narrower copy, both members are visible on the plugin's
published face too. That was a deliberate, recorded trade in that PR (converging
onto the canonical shape) and is called out in its body — but it does mean the
retirement, if that is the ruling, now covers two published faces rather than one.

Not fixed there: removing a published key is an ADR-0049 retirement with its own
route (registries, mirror, parity test, changeset), not a convergence.

Reproduce

grep -rn "sanitize\|components" packages/plugin-markdown/src/index.tsx packages/plugin-markdown/src/MarkdownImpl.tsx
grep -rn "schema.sanitize\|schema.components" packages/ apps/ --include=*.ts --include=*.tsx

Refs: #6172 (where this was measured), #6942 / #6910 (same class).


Generated by Claude Code

Activity

  1. huangyiirene commented on Sep 4, 2026

    @huangyiirene
    Collaborator

    Triage — first touch. Graded bug·pm:queue·priority:p2·domain:spec. Re-verified on e30ed155cb71, 03:44Z (card measured 8f1d995ef447).

    Every claim reproduces

    claim today
    sanitize?: boolean declared ✅ packages/types/src/data-display.ts:1167
    components?: Record<string, any> declared ✅ :1171
    zod mirror carries both ✅ zod/data-display.zod.ts:292 / :293
    renderer forwards only two values ✅ MarkdownImpl({ content, className }: MarkdownImplProps) @ MarkdownImpl.tsx:196
    zero readers ✅ 0, against a control of 27 files reading schema.content in the same query shape

    ⭐ "Sanitization is unconditional" — checked as control flow, not as a grep hit

    A single grep line naming rehypeSanitize would not have settled this; a plugin can be conditionally included. It is not. rehypePlugins is a module-level const array (:99–:107), built once with [rehypeSanitize, sanitizeSchema] as its last link, and handed straight to ReactMarkdown at :226. No ternary, no if, no runtime assembly.

    ⇒ There is no code path on which sanitize: false could disable anything. The key type-checks, passes zod validation, and is inert.

    Why p2 rather than the usual p3 for an inert key

    ⚠️ The failure is fail-safe — nothing is currently exploitable, since the un-disableable behaviour is the safe one. That is why this is not p1.

    But it is a declared, validated, security-shaped control that does not exist, on a surface whose whole point is AI-authored metadata. Both readings of it mislead: an author writing sanitize: false believes they turned XSS filtering off, and an author writing sanitize: true believes they turned it on. Neither is true. A key that lies about a security gate is worth more than the ordinary inert-key p3.

    ⭐ The two keys do NOT have the same disposition — and that is the useful output

    The card treats them as one population. Under ADR-0049 enforce-or-remove they diverge:

    • sanitize — the enforce arm means building a real switch that disables XSS sanitization. ⛔ That is not an acceptable outcome. So for this key enforce-or-remove collapses to remove, which makes it dispatchable rather than decision-shaped.
    • components — a conventional renderer-override extension point that simply was never wired. Both arms are genuinely open here, and wiring it is a plausible feature rather than a hazard.

    ⛔ Not splitting the card. Same file, same mirror, same retirement route, same lane — two cards would cost more than the shared context is worth. Recording the asymmetry is enough for whoever takes it.

    Lane

    domain:spec, not ui: the declaration and its mirror both live in packages/types, which the objectui three-stream split assigns to the contract face, and removing a key changes accept/reject behaviour ⇒ Clause ② at dispatch. Same routing as its siblings #6910 and #6942.

    ⚠️ Carried forward from the card and ⛔ not re-measured by me: #6172's convergence means the retirement now covers two published faces, since @object-ui/plugin-markdown re-exports the canonical shape rather than declaring a narrower copy.


    Generated by Claude Code

  2. added
    bugSomething isn't working
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    and removed on Sep 4, 2026
  3. self-assigned this
    on Sep 5, 2026
  4. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Claim: PM loop round R1
    Session: session_01BAZFhALsQsGqxui8sNqM8s
    Branch: claude/issue-6972-markdown-inert-keys-retire
    Worktree: objectui-issue-6972
    Domain: domain:spec
    File surface (region level): packages/types/src/data-display.ts — the MarkdownSchema interface only (sanitize / components at :1164–1171) · packages/types/src/zod/data-display.zod.ts — the MarkdownSchema mirror only (:292–293) · packages/types/src/zod/tombstone.zod.ts — read only (reuse retirementTombstone / handlerKeyRefusal; a new helper, if one is truly needed, goes below retirementTombstone, ⛔ never inside the handlerKeyRefusal region, which #6854 owns this batch) · packages/types/src/__tests__/ — one new refusal pin; zod-mirror-parity.test.ts only if the ledger needs a row · packages/plugin-markdown/src/types.ts — the re-exported authority (expected read-only; measure) · the markdown docs page under content/docs/components/** · .changeset/* (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: fable = CONTRACT_REVIEW_TIER — two declared keys become refusals on two published faces (@object-ui/types and @object-ui/plugin-markdown's re-export); objectui has no dispatch-gates.mjs, tier judged from card content
    Clause-②: yes
    Serial constraints cleared (file granularity per the maintainer ruling recorded at #5734 comment 5551274423): the #7122 chain holds packages/types/src/{field-types,form,objectql,select-option,index}.ts, zod/{form,index}.zod.ts and the __tests__ files named on its claim — none of this card's regions; #7701 already landed the DrillDownConfig mirror in data-display.zod.ts, a different region · #6854 (dispatched this batch) owns zod/app.zod.ts and the handlerKeyRefusal region of tombstone.zod.ts plus packages/runner/** — this card reads tombstone.zod.ts and does not edit that region · #6939 (domain:ui) has all eight groups on main, nothing in flight · the dev merges origin/main before opening the PR and again after any sibling lands; conflicts go to the merge queue


    Disposition executed, and where each half's authority comes from

    • sanitize — remove. Triage (5535325734) collapsed enforce-or-remove for this key: the enforce arm is a switch that disables XSS sanitisation, which is not an acceptable outcome. Verified at claim on origin/main 4ce14f1: rehypePlugins is a module-level const with rehypeSanitize as its last link, no conditional path; zero readers of schema.sanitize against a control of 19 files reading schema.content. Retire as an ADR-0049 tombstone on both faces (type member removed; zod face refuses the key by name with guidance that sanitisation is unconditional).
    • components — remove, by this seat's discretion under a declared veto window. Triage left both arms open. This seat's reading: a Record<string, any> of React component overrides is not a JSON-authorable value — it is the same shape as the handler keys objectui#6124 retired ("JSON has no function value"), so it folds into that already-ruled refusal class rather than needing a fresh decision (zero readers, zero pull, a permanent obligation if kept). Four axes: long-term — one fewer declared-but-inert member on a published schema; pull — none measured; AI-error — a declared key that does nothing is the silent failure ADR-0049 exists to end; startup focus — remove over declare-and-maintain. Falsifiable premise for the dev: if any host path (props, plugin API, a MarkdownImpl prop) actually consumes a components map, or if a runtime-slot disposition (TS twin stays callable for hosts) is the honest shape, stop on that half and report the fork — do not silently pick. The maintainer may veto this half on the card; the PR stays draft until in-seat contract review.

    Route: both faces of @object-ui/types; @object-ui/plugin-markdown re-exports MarkdownSchema from ./types (measure whether that is a local declaration or a re-export of the authority — #6172 converged it); docs rows removed; a refusal pin asserting the refusal and the guidance text; changeset per the repo's tombstone precedents (feat(types)!: retirements #7639 / #7669 today, minor under the launch-window convention — the dev reads those PRs' changesets rather than inferring).


    Generated by Claude Code

  5. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Claim (dev): implementing for PM loop round R1, dispatch of session_01BAZFhALsQsGqxui8sNqM8s.

    • Branch: claude/issue-6972-markdown-inert-keys-retire (pushed; the empty-branch write probe succeeded before any edit)
    • Worktree: ../objectui-issue-6972, cut from origin/main = 4ce14f1
    • Assignee: set by the PM seat, not touched here.
    • Both halves executed as ruled / dispositioned: sanitize retired as an ADR-0049 tombstone (both faces), components retired the same way after re-measuring the falsifiable premise on the branch base — no host path consumes a components map (MarkdownImplProps accepts only content and className; the components map the renderer hands to ReactMarkdown is its own module-level mdComponents, never merged with anything off the schema; zero MarkdownSchema consumers outside packages/types and packages/plugin-markdown). runtime-slot therefore does not apply. The handlerKeyRefusal region of tombstone.zod.ts was not touched; both keys use retirementTombstone().
    • One dispatch assumption falsified in passing: the docs page is content/docs/plugins/plugin-markdown.mdx, not under content/docs/components/**.

    Draft PR and the os-dev-report comment follow.


    Generated by Claude Code

  6. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    os-dev-report

    {
      "issue": 6972,
      "status": "done",
      "branch": "claude/issue-6972-markdown-inert-keys-retire",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7718",
      "premise_still_valid": true,
      "summary": "Both inert MarkdownSchema keys retired as ADR-0049 tombstones on both published faces, on the route PR #7639 / PR #7669 established: `?: never` on the TS interface (packages/types/src/data-display.ts) and a retirementTombstone() named refusal on the Zod mirror (zod/data-display.zod.ts); the handlerKeyRefusal region of tombstone.zod.ts was not touched and no new helper was needed. `sanitize` half executed on the triage collapse (enforce = XSS-off switch); the guidance string states that sanitization is unconditional, re-verified as control flow on 4ce14f1 (rehypePlugins is a module-level const, last link [rehypeSanitize, sanitizeSchema], handed straight to ReactMarkdown, no conditional path). `components` half executed under the PM's veto window after re-measuring the falsifiable premise: no host path consumes a components map (MarkdownImplProps accepts only content and className; the components map the renderer passes is its own module-level mdComponents, never merged with the schema; LazyMarkdown receives only content and className; zero MarkdownSchema consumers outside packages/types and packages/plugin-markdown), so runtime-slot does not apply. Readers: zero for schema.sanitize / schema.components over packages/ apps/, control schema.content read in 20 .tsx files (28 ts+tsx). Two faces confirmed: plugin-markdown/src/types.ts is a type re-export of the authority, no plugin source edit; its own index.test.ts gained a two-face pin. One fixture authored the key (packages/types/examples/data-display-examples.json, sanitize: true) and lost it; docs rows replaced by a Retired blockquote. Assignee was set by the PM; not touched. origin/main moved by one unrelated commit (f96a781, schema-catalog fixture) after the PR opened; not re-merged, the queue rebuilds on current main.",
      "tests": "All under the verify lock, exit captured before any pipe, on HEAD 61eccaa (= pushed head). `pnpm exec vitest run --maxWorkers=2 packages/types/ scripts/__tests__/one-authority-per-exported-name-6273.test.ts`: Test Files 113 passed (113), Tests 1955 passed (1955). `pnpm exec vitest run --maxWorkers=2 packages/plugin-markdown/`: Test Files 5 passed (5), Tests 49 passed (49). `pnpm --filter @object-ui/types build` (dist completeness: 120 emitted files verified) then `pnpm --filter @object-ui/types type-check` (script echoed: tsc --noEmit, tsconfig.examples.json, tsconfig.test.json): exit 0. `pnpm --filter @object-ui/plugin-markdown type-check` against the rebuilt types dist: exit 0. New pin packages/types/src/__tests__/markdown-inert-keys-retired-6972.test.ts = 19 tests (per key: envelope path/invalid_type/expected never, message = guidance literal and contains the prescriptive sentence, message = .describe(), key stays in .shape; controls: absent green, className green, wrong content still red, fixture green, undeclared key rides passthrough = the DELETED row live; TS half: fresh literal, document form, widened value under @ts-expect-error enforced by tsconfig.test.json). ABLATION (fix committed first, trap-guarded, absolute paths): both declaration files reverted to 4ce14f1; on-disk markers sanitize?: never 0 / sanitize?: boolean 1 / components?: never 0 / zod tombstones 0; types rebuilt; dist markers dist/data-display.d.ts never 0 boolean 1, dist/zod/data-display.zod.d.ts ZodNever 0 ZodBoolean 2. Direction RED as expected: pin 8 failed | 11 passed (19) exit 1 (4 refusal legs, 2 guidance legs, 2 keeps-declared legs), types type-check exit 2 with 6 TS2578, plugin-markdown type-check exit 2 with 2 TS2578 through the re-exported face. Restore: git checkout HEAD -- both paths, git diff HEAD --stat empty, git hash-object equals HEAD blobs e3ef905ac426 / 5657e60c825d, rebuilt, dist markers back (never 1 boolean 0; ZodNever 2 ZodBoolean 0), pin 19/19 green, plugin-markdown type-check exit 0. Vitest spelling: the dispatch's `pnpm --filter X exec vitest run` form is refused by the repo's invocation guard (root not repo root, objectui#3378); root-relative paths used instead, same config CI runs. Affected census: TURBO_SCM_BASE=4ce14f1 turbo ls --affected = 43 packages (everything downstream of @object-ui/types); declared narrowing: only a package naming MarkdownSchema or authoring sanitize/components on a markdown node can be affected, and all three censuses are zero outside the two packages run; the remaining 41 are CI's.",
      "mcp_calls": "6 — add_issue_comment (claim), create_pull_request, pull_request_read get (body read-back), issue_read get_labels (failed: PR numbers do not resolve there), issue_write update (label union write), add_issue_comment (this report). Issue/comments/PR labels were read through the zero-quota web payload channel; REST reads are 403 from this container (measured on the issue).",
      "gates": [
        {"command": "pnpm exec vitest run --maxWorkers=2 packages/types/ scripts/__tests__/one-authority-per-exported-name-6273.test.ts", "exit": 0, "head": "61eccaa", "verdict": "Test Files 113 passed (113) · Tests 1955 passed (1955)"},
        {"command": "pnpm exec vitest run --maxWorkers=2 packages/plugin-markdown/", "exit": 0, "head": "61eccaa", "verdict": "Test Files 5 passed (5) · Tests 49 passed (49)"},
        {"command": "pnpm --filter @object-ui/types build && pnpm --filter @object-ui/types type-check", "exit": 0, "head": "61eccaa", "verdict": "clean; script name echoed"},
        {"command": "pnpm --filter @object-ui/plugin-markdown type-check", "exit": 0, "head": "61eccaa", "verdict": "clean; script name echoed"},
        {"command": "node scripts/check-changeset-presence.mjs", "exit": 0, "head": "61eccaa", "verdict": "4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)"},
        {"command": "node scripts/check-changeset-fixed.mjs", "exit": 0, "head": "61eccaa", "verdict": "All workspace packages are in the changeset fixed group"},
        {"command": "node scripts/check-changeset-no-major.mjs", "exit": 0, "head": "61eccaa", "verdict": "No changeset declares a major bump"},
        {"command": "node scripts/check-changeset-overwrite.mjs", "exit": 0, "head": "61eccaa", "verdict": "No pre-existing changeset was modified or deleted"},
        {"command": "pnpm check:control-bytes", "exit": 0, "head": "61eccaa", "verdict": "OK (scanned 6293 tracked text file(s))"},
        {"command": "pnpm check:spec-symbols", "exit": 0, "head": "61eccaa", "verdict": "no comment cites a key its spec symbol does not declare"},
        {"command": "pnpm check:doc-types", "exit": 0, "head": "61eccaa", "verdict": "Every documented component type is registered"},
        {"command": "pnpm check:doc-fences", "exit": 0, "head": "61eccaa", "verdict": "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript"},
        {"command": "node scripts/check-doc-links.mjs", "exit": 0, "head": "61eccaa", "verdict": "Links are valid across 17 scan roots"},
        {"command": "pnpm check:doc-snippets", "exit": 2, "head": "61eccaa", "verdict": "NOT MEASURED — PRECONDITION NOT MET (unbuilt plugin packages; the gate says it could not run, not that it found errors); declared to CI; the only doc edit is a table row removal plus a blockquote, no fence changed"},
        {"command": "node scripts/check-governed-queue-guard.mjs --test PATHS", "exit": 0, "head": "61eccaa", "verdict": "NOT GOVERNED — 7 path(s) checked against 5 governed surface(s); none matched"},
        {"command": "grep -naP control-character scan over the 7 changed files", "exit": 1, "head": "61eccaa", "verdict": "no control bytes (grep exit 1 = no match)"},
        {"command": "pnpm --filter '@object-ui/plugin-markdown^...' build (dependency closure, first command in the worktree)", "exit": 0, "head": "4ce14f1", "verdict": "VERDICT command-exit 0 · held the lock 70s"}
      ],
      "line_budget": "n/a",
      "deviations": [
        "packages/plugin-markdown/src/index.test.ts edited (claim listed the plugin as expected read-only): the two-face pin lives there because it is the only place the re-exported face can be asserted; PR #7639 set the precedent.",
        "packages/types/examples/data-display-examples.json edited: dropped the one in-repo `sanitize: true` write, which the tombstone would refuse; the fixture is now pinned to parse green.",
        "Docs page is content/docs/plugins/plugin-markdown.mdx, not under content/docs/components/** as the dispatch assumed; rows removed and a Retired blockquote added in the report-schema.mdx form. check:doc-* gates cannot see table rows, so the docs disposition is carried by prose.",
        "Dispatch gate spelling `pnpm --filter @object-ui/types exec vitest run --maxWorkers=2` is refused by this repo's vitest invocation guard (package-dir root); the sanctioned root-relative path form was run instead.",
        "check:doc-snippets not measured locally (precondition: 20+ plugin builds); declared to CI.",
        "Affected census (43 packages) narrowed to packages/types + packages/plugin-markdown + the one-authority pin, with the narrowing argument stated in the PR body; remaining 41 declared to CI.",
        "Claim comment was posted after implementation rather than before (5551424489); the PM claim 5551288628 already held the branch.",
        "Label write went through MCP issue_write (REST is 403 here): read current set via the web payload, wrote the union {package: types, plugin, plugin: markdown, tests, needs:contract-review}, read back the same five — nothing stripped.",
        "origin/main advanced by one unrelated commit (f96a781) after the PR opened; not re-merged, the merge queue rebuilds on current main."
      ],
      "files_changed": [
        ".changeset/6972-markdown-inert-keys-retired.md",
        "content/docs/plugins/plugin-markdown.mdx",
        "packages/plugin-markdown/src/index.test.ts",
        "packages/types/examples/data-display-examples.json",
        "packages/types/src/__tests__/markdown-inert-keys-retired-6972.test.ts",
        "packages/types/src/data-display.ts",
        "packages/types/src/zod/data-display.zod.ts"
      ],
      "clause_2": [
        "sanitize: ACCEPT-SET CHANGE on both faces — a markdown node authoring `sanitize` (either value) parsed GREEN on 4ce14f1 and now parses RED (path sanitize, code invalid_type, expected never) with guidance stating sanitization is unconditional; TS `sanitize?: never`. Authority: triage collapse (enforce = XSS-off switch) + ADR-0049; ruled, not re-decided.",
        "components: ACCEPT-SET CHANGE on both faces — `components` parsed GREEN and now parses RED by name; TS `components?: never`. Authority: PM disposition under a declared veto window (claim 5551288628), NOT a maintainer ruling; falsifiable premise (no host consumer) re-measured on 4ce14f1 and holds, so runtime-slot was not the honest shape; PR stays draft for contract review and the maintainer may veto this half on the card."
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Commit trailers carry the session URL; the PR body (read back in full after creation, all sections intact, platform appended its own footer) carries the session id in prose as a code span. Dev of session_01BAZFhALsQsGqxui8sNqM8s.


    Generated by Claude Code

  7. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    In-seat contract review — PASS — and ACCEPT of PR #7718 (head 61eccaa), verified on GitHub and the tree at 2026-09-05T11:3xZ

    Implemented-by: branch claude/issue-6972-markdown-inert-keys-retire (mode:subagent dev of this seat's dispatch, CONTRACT_REVIEW_TIER)
    Reviewed-by: session_01BAZFhALsQsGqxui8sNqM8s (os-justin), served tier re-read before this verdict: get_session → last_served_model = claude-fable-5-1 = CONTRACT_REVIEW_TIER (11:29Z)

    ① Derived judgments — each accept-set / public-face change in the diff, judged

    change face before → after judgment
    MarkdownSchema.sanitize zod mirror (zod/data-display.zod.ts:292) z.boolean().optional() → retirementTombstone(…) refusing by name, code: invalid_type, expected: never, guidance in the message correct — triage's collapse holds: the enforce arm would be an XSS-off switch. Verified on the PR head myself: MarkdownImpl.tsx:99–107 rehypePlugins is a module-level const array whose last link is [rehypeSanitize, sanitizeSchema] (line 106), handed straight to ReactMarkdown; no conditional path. The guidance sentence is therefore true.
    MarkdownSchema.sanitize TS face (data-display.ts) ?: boolean → ?: never with the retirement docblock correct — tombstone, not deletion; BaseSchema's index signature would have re-admitted a deleted key as any (the precedent's reason, #5905).
    MarkdownSchema.components zod mirror z.record(z.string(), z.any()).optional() → tombstone by name correct under the declared veto window — my disposition, ⛔ not a maintainer ruling. The falsifiable premise (no host path consumes a components map) was re-measured by the dev and re-checked here: MarkdownImplProps (MarkdownImpl.tsx:27–36) declares only content and className; the components={mdComponents} it passes is the plugin's own module-level map (:173); index.tsx:38–40 forwards only content and className. runtime-slot does not apply (nothing reaches a renderer).
    MarkdownSchema.components TS face ?: Record<string, any> → ?: never correct, same reasoning.
    second published face @object-ui/plugin-markdown no source change; src/types.ts:40 re-exports the authority, so its face narrows identically; a two-face tsc pin added in index.test.ts correct — one authority (#6172), the changeset names the plugin so its CHANGELOG carries the note.
    fixture packages/types/examples/data-display-examples.json "sanitize": true removed from examples.markdown correct and required — the only in-repo author of either key; now pinned to parse green.
    docs content/docs/plugins/plugin-markdown.mdx two rows → a Retired (objectui#6972) blockquote correct (the page is under plugins/, not components/ as my brief assumed — dev's correction accepted).

    Boundary controls in the new pin are the right shape: absent stays green, className accepted, wrong content still refused (the mirror still validates), the undeclared-key row rides .passthrough() live (the "deleted" contrast), the envelope is asserted (path, code, expected) not just success === false, and the guidance string is one literal asserted equal on both channels (message = .describe()).

    ② Semver

    minor for @object-ui/types and @object-ui/plugin-markdown, commit subject feat(types)!: — identical grading to the tombstone precedents on main (.changeset/5905-componentinput-inputtype-tombstone.md: same two packages, minor) and to the fixed-group convention check-changeset-no-major enforces. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check are all success on this head. Consistent.

    ③ Boundary flags

    open_questions empty; no needs_decision. The one governance statement in the PR — the components half rests on this seat's discretion — is declared on the card (claim 5551288628), in the PR body, in the changeset and in both docblocks. Veto window stands: the maintainer may reverse that half on this card; the reversal is a one-file re-declaration, so the landing is not held for it.

    Checklist readings

    Landing: needs:contract-review cleared on the PR in this stroke (provenance comment on #7718 cites this PASS). Ready + auto-merge once every check is green; flip re-check armed. pm:dispatched comes off at the MERGED reading.


    Generated by Claude Code

  8. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Patch round (landing window, not a REWORK) — PR #7718 Test (shard 1/4) is red on 61eccaa and the cause is this PR's own docs edit: scripts/__tests__/check-doc-snippet-types.test.ts:624 pins a README sample block in content/docs/plugins/plugin-markdown.mdx by fence line (195), and the 13-line Retired blockquote that replaced two table rows moved that fence. Signature: AssertionError: no compiled block at content/docs/plugins/plugin-markdown.mdx:195. Every other check on the head is green (Lint, Type Check, three shards, the docs gates). The original dev is re-engaged to re-point the line constant inside this PR and push; the ready flip waits for the new head's farm. ⛔ Not a re-run: the failure is deterministic and this PR's.


    Generated by Claude Code

  9. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    os-dev-report

    {
      "issue": 6972,
      "status": "done",
      "round": "patch 1 — CI Test (shard 1/4) red on 61eccaa (job 101298650018)",
      "branch": "claude/issue-6972-markdown-inert-keys-retire",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7718",
      "head": "5fe2a1f8d7370ecdf070514429bf92560d1f8dcb",
      "premise_still_valid": true,
      "summary": "scripts/__tests__/check-doc-snippet-types.test.ts pins the plugin-markdown README sample fence by LINE on purpose (README_SAMPLE_FENCE_LINE; an edit above it forces a re-declaration). The objectui#6972 retirement blockquote replaced two prop-table rows above that fence with 13 lines, so the fence moved 195 -> 206 (re-measured on the head: `fenceLine: i + 1`, opening fence at line 206, base 4ce14f1 had it at 195). Smallest honest fix: the constant is re-declared as 206 with a two-line comment naming the move; assertion shape unchanged; the blockquote is neither shrunk nor moved; the pin is not re-anchored by content (that is its own card). Worktree recreated from the remote branch for the patch, no rebase, no force-push; one commit 5fe2a1f pushed on top of 61eccaa. PR body gained the Deviations line and the re-run row.",
      "tests": "`pnpm exec vitest run --maxWorkers=2 scripts/__tests__/check-doc-snippet-types.test.ts` under the verify lock on 5fe2a1f: Test Files 1 passed (1), Tests 70 passed (70), VERDICT command-exit 0. `pnpm check:control-bytes`: exit 0, `OK (scanned 6293 tracked text file(s))`. `pnpm check:doc-snippets`: exit 2, PRECONDITION NOT MET (unbuilt plugin packages; the gate says it could not run) — NOT MEASURED, unchanged from round 1, declared to CI.",
      "gates": [
        {"command": "pnpm exec vitest run --maxWorkers=2 scripts/__tests__/check-doc-snippet-types.test.ts", "exit": 0, "head": "5fe2a1f", "verdict": "Test Files 1 passed (1) · Tests 70 passed (70)"},
        {"command": "pnpm check:control-bytes", "exit": 0, "head": "5fe2a1f", "verdict": "OK (scanned 6293 tracked text file(s))"},
        {"command": "pnpm check:doc-snippets", "exit": 2, "head": "5fe2a1f", "verdict": "NOT MEASURED — PRECONDITION NOT MET (unbuilt packages); declared to CI"}
      ],
      "line_budget": "n/a",
      "deviations": [
        "scripts/__tests__/check-doc-snippet-types.test.ts is outside the claim's file surface; edited only to re-declare the line constant the docs edit moved, as the coordinator's patch instruction directed."
      ],
      "files_changed": [
        "scripts/__tests__/check-doc-snippet-types.test.ts"
      ],
      "mcp_calls": "this round 3 so far — update_pull_request (Deviations line + re-run row), add_issue_comment (this report), plus one read-back to follow; cumulative for the run 10 (7 in round 1). PR body and labels otherwise read through the zero-quota web payload channel.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Dev of session_01BAZFhALsQsGqxui8sNqM8s.


    Generated by Claude Code

  10. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Landing: PR #7718 marked ready for review and auto-merge enabled (explicit SQUASH) at 2026-09-05T11:58Z (session session_01BAZFhALsQsGqxui8sNqM8s, domain:spec @ objectui seat).

    Readings at the flip, head 5fe2a1f8d7370ecdf070514429bf92560d1f8dcb (patch commit re-declaring README_SAMPLE_FENCE_LINE 195 → 206), base f96a781 = origin/main tip at 11:58Z:

    • check runs on the head: 32 total, all completed — 29 success, 3 skipped (dependabot, Test (coverage), the ${{ matrix.shard }} placeholder). Test (shard 1/4), red on 61eccaa for the line-anchored fence pin, is success on 5fe2a1f (11:55:27Z). The ready flip re-ran Governed Surface Queue Guard (11:58:55Z, success).
    • mergeable_state read behind before the flip — the sibling PR Fixes #7101: drop the undeclared variant key from the dropdown-menu Delete item #7717 (one fixture file, disjoint from this PR's surface) landed first; under the maintainer's file-granularity ruling the queue arbitrates, no manual re-merge was ordered.
    • merge-queue refs at 11:59:15Z: gh-readonly-queue/main/pr-7718-adb2a86… present, queued behind pr-7720-f96a781… (positive probe; the enable call's echo is not relied on).
    • pre-landing three: ① in-seat contract review PASS on this card (comment 5551465094) ✓; ② carriers: PR-side needs:contract-review cleared with provenance (PR feat(types)!: retire MarkdownSchema.sanitize and .components as ADR-0049 tombstones (objectui#6972) #7718 comment 5551468483) — check-clause2-carriers.mjs --pair 7718 returned exit 3 from this seat (REST 403, session-gated), so that reading is NOT a clean-board reading and the pair was verified by hand ✓; ③ every check green, not just the required subset ✓. Governed-surface test on all 8 paths: not governed.

    Next reading: MERGED confirmation by two readings (origin/main contains the squash + PR merged: true), then pm:dispatched is stripped from this card.


    Generated by Claude Code

  11. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    MERGED — two readings at 2026-09-05T12:17Z (session session_01BAZFhALsQsGqxui8sNqM8s, domain:spec @ objectui seat): this card's closing PR #7718 reads state MERGED (card closed completed 12:14:18Z through Fixes #6972); origin/main tip is 446d93d4e226d90a0c89b66690f35a49642824a1 = the squash feat(types)!: retire MarkdownSchema.sanitize and .components as ADR-0049 tombstones (objectui#6972) (#7718); the queue ref pr-7718-adb2a86… is gone. pm:dispatched stripped in the same stroke (label set now bug, priority:p2, domain:spec).

    Executed: sanitize and components are ADR-0049 tombstones on both faces of MarkdownSchema (types + the plugin's re-exported face); the components half rests on this seat's disposition under the declared veto window (claim 5551288628) — reversible by a one-file re-declaration if the maintainer vetoes.


    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

bugSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions