diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e870d2..88a8ec5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,18 @@ Entries are newest first. Keep their headings stable. Publish material correctio new entries so readers who advanced their checkpoint see them. Entries explain what changed, why, when it applies, and any useful small example or verification; they need not reproduce commit logs. +## 2026-09-28: Associated creation without standalone routes + +**Compiler and Skills — coordinated 0.8.0 source candidate, unreleased.** An associated form can use a create +definition without exposing standalone New/Create routes. For example, keep Rating's create inputs, authorization, +and Movie collection form while omitting `new` and `create` from Rating's `resource_routes`. The nested route still +supplies its Movie; a retained standalone `create.return_to` does not choose the nested form's destination. + +Standalone Create without New or a return destination now returns 201 on success even when its definition also +serves associated forms; that shared case previously redirected with 303. Invalid submissions return 422. Review +the [Scaffolds guidance](skills/create-full-stack-app/references/foundation-plan-023.md#scaffolds) for required-parent +gaps and shared-Policy form-entry limits before handing unfinished application behavior to the implementation agent. + ## 2026-09-27: Bookmark assets without a planning option **Compiler and authoring contract — coordinated source candidate, unreleased.** The next Plan contract includes diff --git a/evals/README.md b/evals/README.md index cea5588..9c98f83 100644 --- a/evals/README.md +++ b/evals/README.md @@ -37,6 +37,7 @@ specifically prepared server-backed run. Never print or commit private `.firstdr - `reconcile-optional-field-origin` - `author-deliberate-return-overrides` - `preserve-current-location-return` +- `author-associated-only-creation` - `choose-conventional-validations` - `preserve-structured-validation-gap` - `retain-outside-vocabulary-requirement` diff --git a/evals/create-full-stack-app/cases.json b/evals/create-full-stack-app/cases.json index 4d476b4..759c162 100644 --- a/evals/create-full-stack-app/cases.json +++ b/evals/create-full-stack-app/cases.json @@ -236,6 +236,25 @@ ], "artifacts": [] }, + { + "id": "author-associated-only-creation", + "should_trigger": true, + "prompt": "In this local public movie-rating draft, Ratings should be created only from a Movie's ratings collection, never from a standalone New page or standalone POST. Keep the current Rating create definition, inputs, authorization and standalone return choice unchanged; change only its standalone new/create route exposure. Preserve the other routes, model, empty data and no Account choice. Update the Plan locally and explain how the nested route supplies its Movie and where a successful nested save returns. Do not submit or Compile.", + "expectations": [ + "Removes only new and create from rating.scaffold.resource_routes while preserving the create definition and the Movie collection create_form; does not add a fictitious standalone route to satisfy the schema.", + "Preserves all subject identities, authored inputs, authorization, return choices and unrelated routes without introducing bindings or Accounts.", + "Explains that the authorized Movie from the URL supplies the nested parent and that competing submitted parent values cannot replace it; the scoped form retains other editable inputs and ordinary validation behavior.", + "Explains that the retained create.return_to applies only to standalone creation, which is no longer exposed; nested saves return to the Movie's ratings collection. Does not transfer that override to create_form.return_to without a separate authored choice.", + "Distinguishes local schema/authoring checks from Analysis and generated-app proof, and makes no network submission or Compilation claim." + ], + "artifacts": [ + { + "path": "evals/create-full-stack-app/fixtures/redirect-movies.foundation-plan.json", + "role": "input", + "stage_as": ".firstdraft/foundation-plan.json" + } + ] + }, { "id": "choose-conventional-validations", "should_trigger": true, @@ -502,7 +521,7 @@ "should_trigger": true, "prompt": "The staged Plan's domain, selected iPhone client, Movie icon and Field, and public-index Scaffold are intentional. A successful plan push was followed by plan status --wait, which printed the attached valid analysis. Review the result, but do not compile it.", "expectations": [ - "Branches on analysis.status valid and reports analyzer release foundation-plan-rails/application-2026-09-27-bookmark-assets, graph version 1, and the absence of diagnostics.", + "Branches on analysis.status valid and reports analyzer release foundation-plan-rails/application-2026-09-28-controller-create-context, graph version 1, and the absence of diagnostics.", "Reports the complete empty firstdraft.foundation-gaps/2 object and exact attached analysis.gap_set_sha256, which the CLI validated against the attached GapSet bytes, rather than omitting the reviewed support result; never substitutes a fixture, historical, or other Project's digest.", "Preserves the complete staged Plan unchanged, including its domain, native.ios request, semantic icon, required title Field, and exact public-index Scaffold.", "Recognizes that the prepared 2026-09 boundary admits this bounded web public-index and selected iPhone request, while analysis alone does not prove generated output.", diff --git a/evals/create-full-stack-app/fixtures/analysis-failed-analysis.json b/evals/create-full-stack-app/fixtures/analysis-failed-analysis.json index d325aa8..aa5dc10 100644 --- a/evals/create-full-stack-app/fixtures/analysis-failed-analysis.json +++ b/evals/create-full-stack-app/fixtures/analysis-failed-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000902", "graph_version": 1, "head_source_sha256": "9f07c5b77e6dc96c2cd733ac04781abd5b5bfc6ea336a36c4f49a0116bd7f47e", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" diff --git a/evals/create-full-stack-app/fixtures/appearance-current-analysis.json b/evals/create-full-stack-app/fixtures/appearance-current-analysis.json index 836cdac..2b63c3c 100644 --- a/evals/create-full-stack-app/fixtures/appearance-current-analysis.json +++ b/evals/create-full-stack-app/fixtures/appearance-current-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000906", "graph_version": 1, "head_source_sha256": "d6f15d4062c95574c40f47a993ea7d24dd920fc10736306ae64c3461b5fda15f", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" @@ -25,9 +25,9 @@ "graph_version": 1 }, "analysis": { - "release": "foundation-plan-rails/application-2026-09-27-bookmark-assets" + "release": "foundation-plan-rails/application-2026-09-28-controller-create-context" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" @@ -45,7 +45,7 @@ } ] }, - "gap_set_sha256": "15b24b73c98d2da9abb77d9ca364f950ab26ee8a83ad37289f7efda81a39e8d0", + "gap_set_sha256": "82a2dedf411a3ea673badc5319d90acf7fcaea098bc7baf6b3364796c965a8d4", "started_at": "2026-07-30T12:03:00.000Z", "completed_at": "2026-07-30T12:03:01.000Z" } diff --git a/evals/create-full-stack-app/fixtures/application-intent-valid-analysis.json b/evals/create-full-stack-app/fixtures/application-intent-valid-analysis.json index cc850e5..d624d48 100644 --- a/evals/create-full-stack-app/fixtures/application-intent-valid-analysis.json +++ b/evals/create-full-stack-app/fixtures/application-intent-valid-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000905", "graph_version": 1, "head_source_sha256": "b7369e48d1e01418a045d82b1a46e1b4a24b1d472ed20add0af46d402fea39fc", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" @@ -25,16 +25,16 @@ "graph_version": 1 }, "analysis": { - "release": "foundation-plan-rails/application-2026-09-27-bookmark-assets" + "release": "foundation-plan-rails/application-2026-09-28-controller-create-context" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" }, "gaps": [] }, - "gap_set_sha256": "44d81a907339289088976639534111e6fee878d1f3ad5692df80094bf75abee3", + "gap_set_sha256": "dbee576eddfebf638104f92d1a28843e006ce4bfe94f7eb84d53d2efa9dee4cd", "started_at": "2026-07-30T12:02:00.000Z", "completed_at": "2026-07-30T12:02:01.000Z" } diff --git a/evals/create-full-stack-app/fixtures/issues-found-analysis.json b/evals/create-full-stack-app/fixtures/issues-found-analysis.json index bf069f6..bc59103 100644 --- a/evals/create-full-stack-app/fixtures/issues-found-analysis.json +++ b/evals/create-full-stack-app/fixtures/issues-found-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000901", "graph_version": 1, "head_source_sha256": "603567c344fe5b0e46d20eeec229c5a2726fa1b4212467dc713c38295b2df85f", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" diff --git a/evals/create-full-stack-app/fixtures/mixed-application-issues-analysis.json b/evals/create-full-stack-app/fixtures/mixed-application-issues-analysis.json index f40a495..598c7f2 100644 --- a/evals/create-full-stack-app/fixtures/mixed-application-issues-analysis.json +++ b/evals/create-full-stack-app/fixtures/mixed-application-issues-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000907", "graph_version": 1, "head_source_sha256": "00723c6e838dcd4c1c2ed96d2a82c64806843b619cbba6edf63dede4d829ba15", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" diff --git a/evals/create-full-stack-app/fixtures/recurring-issues-analysis.json b/evals/create-full-stack-app/fixtures/recurring-issues-analysis.json index d2244ca..706863e 100644 --- a/evals/create-full-stack-app/fixtures/recurring-issues-analysis.json +++ b/evals/create-full-stack-app/fixtures/recurring-issues-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000904", "graph_version": 2, "head_source_sha256": "ce6c5037e410718e871eecd89f9ce55797fdb88e27a13ce19a7eeb91ba30d032", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" diff --git a/evals/create-full-stack-app/fixtures/superseded-analysis.json b/evals/create-full-stack-app/fixtures/superseded-analysis.json index be97fa2..4ac840d 100644 --- a/evals/create-full-stack-app/fixtures/superseded-analysis.json +++ b/evals/create-full-stack-app/fixtures/superseded-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000903", "graph_version": 2, "head_source_sha256": "9f07c5b77e6dc96c2cd733ac04781abd5b5bfc6ea336a36c4f49a0116bd7f47e", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" diff --git a/evals/create-full-stack-app/fixtures/theme-toggle-analysis.json b/evals/create-full-stack-app/fixtures/theme-toggle-analysis.json index ee54a33..7e04c33 100644 --- a/evals/create-full-stack-app/fixtures/theme-toggle-analysis.json +++ b/evals/create-full-stack-app/fixtures/theme-toggle-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000909", "graph_version": 1, "head_source_sha256": "d9c3fe7a339e0fe9074e13fe3f24faf27ab4cfd5c428be0bcf13f19dfb34665b", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" @@ -25,9 +25,9 @@ "graph_version": 1 }, "analysis": { - "release": "foundation-plan-rails/application-2026-09-27-bookmark-assets" + "release": "foundation-plan-rails/application-2026-09-28-controller-create-context" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" @@ -55,7 +55,7 @@ } ] }, - "gap_set_sha256": "055eab90b7f72c0fbc184774fc299913e44db8fce1686bc5585f165bd4d9f4ca", + "gap_set_sha256": "f35279796f263ebbca731782444a38be445def7ff3606fc5c5b2cba265be63b8", "started_at": "2026-07-30T12:03:00.000Z", "completed_at": "2026-07-30T12:03:01.000Z" } diff --git a/evals/create-full-stack-app/fixtures/unsupported-graph-analysis.json b/evals/create-full-stack-app/fixtures/unsupported-graph-analysis.json index cb01599..ee7ab08 100644 --- a/evals/create-full-stack-app/fixtures/unsupported-graph-analysis.json +++ b/evals/create-full-stack-app/fixtures/unsupported-graph-analysis.json @@ -7,8 +7,8 @@ "id": "01900000-0000-7000-8000-000000000908", "graph_version": 1, "head_source_sha256": "3318da62e564ae5b8e6e3f549912354396dbe7b5e694c10a12e2e589ce0ba125", - "analyzer_release": "foundation-plan-rails/application-2026-09-27-bookmark-assets", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "analyzer_release": "foundation-plan-rails/application-2026-09-28-controller-create-context", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" @@ -25,9 +25,9 @@ "graph_version": 1 }, "analysis": { - "release": "foundation-plan-rails/application-2026-09-27-bookmark-assets" + "release": "foundation-plan-rails/application-2026-09-28-controller-create-context" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context", "target": { "id": "rails", "profile": "rails-sketch/2026-09-bookmark-assets" @@ -55,7 +55,7 @@ } ] }, - "gap_set_sha256": "9da351eaa2f52e55d5915db9cef43b67f01e372cf03f0977fca76de5dd762b5a", + "gap_set_sha256": "2a81b30fe6903beb7de5757c44dc443a6c40794968ee7ab7e5230f4312b56cce", "started_at": "2026-07-30T12:04:00.000Z", "completed_at": "2026-07-30T12:04:01.000Z" } diff --git a/release/compatibility.json b/release/compatibility.json index 41fd00b..2b20ab7 100644 --- a/release/compatibility.json +++ b/release/compatibility.json @@ -4,7 +4,7 @@ "version": "0.8.0", "plugin_source": { "package": "@firstdraft.com/claude-code", - "tarball_sha256": "02a7ffe39c563e064890070df174cb34c8be28f63721a32e322b1d43851dc640" + "tarball_sha256": "6019486584f971fa6e405de340e28115b7447a7efd0539296394a7eb5e41c6e8" }, "requires": { "api_contract": [ diff --git a/script/cli-contract/config.mjs b/script/cli-contract/config.mjs index 206dffa..19a3854 100644 --- a/script/cli-contract/config.mjs +++ b/script/cli-contract/config.mjs @@ -42,9 +42,9 @@ export const apiToken = "canary-private-api-token"; export const foundationPlanFormat = "firstdraft.foundation-plan.sketch/0.23"; export const analyzerRelease = - "foundation-plan-rails/application-2026-09-27-bookmark-assets"; + "foundation-plan-rails/application-2026-09-28-controller-create-context"; export const compilerRelease = - "foundation-plan-rails/compiler-application-2026-09-27-bookmark-assets"; + "foundation-plan-rails/compiler-application-2026-09-28-controller-create-context"; export const compilationTarget = { id: "rails", profile: "rails-sketch/2026-09-bookmark-assets", diff --git a/skills/create-full-stack-app/references/foundation-plan-0.23.schema.json b/skills/create-full-stack-app/references/foundation-plan-0.23.schema.json index 1ffafaf..a1b21ec 100644 --- a/skills/create-full-stack-app/references/foundation-plan-0.23.schema.json +++ b/skills/create-full-stack-app/references/foundation-plan-0.23.schema.json @@ -3017,26 +3017,6 @@ } } }, - { - "if": { - "required": [ - "create" - ] - }, - "then": { - "required": [ - "resource_routes" - ], - "properties": { - "resource_routes": { - "type": "array", - "contains": { - "const": "create" - } - } - } - } - }, { "if": { "properties": { @@ -4932,7 +4912,7 @@ }, "scaffoldCreateForm": { "title": "Associated create form", - "description": "An associated-create entry point backed by the target Entity's selected create definition. Rails derives scoped New and POST routes and supplies the inverse Reference from the route-bound parent.", + "description": "An associated-create entry point backed by the target Entity's create definition, independently of top-level route selection. Rails derives scoped New and POST routes and supplies the inverse Reference from the route-bound parent.", "type": "object", "additionalProperties": false, "properties": { diff --git a/skills/create-full-stack-app/references/foundation-plan-023.md b/skills/create-full-stack-app/references/foundation-plan-023.md index ca74c03..5d80157 100644 --- a/skills/create-full-stack-app/references/foundation-plan-023.md +++ b/skills/create-full-stack-app/references/foundation-plan-023.md @@ -76,8 +76,8 @@ generated output, not observed application behavior, device installation, or dep The bundled schema was copied byte-for-byte from `docs/architecture/design/foundation-plan.schema.json` at Service revision -`ff0b6ac9cc6fab5aa2a63c455d3da73469211c43` and has SHA-256 -`6dd98b424fd1744d1e5d3a40fb51f0a22f89e77b58a175b08e29e6c8e3aed7ba`. This is exact contract provenance, not +`806090dd36113c856fbd63d702ee74e353e9a7e2` and has SHA-256 +`242889fae4e1031f3be7aa3d226a9966402e7516f0055f506bb40ce05d1430dd`. This is exact contract provenance, not release or execution evidence. The source candidate and pinned contract check use the exact reviewed CLI revision and runtime digest in @@ -515,10 +515,11 @@ records the applicable native consequence instead of borrowing Web authorization ### Scaffolds -The schema couples each selected standard `resource_routes` member to its matching `index`, `show`, `create`, -`update`, or `destroy` definition. `new` requires create and `edit` requires update. A custom `profile` requires a -sibling `update`; an Account may instead author `update` alone for its derived settings routes. Every authored -request declares public or Policy-controlled authorization; do not infer access from route shape. +`scaffold.create` defines creation inputs, bindings, and authorization independently of standalone route exposure. +An associated `create_form` can use that definition without selecting standalone `new` or `create` in +`resource_routes`. Each selected standalone route still requires its matching definition; `new` requires selected +`create`, and `edit` requires `update`. A custom `profile` requires a sibling `update`; an Account may instead author +`update` alone for its derived settings routes. Do not infer authorization from route shape. The current Web target realizes bounded standard routes, public and Policy-controlled request checks, direct and recursive projections, Predicate and Ordering selection, cursor pagination, Field and Association inputs, server @@ -532,17 +533,35 @@ the exact associated-create parent. Protected forms authorize before loading opt may target only the realized Account Reference in a non-public Account-backed context; it cannot silently turn a public create into an authenticated request. +If an admitted associated form supplies a required parent but the selected standalone create has no source for it, +the standalone endpoint remains generated with its authored inputs and authorization plus a `partially_generated` +gap naming the missing value. Preserve working associated creation and record the unfinished standalone behavior +for implementation; do not invent an editable parent or binding. Unrelated required values still need an admitted +source, and a selected standalone create with no admitted associated form needs the complete source set. + +New and Create share the create Policy. New authorizes after URL-parent assignment and explicit bindings, before +editable values exist; Create authorizes the submitted record. A Policy requiring an editable `token.author` to +equal `current_account` can allow a valid POST while denying the empty New form and hiding its Add link. A separate +form-entry Policy is not an FP option. Preserve the editable input and submitted-record authorization, and carry +the required application form-entry work into [implementation notes](modeling-guide.md#retain-implementation-requirements). +Qualifying fixtures do not fix that application behavior; add successful New/browser coverage after repairing it. + Omit `return_to` when the conventional interaction is intended: standalone New/Edit returns to the saved record, scoped associated create returns to its collection, destroy returns to the record's collection, and profile updates return to Account. If a preferred record or collection route is unavailable, Rails uses the admitted collection or home fallback. An explicit override remains authored meaning and must itself be lowerable; it does not silently become a default. The browser supplies no destination URL, hidden return input, or history-based redirect. +A selected standalone create without New or an explicit return uses an empty `201 Created` response on success +and `422` for validation failure, even when its definition also serves associated forms. Authorization denials keep +their own status. Form-backed writes redirect with `303` on success and redisplay entered values and errors on failure. + Admitted details collections show a bounded preview with a separate paginated full collection page. A supported `create_form` selects an Add entry point on that collection page and a dedicated scoped New page, such as `/movies/42/credits/new`, posting to `/movies/42/credits`. Rails supplies `params[:movie_id]` from the route; child attributes remain under `params[:credit]`. The authorized parent association builds the child, with no hidden -parent input. The shared target new/create actions and form retain the other inputs and validation errors. +parent input; a competing submitted parent does not replace it. Shared target new/create actions select the form +for that context and retain its other inputs and validation errors. Ordinary child edit/update/destroy routes stay flat. Preview rows contain selected properties and an optional authorized details link, without inline mutation controls. If no target show route is selected, its flat mutation routes can remain unlinked starter code. Do not invent a show route or discard authored properties to fill that gap. diff --git a/test/repository.test.mjs b/test/repository.test.mjs index 1d22a3e..3051cf5 100644 --- a/test/repository.test.mjs +++ b/test/repository.test.mjs @@ -44,9 +44,9 @@ const reviewedFixtureAnalyzerRelease = "foundation-plan-rails/application-2026-08-28-reviewed-realization"; const reviewedFixtureCompilerRelease = "foundation-plan-rails/compiler-application-2026-08-28-reviewed-realization"; -const currentFoundationPlanServiceBaseline = "ff0b6ac9cc6fab5aa2a63c455d3da73469211c43"; +const currentFoundationPlanServiceBaseline = "806090dd36113c856fbd63d702ee74e353e9a7e2"; const foundationPlanSchemaDigest = - "6dd98b424fd1744d1e5d3a40fb51f0a22f89e77b58a175b08e29e6c8e3aed7ba"; + "242889fae4e1031f3be7aa3d226a9966402e7516f0055f506bb40ce05d1430dd"; const currentFoundationPlanSchemaBaseline = currentFoundationPlanServiceBaseline; const prettyJsonSha256 = (value) => createHash("sha256") diff --git a/test/ui-foundation-authoring.test.mjs b/test/ui-foundation-authoring.test.mjs index bd00040..5f41ac4 100644 --- a/test/ui-foundation-authoring.test.mjs +++ b/test/ui-foundation-authoring.test.mjs @@ -47,6 +47,21 @@ test("default returns retain route coupling and closed authored context", () => assert(!validate("scaffoldCreateForm", { return_to: "/movies/42" })); }); +test("associated creation needs a definition without standalone route exposure", async () => { + const plan = JSON.parse(await readFile( + new URL("../evals/create-full-stack-app/fixtures/redirect-movies.foundation-plan.json", import.meta.url), + "utf8", + )); + const rating = plan.application.entities.find(({ key }) => key === "rating"); + rating.scaffold.resource_routes = rating.scaffold.resource_routes.filter( + (route) => route !== "new" && route !== "create", + ); + assert(ajv.getSchema(schema.$id)(plan)); + + assert(validate("scaffold", { create: rating.scaffold.create })); + assert(!validate("scaffold", { resource_routes: ["create"] })); +}); + test("Entity validation errors belong to a Field or Reference", () => { const uniqueness = { subject_uuid: "01900000-0000-7000-8000-000000000001",