Skip to content

feat(spec): wizard view v1 — declaration-and-refusal tightening of FormViewSchema type:'wizard' (Card S) - #13733

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-13704-wizard-view-tightening
Aug 31, 2026
Merged

feat(spec): wizard view v1 — declaration-and-refusal tightening of FormViewSchema type:'wizard' (Card S)#13733
os-warren merged 2 commits into
mainfrom
claude/issue-13704-wizard-view-tightening

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #13704

Card S of the #13342 chain — the wizard view v1 declaration-and-refusal tightening of FormViewSchema type: 'wizard', implementing the #13622 design of record (proposal D1–D8; maintainer ruling 2026-08-31, director batch #12, verbatim 「同意」 — T1 guidance-refusal, T2 narrow breadth, T3 defer buttons, T4 semver to this review chain, T5 existing superRefine). Zero new authorable keys. Card R (objectui#6985) follows after this lands and is deliberately not co-batched.

What lands (all in packages/spec/src/ui/view.zod.ts + tests + gate-owned regenerations + changeset)

  1. Step-key refusal (D2/T2/T5) — the existing FormViewSchema superRefine (the section.pane refusal precedent) now refuses, on type: 'wizard' only, the wizard-inert step keys: section visibleWhen (and its deprecated visibleOn alias, which normalizeVisibleWhen folds into visibleWhen before the refinement runs — the message says so, since the reported path can only name the canonical key), and collapsible: true / collapsed: true. It runs inside the same bucket loop as the pane check, so it covers the legacy groups bucket and — via .extend() retention — the flattened runtime-overlay door. Breadth is ruled NARROW: exactly this family, no per-type presentation-key matrix (proposal F1 absorbed as not-done by the T2 ruling).
    • Measured boundary, called out for review: collapsible/collapsed carry .default(false), so post-parse an authored false is indistinguishable from the default. Only true is refused — an authored false declares exactly the behavior a wizard delivers, so it stays accepted (pinned as its own test with the reasoning).
  2. steps: guidance-refusal (D3/T1) — a guidance entry on the strict surface: the unknown-key rejection now carries the ruled prescription (sections ARE the steps; array order is step order). Never accepted as an alias.
  3. Empty/absent sections refusal (D7)type: 'wizard' with absent or empty sections is refused loudly. Measured before-state: the renderer chain silently fell back to plain simple rendering (objectui RecordFormPage's conditional spread + ObjectForm's sections?.length guard). No "at least 2 steps" floor — a one-step wizard is legal.
  4. TSDoc + generated reference docs record the ruled postures (D4/D5/D6/D8) — the gate is the DEFAULT semantics of type: 'wizard'; allowSkip = navigation freedom, not validation exemption; the gate is UI admission, never authorization; progress is derived state with showStepIndicator the only authorable knob; per-step validation binds only the existing field-level vocabulary; array order IS step order. Landed in the wizard docblock + describe() strings, which project into content/docs/references/ui/view.mdx and api/protocol.mdx (regenerated by gen:docs, committed here — no hand-authored teaching material; the docs(objectui): layout-dsl teaches only shapes the live schemas accept #13337/[finding] YAML doc fences are validated by nothing — check:skill-examples reads only ts/tsx fences, and only opt-in, so every metadata example in content/docs/** is unverifiable by construction #13086 fence stands).
  5. Changeset (T4)@objectstack/spec: minor, argued as a BREAKING accept-set narrowing shipped minor under the repo's launch-window convention, naming the nearest precedents (ActionSchema.onSuccess and a handler-returned redirectUrl are both reachable on one type: 'script' action, and the spec rules neither — renderer-side precedence is deciding it today #11519 / ActionSchema documents "newTabUrl is only valid together with opensInNewTab" but no refine enforces the pair (observation) #11842 ActionSchema narrowings, both minor + BREAKING header; mechanism precedent: the section.pane refusal). ADR-0087 disposition: not-required (no-migration-prescription), mirroring those precedents. Final grade is this review chain's call.

Census (before tightening)

Probe: repo-wide grep for type: 'wizard' / type: "wizard" / "type": "wizard" over *.ts|tsx|json|yaml|yml|mdx|md (node_modules/dist excluded), plus a steps: sweep over examples and lint fixtures, plus key inspection of every hit.

  • Authored corpus (examples, packaged metadata, docs): 2 wizard declarationsexamples/app-showcase/src/ui/views/task.view.ts (the task.view.ts:386 accept-pin shape) and its mirror packages/lint/src/showcase-shape.fixtures.ts. Both carry only name/label/columns/fields, non-empty sections. Zero hits on the newly-refused shapes; zero steps: on any form view; zero wizard examples in content/docs. No premise fork — nothing to migrate.
  • Positive controls: the probe's own hit list contains the two known-present declarations above, and the same pipeline run over a synthetic wizard-with-visibleWhen control file matches it; the test-fixture sweep found the bare { type: 'wizard' } fixtures listed next, proving the probe reaches refused shapes where they exist.
  • Test-fixture triage (three-way, not bulk-migrated): view.test.ts "accept all form view types" bare-wizard iteration — pinned exactly the removed branch, replaced with a stepped wizard + red pins for the bare spelling; view-union-diagnostics.test.ts overlay fixtures ({ type: 'wizard' } + binding) — kept, now pin the overlay exemption; component.test.ts formType: 'wizard' — different surface (page component), untouched; packages/lint fixtures — clean keys, untouched.

Evidence

  • Targeted suites at head cee7ddde: src/ui/view.test.ts + view-union-diagnostics + view-inline-object-binding + view-form-features-root — 4 files, 422 passed (verify-lock VERDICT command-exit 0). Full @objectstack/spec suite: 443 files / 11883 passed. Downstream consumer with wizard fixtures, @objectstack/lint: 85 files / 2360 passed. @objectstack/spec typecheck (tsc + scripts + test program): green; --listFiles proves view.zod.ts in the main program and view.test.ts in the test program (1 hit each — not the excluded-tests false green).
  • Ablation (predicted-red, hash-proven): view.zod.ts reverted to base blob 668fb2d9 (disk hash equal, tightening anchors grep 0), wizard-named tests re-run → 12 failed / 7 passed — exactly the 12 refusal pins predicted red, all 7 accept pins green; restore to HEAD blob 1d1fcc7f hash-verified, git diff HEAD empty, anchor count restored. No build leg by construction: spec's vitest imports ./view.zod source relatively — no exports-map/dist resolution on this pair.
  • Gates at head cee7ddde (derived via scripts/pm/dispatch-gates.mjs, no self-fed diff): check:authorable-surface green ("All 14 generated artifacts are up to date" from check:generated; authorable surface unchanged — zero new keys), check:docs, check:yaml-examples, check:skill-examples ("260 prose examples type-check across 3 surfaces"), check:doc-authoring (one hit fixed: internal issue id stripped from a customer-facing refusal message), check:liveness, check:strictness-ledger, check:variant-docs, check:empty-state, check:llms-txt, check:skill-refs, check:engine-double-contract, check:where-matcher, check:query-options-erasure, check:dispatcher-error-vocabulary, check:type-check-coverage, check:cross-package-test-inputs, check:nul-bytes, and the changeset gates (check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check:objectui-changeset, check:pm-half-states, check:changeset-gate-self-tests) — all exit 0 locally.
  • Declared narrowings (measured-as-narrowed, not skipped): check:dual-build-cjs-loads and check-dev-prereqs refuse without a whole-workspace build ("NOT a pass: nothing was measured") — CI's Build Core owns that farm; this diff adds no entry points or exports (api-surface current). scripts/pm/check-half-states.mjs exit 3 = its GitHub instrument unread from this seat (session-gated REST), a board patrol independent of this diff. check:react-declaration-parity needs objectui's browser-built manifest by design. Shard attestation / test completeness are Test Core job steps over CI's own artifacts.

Sequencing and fences

Parked DRAFT with needs:contract-review per the clause-② ruling — the review chain/maintainer owns enqueue; never ready/enqueued by this seat.

Generated by Claude Code


Generated by Claude Code

claude added 2 commits August 31, 2026 07:49
…steps refusal, ruled TSDoc (#13704)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
…om customer-facing refusal text (#13704)

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 2 documentable anchor(s).

13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via visibleWhen (literal))
  • content/docs/automation/flows.mdx (via visibleWhen (literal))
  • content/docs/data-modeling/field-types.mdx (via visibleWhen (literal))
  • content/docs/data-modeling/fields.mdx (via visibleWhen (literal))
  • content/docs/data-modeling/formulas.mdx (via visibleWhen (literal))
  • content/docs/data-modeling/schema-design.mdx (via visibleWhen (literal))
  • content/docs/data-modeling/validation-rules.mdx (via visibleWhen (literal))
  • content/docs/deployment/environment-variables.mdx (via visibleWhen (literal))
  • content/docs/protocol/objectui/concept.mdx (via FormViewSchema (symbol), visibleWhen (literal))
  • content/docs/protocol/objectui/index.mdx (via FormViewSchema (symbol))
  • content/docs/protocol/objectui/layout-dsl.mdx (via FormViewSchema (symbol), visibleWhen (literal))
  • content/docs/ui/pages.mdx (via visibleWhen (literal))
  • content/docs/ui/views.mdx (via visibleWhen (literal))

5 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/implementation-status.mdx (via visibleWhen (literal))
  • content/docs/releases/v12.mdx (via visibleWhen (literal))
  • content/docs/releases/v15.mdx (via visibleWhen (literal))
  • content/docs/releases/v16.mdx (via visibleWhen (literal))
  • content/docs/releases/v17.mdx (via visibleWhen (literal))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 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.

Coarse fallback — 126 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 787d757405db4f3ebbc6ca811948af4438a3fe7a

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 787d757405db4f3ebbc6ca811948af4438a3fe7a → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Copy link
Copy Markdown
Collaborator Author

放行(维护者词) — Provenance:维护者于本席 Claude Code 会话(session_01PBjwYLS6BciTQW3c9xQiD2,2026-08-31 ~12:4xZ)原话:

13733 为什么不合并

按「pr 绿了为什么不合并」→「12606 绿了」的在案个人放行先例执行。放行前核验:head cee7ddde 全部 36/36 check 完成且绿(skip 项为路径过滤/重复触发);条款② 状态:fable 派发 + 派发席收集复核 PASS(#13704 comment 5476081594),两个已挂旗边界(D7 overlay 豁免、collapsible 仅拒 true)按 dev 推荐 A 落地,评审链缺位由维护者词补足放行权。操作:剥 needs:contract-review(双载体)→ ready → auto-merge。T4 semver 终评(minor + BREAKING header,adr-0087 not-required)随本放行视为维持 PR 所载论证。


Generated by Claude Code

@os-warren
os-warren marked this pull request as ready for review August 31, 2026 12:48
@os-warren
os-warren enabled auto-merge August 31, 2026 12:48
@os-warren
os-warren added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit 4bc18e5 Aug 31, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-13704-wizard-view-tightening branch August 31, 2026 13:27
os-warren pushed a commit that referenced this pull request Aug 31, 2026
…sus line

The changeset gains the not-required (no-migration-prescription) marker
mirroring the #11519/#11842/#13733 narrowing precedents; the superRefine
insertion shifted field.zod.ts line numbers, so the system-context page
anchor is re-anchored by the census --fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
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 protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wizard view v1 — declaration-and-refusal tightening of FormViewSchema type:'wizard' (Card S, ruled shape of #13622)

2 participants