diff --git a/content/docs/protocol/objectui/actions.mdx b/content/docs/protocol/objectui/actions.mdx
index 7d3fc06d38..53b2afc6ec 100644
--- a/content/docs/protocol/objectui/actions.mdx
+++ b/content/docs/protocol/objectui/actions.mdx
@@ -49,7 +49,7 @@ The `type` field selects how an action is dispatched. The complete enum is **`sc
| `flow` | Invoke a Screen/automation Flow by name. | Yes |
| `modal` | Open a modal/page by name. | Yes |
| `api` | Call an API endpoint (`method` defaults to `POST`). | Yes |
-| `form` | Open a FormView by name, routed to `/console/forms/:name`. | Yes |
+| `form` | Open a FormView by name, routed to `/console/forms/:name` **inside the console shell**; the submit lands on the created record. | Yes |
`target` is the canonical binding for every non-`script` type, and since protocol 17 it is the **only** one. The deprecated `execute` alias was removed (#3855): authoring it is rejected with an error naming the replacement, not silently stripped. Run `os migrate meta --from 16` to rewrite existing sources automatically — the removal also ships in the machine-readable change manifest (`spec-changes.json`), which composes across however many majors you are jumping.
@@ -159,6 +159,33 @@ type: form
target: customer_quick_edit
```
+#### What `type: 'form'` promises
+
+`type: 'form'` is the platform's first-class way to open an object's form — the shape that replaced the `type: 'modal'` object fallback — so its contract is stated here rather than left to each renderer to decide (ruled 2026-08-10 on [#7245](https://github.com/objectstack-ai/objectstack/issues/7245)):
+
+1. **It renders in-shell.** The internal form route nests **inside** the console shell: sidebar, app navigation and breadcrumb stay. A form action never drops the operator onto an unchromed standalone page with the browser Back button as the only way home. The route survives too — `/console/forms/:name` stays deep-linkable and bookmarkable, which is what a route buys you over a transient dialog.
+2. **A successful submit lands on the created record.** On the internal path the default post-submit behavior is a **redirect to the record that was just created**, not a confirmation panel. The `thank-you` panel is the default for the **public** `/console/f/:slug` path only, where there is no record the anonymous submitter is allowed to see.
+3. **An explicit `submitBehavior` on the FormView always wins**, in either mode. The defaults above are what you get for declaring nothing; they are not a ceiling.
+
+| | Public path (`/console/f/:slug`) | Form action / internal path (`/console/forms/:name`) |
+| --- | --- | --- |
+| Audience | anonymous visitors | authed operators — where `type: 'form'` sends them |
+| Chrome | standalone page, no console chrome | nested inside the console shell |
+| Default when `submitBehavior` is omitted | `thank-you` confirmation panel | redirect to the created record |
+
+The four `submitBehavior` kinds and their options are documented on the [Forms guide](/docs/ui/forms).
+
+
+**Current renderer status (2026-08-10).** The console shipping at the pinned
+`.objectui-sha` does **not** implement this contract yet: it renders
+`/console/forms/:name` as a top-level page outside the shell, and it falls back
+to the `thank-you` panel in both modes. The statement above is the contract —
+the renderer half is tracked in ObjectUI and linked from the
+[#7245 thread](https://github.com/objectstack-ai/objectstack/issues/7245). Until
+it lands, an internal FormView that must land on its new record has to declare
+`submitBehavior` explicitly.
+
+
### Modal Actions
Open a modal/page by name. `target` is resolved as a **page** first, then as an object (which opens that object's create/edit form); when it names neither, the action falls through to its server-side handler.
diff --git a/content/docs/ui/actions.mdx b/content/docs/ui/actions.mdx
index 700cbb18cb..c7850372e0 100644
--- a/content/docs/ui/actions.mdx
+++ b/content/docs/ui/actions.mdx
@@ -21,7 +21,7 @@ The types you'll actually use:
| `url` | Navigate / open a link | `target` is the URL (`${ctx.record.id}` interpolation supported) |
| `modal` | Open a modal page — client-side only, no server dispatch | `target` names the modal page (to collect input *and* run logic, use `script` + `params`) |
| `api` | Call an HTTP endpoint directly | `target` is the endpoint; `method` / `bodyShape` / `bodyExtra` shape the request |
-| `form` | Open a form view, prefilled with the current record | `target` names the FormView; routed to `/forms/:target?recordId=…` |
+| `form` | Open a form view, prefilled with the current record — in-shell, and the submit lands on the created record ([contract](/docs/protocol/objectui/actions#what-type-form-promises)) | `target` names the FormView; routed to `/forms/:target?recordId=…` |
Prefer `confirmText`/`params` over the two action properties that are **not
diff --git a/content/docs/ui/forms.mdx b/content/docs/ui/forms.mdx
index b6f529c2d9..36a1e9c997 100644
--- a/content/docs/ui/forms.mdx
+++ b/content/docs/ui/forms.mdx
@@ -17,7 +17,7 @@ Both modes:
- Use the same `FormView` Zod schema (`@objectstack/spec/ui`)
- Render through the same `FormPage` renderer shipped by the ObjectUI console (a separate package/repo)
- Honor `?prefill_=` URL params
-- Honor `submitBehavior` (thank-you / redirect / continue / next-record)
+- Honor `submitBehavior` (thank-you / redirect / continue / next-record) — with **mode-aware defaults** when it is omitted (see [§8](#8-submitbehavior--what-happens-after-submit))
A **public form** is the Salesforce *Web-to-Lead* style embeddable form — declare a `FormView` with `sharing.allowAnonymous: true`, give it a `publicLink`, and the framework wires the anonymous REST endpoints automatically.
@@ -341,14 +341,27 @@ formViews: {
| `kind` | Renderer behavior |
|---|---|
-| `thank-you` *(default)* | Replace the form with a confirmation panel (`title`, `message`). |
+| `thank-you` | Replace the form with a confirmation panel (`title`, `message`). |
| `redirect` | `window.location.assign(url)` after `delayMs` (defaults to 0). |
| `continue` | Re-read prefill values and reset state — user can submit another response without reload. |
| `next-record` | Stub for queue contexts; falls back to thank-you when no queue is wired. |
+### The default is mode-aware
+
+`submitBehavior` is optional, and **what you get for omitting it depends on the mode** — a public collection form and an authed create have different right answers (ruled 2026-08-10 on [#7245](https://github.com/objectstack-ai/objectstack/issues/7245)):
+
+| Mode | Default when `submitBehavior` is omitted | Why |
+|---|---|---|
+| **Public** (`/console/f/:slug`) | `{ kind: 'thank-you' }` — the confirmation panel | The anonymous submitter may not read the record back, so a receipt is all there is to show. |
+| **Internal** (`/console/forms/:name`) | Redirect to the **created record** | An operator who just created a record belongs on that record, not on a "submission received" receipt. |
+
+An explicit `submitBehavior` overrides the default in either mode, so nothing here removes an option — it only changes what an author gets for declaring nothing.
+
+> **Current renderer status (2026-08-10).** The console shipping at the pinned `.objectui-sha` still applies `thank-you` as the default in **both** modes. The table above is the contract; the renderer half is tracked in ObjectUI and linked from the [#7245 thread](https://github.com/objectstack-ai/objectstack/issues/7245). Until it lands, declare `submitBehavior` explicitly on an internal FormView that must land somewhere specific.
+
## 9. `type: 'form'` action — declarative form launchers
-App actions can declare `type: 'form'` to open a FormView without resorting to free-form URLs. The `target` is the FormView name; the runtime navigates to `/console/forms/:name`.
+App actions can declare `type: 'form'` to open a FormView without resorting to free-form URLs. The `target` is the FormView name; the runtime navigates to `/console/forms/:name` **inside the console shell** — the operator keeps the sidebar, navigation and breadcrumb, and a successful submit lands on the created record per the mode-aware default above. The full contract is stated on the [Action Protocol page](/docs/protocol/objectui/actions#what-type-form-promises).
{/* os:check */}
```ts
diff --git a/content/docs/ui/public-data-collection.mdx b/content/docs/ui/public-data-collection.mdx
index 8b6a170748..14fddf5e3a 100644
--- a/content/docs/ui/public-data-collection.mdx
+++ b/content/docs/ui/public-data-collection.mdx
@@ -34,6 +34,8 @@ formViews: {
}
```
+The `submitBehavior` above sets the **copy**, not the behavior: `thank-you` is already the default on the public path, because an anonymous submitter may not read the record back. The internal path defaults the other way — a `type: 'form'` action lands the operator on the record it just created — so do not carry a public form's confirmation panel over to an internal one by habit. See [`submitBehavior` is mode-aware](/docs/ui/forms#the-default-is-mode-aware).
+
That `sharing` block wires the anonymous endpoints automatically:
```