Repository navigation
A type: 'form' action leaves the console shell for a bare /forms/:name page and ends on a generic "Thanks!" panel instead of the created record (observation) #7245
Description
Activity
Maintainer ruling (2026-08-10, directed in session
session_01BPWqbmEFU8gJepBJTHESXd): thetype: 'form'contract means in-shell, and an internal submit lands on the record./forms/:nameinmode="internal"nests inside the console shell (keep the route — deep-linking survives; the missing chrome is the defect, not the navigation).- Internal-mode submit defaults to redirect-to-created-record;
thank-youstays the default for the public/f/:slugpath only. showcase_task.editmay declare asubmitBehavioras an interim corpus fix, but the platform default is the ruling above — the corpus must not have to opt out of a wrong default.
The renderer changes land in objectui (
App.tsxroute nesting,FormPagemode-aware default) — per the file-at-destination ruling (#7167), the objectui half migrates there with this ruling quoted; this card tracks the contract statement.needs-user-decision→pm:queue.
Generated by Claude Code
Claim: PM loop wave 2, card 3
Session:session_01YS2qzDAn3CpWdY7uBX9bFQ(devx seat, #6023)
Branch:claude/issue-7245-form-action-contract-docs
Worktree: cloud session, fresh clone (mode:cloud)
Domain:domain:devx
File surface: hand-written docs stating thetype: 'form'action contract — expectedcontent/docs/protocol/objectui/actions.mdxandcontent/docs/ui/forms.mdx(+ immediately adjacent hand-written pages if the contract is restated there). ⛔ Generatedcontent/docs/references/**andpackages/spec/src/**are out of surface: if the measured producer of the contract text is a spec zoddescribe/JSDoc, that isdomain:spec-surfaceterritory — stop and report the flag, do not patch the consumer side.
Container & model: M,mode:cloud,model: opus
Serial constraints cleared: none — no in-flight claim touches these docs pages; wave-mates are file-disjoint (SKILL.md / docs/qa).Split per the maintainer's ruling above: this card delivers the contract statement in objectstack docs (in-shell for internal mode; internal submit defaults to redirect-to-created-record;
thank-youis public-path-only). The renderer half (objectuiApp.tsxroute nesting,FormPagemode-aware default,ActionRunner.executeForm) migrates to an objectui card filed by this seat with the ruling quoted — cloud dev sessions cannot reach the un-attached sibling repo, so the cross-repo card is PM-filed (backlink will follow on this thread).If an earlier claim comment with a different session ID exists above this one, that claim wins.
Generated by Claude Code
{ "issue": 7245, "status": "delivered", "half": "objectstack — contract statement in hand-written docs only (objectui renderer half NOT touched; no objectui access in this session)", "branch": "claude/issue-7245-form-action-contract-docs", "commit": "c8417f4", "pr": { "number": 7417, "url": "https://github.com/objectstack-ai/objectstack/pull/7417", "draft": true, "first_line": "Part of #7245 (NOT Fixes — the objectui renderer half must stay trackable)" }, "measured_before_writing": [ "content/docs/protocol/objectui/actions.mdx §Form Actions — route only ('routes to /console/forms/:name'); silent on chrome and on post-submit.", "content/docs/ui/forms.mdx §8 — kind table labelled thank-you '*(default)*': today's mode-blind renderer fallback stated as the contract.", "content/docs/ui/forms.mdx §9 type:'form' launchers — route only.", "content/docs/ui/actions.mdx type table — one-line form row, route only.", "content/docs/ui/public-data-collection.mdx — declares an explicit thank-you but never says it is also the public default, so the split was invisible." ], "changes": [ "protocol/objectui/actions.mdx: new '#### What type: form promises' subsection — (1) renders in-shell, route kept so deep-linking survives; (2) internal submit defaults to redirect-to-created-record; (3) explicit submitBehavior always wins. Plus a public-vs-internal table (audience / chrome / default). Action Types row gains the in-shell + lands-on-record clause.", "ui/forms.mdx: §8 drops the bare '*(default)*' marker and gains a '### The default is mode-aware' table (public /console/f/:slug → thank-you; internal /console/forms/:name → redirect to created record, with the reason for each). §9 and the top-of-page bullet restate it and link the contract.", "ui/actions.mdx: form row links the contract anchor.", "ui/public-data-collection.mdx: one paragraph — the explicit thank-you there is copy, not behavior; internal defaults the other way." ], "honesty_note": "Both edited pages carry a dated 'Current renderer status (2026-08-10)' note: the console at the pinned .objectui-sha renders /console/forms/:name outside the shell and falls back to thank-you in both modes. The note points at the objectui half tracked from this thread and tells authors to declare submitBehavior explicitly until it lands. The defect is NOT documented as the contract.", "spec_surface_flag": { "verdict": "NOT spec-surface — proceeded", "evidence": "submitBehavior is z.discriminatedUnion(...).optional().describe('Post-submit behavior') with no .default(); the generated content/docs/references/ui/view.mdx:269 reproduces exactly that neutral string and carries no default claim. The 'thank-you (default)' resolution happens in the objectui renderer (FormPage '?? { kind: thank-you }'), so the reader-facing producer of the '(default)' claim was the hand-written page, not the spec.", "out_of_surface_followup": "packages/spec/src/ui/view.zod.ts (~line 2029) JSDoc still reads '`thank-you` (default) — show a confirmation panel'. Source comment only, not published, so left untouched — worth a domain:spec-surface card to make it mode-aware once the renderer half lands." }, "local_gates": { "check:nul-bytes": "pass (6801 files)", "check:role-word": "pass (44 baselined files, no new occurrences — new prose avoids the ratcheted word)", "check:quick-reference-counts": "pass", "check:docs-audit-scope": "pass (both self-tests; 179 hand-written docs in sync)", "check:doc-formula-expressions": "DOES NOT EXIST in package.json — nearest existing doc-corpus gate check:doc-authoring was run instead: pass (374 files clean)" }, "ci": { "settled": "20 of 21", "green": ["Build Docs", "Check Documentation Links", "ESLint", "Test Core", "Dogfood Regression Gate", "Spec property liveness", "Console Pin Freshness", "ADR maintainer approval", "Check PR Size", "Auto Label", "No other open PR may claim the same issue", "filter", "Vercel Preview Comments"], "in_flight": ["TypeScript Type Check (monorepo-wide; cannot be affected by a docs-only diff)"], "red": [ { "check": "Check Changeset", "cause": "no changeset present — expected", "resolution": "PM applies the 'skip-changeset' label. The gate's own message names content/ under 'releases nothing → apply skip-changeset', marked PREFERRED, and explicitly closes the empty-changeset route (#5471/#4898)." } ] }, "changeset": "none — docs-only diff touching no published package; the reader-visible behavior change belongs to the objectui renderer half and should carry its changeset there. A changeset here would announce a change that has not shipped.", "not_done": [ "objectui renderer half (App.tsx route nesting, FormPage mode-aware default, ActionRunner.executeForm) — out of this session's reach by ruling and by access; PM files that card and posts its number on this thread. The 'current renderer status' notes in both docs pages come out when it lands.", "skills/objectstack-ui/SKILL.md:1808 restates the form-action route — left untouched because a wave-mate holds SKILL.md (file-disjointness per the claim comment). Worth folding the in-shell clause in there later.", "showcase_task.edit interim submitBehavior (ruling clause 3) — corpus fix, not this card's file surface." ], "observed_not_fixed": "Hand-written docs spell the console mount /console/forms/:name (9 occurrences across content/docs + skills/), while the live URL measured in this issue is /_console/forms/... and protocol/objectui/actions.mdx:87 itself uses /_console/. Fixing 2 of 9 would worsen the inconsistency — wants its own docs-accuracy card.", "blockers": [] }
Generated by Claude Code
ACCEPT (objectstack half) — PR #7417. Reviewed against GitHub.
- Diff: 4 files, +47/−5, all hand-written
content/docs/**—protocol/objectui/actions.mdx,ui/forms.mdx,ui/actions.mdx,ui/public-data-collection.mdx. ⛔ Nocontent/docs/references/**, nopackages/spec/src/**— the declared surface boundary held. - Gate job conclusions read personally: ESLint
success, TypeScript Type Checksuccess;Check Changesetred only pre-label.skip-changesetapplied — the reader-visible behavior change belongs to the renderer half and carries its changeset there; announcing it here would announce something that has not shipped. That reasoning is in the PR body, which is what the dispatch asked for. - First line verified:
Part of #7245— ⛔ notFixes, so this card stays open for the renderer half. - The surface-boundary flag was measured, not assumed:
submitBehavioris.optional().describe('Post-submit behavior')with no.default(), and the generatedreferences/ui/view.mdx:269reproduces exactly that neutral string — so the "(default)" claim's reader-facing producer really was the hand-written page. Correct call to fix here rather than flagging todomain:spec-surface. - Honest about contract vs. shipped renderer: both pages carry a dated "Current renderer status (2026-08-10)" note saying the console at the pinned
.objectui-shadoes not implement this yet, with the interim advice to declaresubmitBehaviorexplicitly. Documenting the defect as the contract was the failure mode to avoid, and it was avoided.
Renderer half: objectui#4109, filed by this seat with the ruling quoted verbatim (file-at-destination, #7167). This card closes when both halves land.
Two flags from the PR body, neither absorbed into this diff (correct — single-purpose):
packages/spec/src/ui/view.zod.ts(~line 2029) JSDoc still says "thank-you(default)"; a source comment, not published, but false once the renderer lands ⇒domain:spec-surfacefollow-up.- The docs spell the console mount
/console/forms/:namein 9 places while the measured live URL is/_console/forms/…(andactions.mdx:87itself uses/_console/) — fixing 2 of 9 would make it worse; wants its own docs-accuracy card.
Both are being filed rather than left in a PR body. Flipped ready, auto-merge armed; same #6207/5-of-5-slots reading as #7422 — remedy toggle deferred one patrol.
Generated by Claude Code
- Diff: 4 files, +47/−5, all hand-written
2 remaining items
os-project-manager commented
on Aug 16, 2026 CollaboratorMore actionsUnlock scan: the renderer half is delivered and already inside the pinned console — released,
pm:blocked⇒pm:queue. Remaining scope re-verified onorigin/main: remove the two now-stale interim notes in the docs, then close. Triage seat (whole-repo Routine), scheduled fire ~01:08Z 2026-08-16.This card's
Blocked-by:lived in the comment above (deliberately, per the body-rewrite hazard), so the body-grep unlock scan was blind to it; surfaced this round via the per-card comment read.Upstream discharged — verified, not inherited
- Internal
type: 'form'actions: nest/forms/:name(mode=internal) inside the console shell; internal submit defaults to redirect-to-created-record objectui#4109 closed completed 2026-08-14: its PR merged 2026-08-11 delivering both ruling deliverables (internal/forms/:namenested in shell chrome; internal submit defaulting to redirect-to-created-record). Follow-ups live on their own objectui cards; the spec-docstring flag was routed back here at the time and the contract half already shipped in the merged docs PR. - The console pin already covers it — re-verified this round by content, not ancestry guesswork: at the pinned
.objectui-sha665661ab…,FormPage.tsxcarriesresolveSubmitBehavior(internal ⇒ created-record) andApp.tsxmounts/forms/:namevia the reworkedInternalFormRoute. So the shipped console now implements the contract this card documented.
Remaining scope (what a dispatch actually does — small and mechanical)
The docs PR honestly carried dated "Current renderer status (2026-08-10)" caveats saying the pinned console did not yet implement the contract, to come out when the renderer half landed. That moment has passed, and both caveats are now false on
origin/main:content/docs/protocol/objectui/actions.mdx:179content/docs/ui/forms.mdx:437
Remove/rewrite those two notes (
⚠️ the other note atforms.mdx:424belongs to a different card — the redirect-token substitution consumer half — and stays unless that card has landed; verify, don't sweep). Docs-only,skip-changesetper the shipped precedent on this card's own first PR. This closes the card (Fixes) — both halves and the cleanup will then be done.Size/model suggestion: S,
sonnet(mechanical removal with one verify step).
Generated by Claude Code
- Internal
os-project-manager commented
on Aug 16, 2026 CollaboratorMore actionsPM dispatch: claimed for the
domain:devxlane on the strength of the triage unlock scan above.pm:queue→pm:dispatched. Sessionsession_011RB4waLuNbdruCo6X9oobm, branchclaude/issue-7245-drop-stale-renderer-notes. The implementing agent posts its own claim comment with its session ID before writing.Own-error note, recorded because it is the exact failure this card's own history is about. I first read this card and set it aside as undispatchable — "three undecided directions, renderer code lives in objectui, needs the maintainer". Every word of that came from the body, and every word of it has been false since 2026-08-10: the maintainer ruled it that day, the docs half shipped in PR #7417, the renderer half merged as objectui#4109 on 08-11, and triage released the remainder to the queue at 01:20Z today. The seat post's own handover instruction is 「读正文 → 读晚于正文最后编辑的评论」 and I skipped the second step. On a card whose body still opens with "Shape of the question (not decided here)", the body is the least current thing on the page — six days and three state transitions live only in the comments.
⚠️ This is a generalisable hazard, not a one-off slip: a long-lived card's body is frozen at filing time, and this repo deliberately keepsBlocked-by:and state corrections in comments (see the 2026-08-12 correction above, which explains why the body was not rewritten). So for any card older than a day, body-only reading systematically returns the pre-decision world.Dispatch scope is triage's, unchanged: remove the two now-false dated renderer-status notes,
⚠️ leaveforms.mdx:424alone — it belongs to the redirect-token consumer half and is a different card.Fixes #7245, so this closes both halves plus the cleanup.
Generated by Claude Code
os-project-manager commented
on Aug 16, 2026 CollaboratorMore actionsClaim: implementing dev for the triage-unlocked cleanup scope (remove the two now-stale "Current renderer status (2026-08-10)" notes; verify premise against
origin/mainpin first).
Session:session_011RB4waLuNbdruCo6X9oobm
Branch:claude/issue-7245-drop-stale-renderer-notesRe-read the comment thread above before starting: PM dispatch comment above carries the same session ID, so no earlier claim with a different session ID exists. Proceeding.
Generated by Claude Code
Generated by Claude Code
os-project-manager commented
on Aug 16, 2026 CollaboratorMore actionsos-dev-report
{ "issue": 7245, "status": "done", "branch": "claude/issue-7245-drop-stale-renderer-notes", "pr": "https://github.com/objectstack-ai/objectstack/pull/9051", "premise_still_valid": true, "summary": "Verified by content (not ancestry) that the objectui pin on origin/main right now (.objectui-sha 665661ab093263f39f2e660a295ea615dbcee35a, unchanged since triage's 01:20Z measurement) really implements the 2026-08-10 ruling: FormPage.tsx's resolveSubmitBehavior returns { kind: 'created-record' } for internal mode, and App.tsx mounts /forms/:name via ProtectedRoute + InternalFormRoute, which wraps FormPage mode=\"internal\" in DefaultHomeLayout (console shell chrome). Premise held, so removed both now-stale 'Current renderer status (2026-08-10)' notes at content/docs/protocol/objectui/actions.mdx:179 and content/docs/ui/forms.mdx:437, leaving the contract prose above each intact (no rewrite needed). Left forms.mdx:424 (the different, still-open redirect-token note, objectui#4190) untouched.", "tests": "pnpm check:nul-bytes -> OK (5956 text files scanned, 0 raw control bytes). pnpm check:role-word -> OK (43 baselined files, no new occurrences). pnpm check:docs-audit-scope -> OK (178 hand-written docs in sync; 9 release-owned pages correctly review-only). Gate families derived via node scripts/pm/dispatch-gates.mjs against the two changed paths named exactly these two families plus residue (35 undetermined, 62 silent) with no third family implicated by this diff. check:doc-anchors doesn't apply (no #fragment links touched, none added/removed); page-path links are 'Check Documentation Links' (CI-only, no local lychee binary) -- diff only removes an external GitHub issue link and prose, adds no new link surface. All runs taken at commit b7a436fc7 (git rev-parse --short HEAD), matching the PR head.", "open_questions": [], "out_of_scope_findings": [ "filed as #9050: docs spell the internal form-action mount /console/forms/:name in 14 occurrences across 3 files, while the same page (actions.mdx:87,92) uses /_console/ in its own worked examples, and prior triage measured the live browser URL as /_console/forms/... -- unassigned, no domain label, per boundary. Follow-up 1 from the dispatch prompt (spec JSDoc 'thank-you (default)') was already filed and resolved as #7441 (closed completed, merged PR #7473) -- cited, not refiled." ] }
Generated by Claude Code
- added a commit that references this issue
on Aug 16, 2026 - added 4 commits that reference this issue
on Aug 17, 2026
Found while browser-verifying #6739 / PR #7237 (the ruled
type: 'modal'→type: 'form're-work ofshowcase_new_task). Filed separately, unfixed: it is pre-existing behavior that #6739's fix newly routes the showcase home page's primary CTA through, and it is not that card's scope.Observation-class: nothing crashes, nothing is refused, and the record is created correctly. What is off is where the user ends up.
Measured
Real browser (Chromium 141), showcase booted with
objectstack dev --ui --seed-admin, console served frompackages/console/distbuilt byscripts/build-console.shat the pinned.objectui-sha09987b680d53801c79f67d969b14e9bb732b8a22. Full journey — start on the showcase home page, click the "Create Task" CTA:The record IS created. Two things about the surface it happens on:
1. The form route sits outside the console shell. In objectui
apps/console/src/App.tsx(pinned SHA, line ~233)/forms/:nameis a top-level route, a sibling of the app-shell routes rather than a child:So the rendered page has no sidebar, no app navigation, no breadcrumb — screenshotted and confirmed visually, not inferred from the DOM. A user who clicks a record-create CTA on an app page is dropped onto an unchromed page with no way back except the browser's Back button.
2. The post-submit panel is the anonymous-form confirmation.
FormPageresolvesconst behavior = loaded?.form?.submitBehavior ?? { kind: 'thank-you' }, so any form view that does not declaresubmitBehavior— includingshowcase_task.edit— renders:That is the right default for the public
/f/:sluganonymous path the same component also serves. For an authed internal action it reads as a contact-form receipt: the user just created a Task and is told a submission was received, with no link to the record and no return to where they were.Why it is worth recording
type: 'form'is the platform's validated, first-class way to open an object's form — the maintainer ruling on #6739 (2026-08-09) makes it the shape for this, retiring thetype: 'modal'object fallback. So this is the surface every such action lands on, not a corner.showcase_log_timealready ships on it; #6739 adds the home page's primary CTA and PR #6737's button.The reference corpus therefore now teaches: "the sanctioned way to open a create form navigates out of your app and confirms with a generic receipt." Worth a decision even if the answer is "working as intended".
Shape of the question (not decided here)
ActionRunner.executeFormnavigate at all, or render the form view in a dialog the way themodalpath does? A route keeps deep-linking; a dialog keeps context./forms/:nameinmode="internal"be nested inside the console shell layout?{ kind: 'redirect' }to the created record rather thanthank-you, leavingthank-youas the public-mode default? Or shouldshowcase_task.editsimply declare asubmitBehavior(a corpus fix rather than a platform one)?The renderer code is in objectui (
packages/core/src/actions/ActionRunner.tsexecuteForm,apps/console/src/App.tsx,apps/console/src/components/FormPage.tsx); filed here because the question is what the spec'stype: 'form'contract should mean, and because #6739's ruling thread lives here. Re-route if the answer is purely a renderer change.No
pm:queue— observation, for triage to grade.Generated by Claude Code