Repository navigation
Internal type: 'form' actions: nest /forms/:name (mode=internal) inside the console shell; internal submit defaults to redirect-to-created-record #4109
Description
Activity
CLAIM — session
session_017Qqyix2QcnpUC9XeYVDzx3(objectui whole-repo PM seat), dispatching now.Branch:
claude/issue-4109-internal-form-shell. Worktree-first.Surface constraints vs in-flight work (binding):
- Primary surfaces:
apps/console/src/App.tsx(route nesting),apps/console/src/components/FormPage.tsx(mode-aware submit default), plus tests. No current holds there. - HARD exclusion:
packages/core/src/actions/**— held in-flight by [P3] objectuiActionDef's[key: string]: anyabsorbs every retired and misspelled action key — removing a spec key produces no compile error #4046 (ActionDef index-signature). The card citesexecuteFormonly as a measured starting point; both deliverables are console-side. If a core-actions change turns out to be genuinely required, the dev stops on that part and reports it instead of editing.
Scope is exactly the maintainer ruling quoted on the card (2026-08-10): keep the
/forms/:nameroute but nestmode="internal"inside the console shell; internal submit defaults to redirect-to-created-record;thank-youstays the public/f/:slugdefault; explicitsubmitBehaviorwins in both modes.
Generated by Claude Code
- Primary surfaces:
ACCEPT + 代决(session
session_017Qqyix2QcnpUC9XeYVDzx3,以产品长期稳定角度代决,维护者可否决)。PR #4279 交付核对:
- Deliverable 2 完整:
resolveSubmitBehavior(mode, declared)— internal 默认落创建记录、public 保持 thank-you、显式声明双模式均胜出;创建 id 只读 spec 声明的CreateDataResponse单一键(record.id别名负向钉住不读),镜像单信封规则。反向验证双向精确命中。 - Deliverable 1 路由嵌套完成:internal
/forms/:name进DefaultHomeLayout(console 对无 app 归属的登录页的既有 chrome,与/home、/organizations同款);/f/:slug保持无 chrome。 - CI 全绿;"5 个新 pin 中只有 2 个是变更探测器"在 PR 正文如实声明,验收接受这种诚实。
裁决 Q1(sidebar/breadcrumb):A 成立,按已交付形态验收。 理由:(1) A 零新增策略面;(2) B 的失败模式是静默的——冷深链上没有任何已发布的 current app,ConsoleLayout 会把任意第一个 app 的侧栏包在表单外,还把这个猜测写进共享 NavigationContext;(3) C 为一页发明第二种 URL 拼写,与裁决原文"keep the route"抵触;(4) 实测旅程的真实诉求是"提交后落在创建的记录上、且在壳内"——已满足,表单页本身是过渡页。B 的 resolver 导出半件无条件正确(单一 resolver、删除 console 本地子集是纯删除,消灭"同一 prose 契约的两个读者"),单独立卡入队;是否挂 ConsoleLayout 侧栏,留在该卡上待维护者定夺。
裁决 Q2(spec docstring 仍写无条件 thank-you 默认):A — 契约陈述半件按卡片原文留在 objectstack#7245 源卡,归 objectstack 车道,此处不越车道代办。
范围外发现 #4278(form action 编辑打开空建单、提交成重复 INSERT——今天用户可撞):采纳为 bug 入队,gated on #4279 落地后立即派发(同 FormPage 面)。
Flipping ready + auto-merge。
Generated by Claude Code
- Deliverable 2 完整:
Closing as completed — theme PM for objectstack#8668 (Seat B), session
session_018x7oGZF6qqdUD1Km4e9n5a.PR #4279 was ACCEPTed and merged 2026-08-11T11:01Z, delivering both ruling deliverables (internal
/forms/:namenested in the console shell; internal submit defaulting to redirect-to-created-record).The one item the PR deliberately left open — the sidebar/breadcrumb — was ruled in the ACCEPT itself (Q1 → A, accepted as delivered, on the grounds that mounting app-scoped
ConsoleLayouton an app-less route invents an app and writes that guess into shared navigation state). The resolver-export half was split out to its own card, #4280, where the remaining "should it mount the sidebar" question now lives. Q2 (the spec docstring) was routed to objectstack#7245.So nothing remains on this card's own scope. It carried a stale
pm:dispatchedfor three days because the merge-time paired write was never made; the label is dropped in the same action as this close.Follow-ups tracked elsewhere, not lost: #4280 (sidebar rider + resolver export), #4278 (
?recordId=ignored on this route). Full orphan re-verification: objectstack#8668.
Generated by Claude Code
- added a commit that references this issue
on Aug 17, 2026
Part of objectstack-ai/objectstack#7245 — the renderer half, migrated here per the file-at-destination ruling (objectstack-ai/objectstack#7167). Filed by the objectstack devx PM seat (session
session_01YS2qzDAn3CpWdY7uBX9bFQ); the contract-statement half stays on the source card.Maintainer ruling (2026-08-10, quoted verbatim from objectstack-ai/objectstack#7245)
Measured starting points (from the source card, browser-verified there at pin
09987b680d53)apps/console/src/App.tsx(~line 233):/forms/:nameis a top-level route, a sibling of the app-shell routes — the rendered page has no sidebar, navigation, or breadcrumb.apps/console/src/components/FormPage.tsx:const behavior = loaded?.form?.submitBehavior ?? { kind: 'thank-you' }— the anonymous-form confirmation is the default for internal authed submits too, so a user who just created a record is told "Your submission has been received" with no link to the record.packages/core/src/actions/ActionRunner.tsexecuteFormis the navigating entry point.Deliverables
mode="internal"/forms/:nameroute inside the console shell layout (keep the route itself — ruling point 1). The public/f/:slugpath keeps its chrome-less rendering.FormPage's submit-behavior default mode-aware:mode="internal"⇒ redirect to the created record; public mode keeps{ kind: 'thank-you' }(ruling point 2). A form view's explicitsubmitBehaviorstill wins in both modes.The source card's remaining question ("dialog instead of route?") is settled by the ruling: keep the route.