diff --git a/README.md b/README.md index 89e261a..575ed04 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ canonical Skills and bundled CLI are packaged once for Claude Code and Codex. UI app's own design and components. The public catalog and Drawing Board pins determine what an installed workspace actually receives. -Source candidate `0.5.0` requires CLI `0.5.0`, Service API `0.5`, and Foundation Plan `0.21`. It preserves the planning +Source candidate `0.6.0` requires CLI `0.6.0`, Service API `0.6`, and Foundation Plan `0.22`. It preserves the planning workspace under `.firstdraft/design/` and defaults to current-folder local output. Use explicit `--github` for server Publication. Its identities belong in [release compatibility](release/compatibility.json). The [public catalog](.claude-plugin/marketplace.json) owns the @@ -22,7 +22,7 @@ local folder; a Drawing Board clone and GitHub push are unnecessary. The - the portable create-full-stack-app Skill and its task routing; - deferred UI extension and consistency-review Skill auditions; -- beginner-to-machine-reference authoring guidance for Foundation Plan 0.21; +- beginner-to-machine-reference authoring guidance for Foundation Plan 0.22; - the exact schema, examples, and review checklists packaged with the Skill; - behavioral evaluations for agent workflow changes; - shared Claude/Codex plugin assembly around the canonical Skills and reviewed CLI package; and @@ -39,8 +39,8 @@ behavior. This repository teaches an agent how to use those contracts without cr | Understand the installed workflow | [Skill entrypoint](skills/create-full-stack-app/SKILL.md) | | Continue or review an app's UI | The app's `UI.md` and shared components; [UI Skill status](#ui-continuation) | | Change Plan authoring guidance | [Skill entrypoint](skills/create-full-stack-app/SKILL.md), then [modeling guide](skills/create-full-stack-app/references/modeling-guide.md) | -| Check current Foundation Plan capability | [Foundation Plan reference](skills/create-full-stack-app/references/foundation-plan-021.md) | -| Inspect exact Plan structure | [Bundled schema](skills/create-full-stack-app/references/foundation-plan-0.21.schema.json) | +| Check current Foundation Plan capability | [Foundation Plan reference](skills/create-full-stack-app/references/foundation-plan-022.md) | +| Inspect exact Plan structure | [Bundled schema](skills/create-full-stack-app/references/foundation-plan-0.22.schema.json) | | Add or run behavioral evaluations | [Evaluation guide](evals/README.md) | | Inspect a dated observation | [Evidence archive](evidence/README.md) | | Prepare or promote a release | [Release runbook](RELEASING.md) | diff --git a/RELEASING.md b/RELEASING.md index 4d6cc19..68f29c7 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -8,14 +8,15 @@ longer part of an ordinary release. Coordinate the service, CLI, and Skills thro [`release/compatibility.json`](release/compatibility.json) owns the candidate version, compatible CLI/API/Plan identities, and deterministic package SHA-256. The current source candidate is -`@firstdraft.com/claude-code@0.5.0` with CLI `0.5.0`, API `>= 0.5.0`, `< 0.6.0`, and Plan `sketch/0.21`. +`@firstdraft.com/claude-code@0.6.0` with CLI `0.6.0`, API `>= 0.6.0`, `< 0.7.0`, and Plan `sketch/0.22`. The [marketplace manifest](.claude-plugin/marketplace.json) independently selects a published plugin version; retain its selection until the intended new version is actually published. Source compatibility is not public catalog selection. Query npm when releasing rather than treating a dated distribution snapshot as current. -Plan `0.21` adds `appearance.theme: "toggle"` and replaces the sole accepted `0.20` input format. API, CLI, and -plugin `0.5.0` record that input and artifact compatibility break. Target `rails-sketch/2026-09`, local-output -defaults, and `.firstdraft/design/` stay the same. There is no retained-Project migration or compatibility bridge. +Plan `0.22` adds optional `application.pwa` and replaces the sole accepted `0.21` input format. Omission and `true` +enable ordinary online installation metadata; `false` omits it. API, CLI, and plugin `0.6.0` record that input and +artifact compatibility break. Target `rails-sketch/2026-09`, local-output defaults, and `.firstdraft/design/` stay +the same. There is no retained-Project migration or compatibility bridge. Use an ordinary pre-1.0 minor bump for a breaking compatibility change and a patch bump for a compatible change. Never reuse a published npm version, protected release tag, or marketplace version for different package bytes. diff --git a/docs/README.md b/docs/README.md index ba67293..e780de7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,8 +13,8 @@ historical observations intentionally have different owners. | How do I repair an existing npm default? | [npm default repair](npm-promotion.md) | | What does the agent execute? | [`SKILL.md`](../skills/create-full-stack-app/SKILL.md) | | How does an agent extend or review generated UI? | The app's `UI.md`; [deferred Skill status](../README.md#ui-continuation) | -| What is exact Plan syntax? | [JSON Schema](../skills/create-full-stack-app/references/foundation-plan-0.21.schema.json) | -| What capability is currently described? | [Foundation Plan reference](../skills/create-full-stack-app/references/foundation-plan-021.md) | +| What is exact Plan syntax? | [JSON Schema](../skills/create-full-stack-app/references/foundation-plan-0.22.schema.json) | +| What capability is currently described? | [Foundation Plan reference](../skills/create-full-stack-app/references/foundation-plan-022.md) | | What happened in a particular run? | [`evidence/README.md`](../evidence/README.md), then one dated record | | What behavior should a fresh agent exhibit? | [`evals/README.md`](../evals/README.md), then one case | | Why do historical pins or limitations exist? | [Repository history](../evidence/repository-history.md) or [release history](../evidence/release-history.md) | diff --git a/evals/README.md b/evals/README.md index ee5535a..23a77b0 100644 --- a/evals/README.md +++ b/evals/README.md @@ -5,7 +5,7 @@ fixtures are review inputs, not execution evidence. Each case declares whether t artifacts are attached, staged into the project, or retained only as expected output. The current Plan/analysis fixtures below are synthetic contract inputs, not new Service observations. -Current authoring cases use Foundation Plan `firstdraft.foundation-plan.sketch/0.21` and target +Current authoring cases use Foundation Plan `firstdraft.foundation-plan.sketch/0.22` and target `rails-sketch/2026-09`. The `appearance-current` Plan/analysis and `resume-current` Plan fixtures replace their older inputs for new evaluations. The original `appearance-issues` Plan/analysis and `resume` Plan retain the exact bytes declared by the [September 14 qualification receipt](../evidence/2026-09-14-ui-authoring-skill-0.2.4-qualification.json). @@ -89,6 +89,8 @@ or live transport of implementation notes through GitHub Publication. - `android-preview-respects-provider-limit` - `preserve-partially-realized-appearance-intent` - `preserve-browser-theme-and-native-residual` +- `explain-default-browser-installation` +- `preserve-browser-installation-opt-out` - `correct-source-issue-alongside-capability-gap` - `analysis-failed-stop` - `standalone-status-binds-accepted-generation` diff --git a/evals/create-full-stack-app/cases.json b/evals/create-full-stack-app/cases.json index 465e001..829adb6 100644 --- a/evals/create-full-stack-app/cases.json +++ b/evals/create-full-stack-app/cases.json @@ -607,6 +607,44 @@ } ] }, + { + "id": "explain-default-browser-installation", + "should_trigger": true, + "prompt": "Before I decide whether to compile the staged Plan, explain its Add to Home Screen behavior on iPhone Safari and Android Chrome. I have not asked for offline access or a custom install prompt. Review only; do not edit or submit the Plan.", + "expectations": [ + "Recognizes omitted application.pwa as the target default of enabled online installation metadata and leaves that authored omission unchanged.", + "Explains application identity, suitable icons, the root start URL, and standalone display through the ordinary browser installation flow; keeps Home and its authentication/authorization behavior unchanged.", + "Does not add offline cache, a service worker, Web Push, a custom installation prompt, or a native client. Existing native selection is independent.", + "Separates metadata/HTTP checks, browser installation promotion, actual Add to Home Screen, standalone launch/navigation, and sign-in/relaunch. Says actual iPhone Safari and Android Chrome qualification is pending rather than treating source support or desktop emulation as device proof.", + "Leaves the staged Plan and subject UUIDs unchanged and runs no plan init, plan push, plan status, or plan compile." + ], + "artifacts": [ + { + "path": "evals/create-full-stack-app/fixtures/application-intent.foundation-plan.json", + "role": "input", + "stage_as": ".firstdraft/foundation-plan.json" + } + ] + }, + { + "id": "preserve-browser-installation-opt-out", + "should_trigger": true, + "prompt": "The staged Plan deliberately sets application.pwa to false. Explain exactly what that choice changes before I decide whether to compile. Keep my selected Home, browser theme, and both native clients. Does the opt-out stop someone from saving the site to their phone? Review only; do not edit or submit it.", + "expectations": [ + "Preserves explicit application.pwa false and explains that it omits the generated manifest route/template/link, installation-capable metadata, manifest request spec, and 192px installation icon together.", + "Explains that favicons, the Apple touch icon, ordinary theme metadata, legal pages, and signup acceptance remain. Does not promise that the opt-out prevents a browser from saving an ordinary site.", + "Keeps home_index movie, appearance.theme toggle, and both selected native clients unchanged; the PWA choice does not replace Home, relax authentication/authorization, remove Appearance, or select/drop a native client.", + "Does not invent offline/cache/push/prompt support or native launcher icon realization. No supplied analysis or compiled output means no current GapSet, generation, browser, or device execution result is claimed.", + "Leaves the complete staged Plan and subject UUIDs unchanged and runs no plan init, plan push, plan status, or plan compile." + ], + "artifacts": [ + { + "path": "evals/create-full-stack-app/fixtures/pwa-disabled.foundation-plan.json", + "role": "input", + "stage_as": ".firstdraft/foundation-plan.json" + } + ] + }, { "id": "correct-source-issue-alongside-capability-gap", "should_trigger": true, @@ -848,7 +886,7 @@ "should_trigger": true, "prompt": "Use First Draft to create and send a Foundation Plan for Scalar Showcase, application key scalar_showcase. Model one Movie Entity with one Field of each currently importable scalar type, using a required short_text title as its Primary Descriptor and a decimal rating defaulting to 7.5. The compatible firstdraft CLI is installed, but no JSON Schema 2020-12 validator is available. Do not install or implement one.", "expectations": [ - "Uses Foundation Plan 0.21, the Modeling guide, and Examples for authoring without opening the complete bundled schema.", + "Uses Foundation Plan 0.22, the Modeling guide, and Examples for authoring without opening the complete bundled schema.", "Runs plan init with the provided application key and name, then runs generate uuid --count 11 through the Skill resolver exactly once for one Entity and ten Fields.", "Authors one Movie Entity with exactly one Field of each supported scalar type and a required movie.title Field Primary Descriptor.", "Authors movie.rating as a decimal with literal default value \"7.5\" as a canonical string, never the JSON number 7.5.", @@ -1268,7 +1306,7 @@ { "id": "compile-prepared-movie-catalog", "should_trigger": true, - "prompt": "I approve the exact pre-Compile semantic read-back and reviewed support result for the unchanged staged Movie Catalog with SHA-256 025c7021ad31bdd64b01eb007632c000f96a84273d1a53ea41ab5f342d11aa3c. Preserve its intentional Appearance request and publish that exact candidate to one private GitHub repository now.", + "prompt": "I approve the exact pre-Compile semantic read-back and reviewed support result for the unchanged staged Movie Catalog with SHA-256 e8191ce54fb8827c3ae3d58f8ab9d9fff95a25d640c931a2113115ec4fc4d279. Preserve its intentional Appearance request and publish that exact candidate to one private GitHub repository now.", "expectations": [ "Requires the installed CLI to expose both plan compile completion modes and no public plan publish command.", "In the same continuing session, treats the prompt as unambiguous approval of the complete semantic model and reviewed nonempty GapSet, rereads the exact local Plan, verifies its SHA-256 and semantic model are unchanged, and does not ask for a second confirmation or repeat the unchanged read-back.", @@ -1294,7 +1332,7 @@ { "id": "compile-prepared-drawing-board-application", "should_trigger": true, - "prompt": "I approve the exact pre-Compile Drawing Board semantic read-back and reviewed support result for the unchanged staged Movie Catalog with SHA-256 025c7021ad31bdd64b01eb007632c000f96a84273d1a53ea41ab5f342d11aa3c. Materialize that exact candidate into the absent ./application directory. Do not create a GitHub repository.", + "prompt": "I approve the exact pre-Compile Drawing Board semantic read-back and reviewed support result for the unchanged staged Movie Catalog with SHA-256 e8191ce54fb8827c3ae3d58f8ab9d9fff95a25d640c931a2113115ec4fc4d279. Materialize that exact candidate into the absent ./application directory. Do not create a GitHub repository.", "expectations": [ "Requires the installed CLI to expose plan compile --output as a direct local completion mode while retaining plan compile --github as the separate GitHub Publication mode.", "In the same continuing session as precompile-drawing-board-read-back, treats the prompt as unambiguous approval of the complete semantic model, reviewed nonempty GapSet, exact Plan bytes, and selected direct local mode; rereads the Plan, verifies its SHA-256 is unchanged, and asks for no second confirmation.", 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 84d46bb..9934f18 100644 --- a/evals/create-full-stack-app/fixtures/analysis-failed-analysis.json +++ b/evals/create-full-stack-app/fixtures/analysis-failed-analysis.json @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000902", "graph_version": 1, - "head_source_sha256": "e5d6ed13362d9cddd16a4294239e52761791c2eb52a1a5dbfe4b74f03cad2457", + "head_source_sha256": "3bca1f3f5d5674857f7d3bc44fbd5100b8d4238f7ded4befd10731caac89fa81", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" 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 fd3c50a..68a2116 100644 --- a/evals/create-full-stack-app/fixtures/appearance-current-analysis.json +++ b/evals/create-full-stack-app/fixtures/appearance-current-analysis.json @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000906", "graph_version": 1, - "head_source_sha256": "025c7021ad31bdd64b01eb007632c000f96a84273d1a53ea41ab5f342d11aa3c", + "head_source_sha256": "e8191ce54fb8827c3ae3d58f8ab9d9fff95a25d640c931a2113115ec4fc4d279", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -18,7 +18,7 @@ "gap_set": { "format": "firstdraft.foundation-gaps/2", "source": { - "sha256": "025c7021ad31bdd64b01eb007632c000f96a84273d1a53ea41ab5f342d11aa3c" + "sha256": "e8191ce54fb8827c3ae3d58f8ab9d9fff95a25d640c931a2113115ec4fc4d279" }, "project": { "id": "01900000-0000-7000-8000-000000000021", @@ -27,7 +27,7 @@ "analysis": { "release": "foundation-plan-rails/application-2026-09-22-theme" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -45,7 +45,7 @@ } ] }, - "gap_set_sha256": "73a0f23d81563e4ea932874fb8af8b6ad4d819304b5917a9798086878a85c03c", + "gap_set_sha256": "76f7b54cf13f7a9b799a498a9a0094417f9b9672a5ae8a8fb1c348a2f8a163f4", "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/appearance-current.foundation-plan.json b/evals/create-full-stack-app/fixtures/appearance-current.foundation-plan.json index 49198ee..0fb8a3b 100644 --- a/evals/create-full-stack-app/fixtures/appearance-current.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/appearance-current.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" 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 01cd5c2..558b194 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 @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000905", "graph_version": 1, - "head_source_sha256": "25f888477e4ecbbcdcdb8bc32314dab579fb4fe7218e2e4f9915a42ff5f4dc33", + "head_source_sha256": "36c076540a7d0bfa7a78c315de44ca3f708bddd65957e93c125f061433886d7e", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -18,7 +18,7 @@ "gap_set": { "format": "firstdraft.foundation-gaps/2", "source": { - "sha256": "25f888477e4ecbbcdcdb8bc32314dab579fb4fe7218e2e4f9915a42ff5f4dc33" + "sha256": "36c076540a7d0bfa7a78c315de44ca3f708bddd65957e93c125f061433886d7e" }, "project": { "id": "01900000-0000-7000-8000-000000000020", @@ -27,14 +27,14 @@ "analysis": { "release": "foundation-plan-rails/application-2026-09-22-theme" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" }, "gaps": [] }, - "gap_set_sha256": "e7ac6578d55ce5ee0b0747b0c07a1553e22b4997e6b4270b132cff373bbc96a9", + "gap_set_sha256": "b8542e1a9a29ab8f6415a94e10d068053ede76c6d6f74cf9b6bcbe1a5521e376", "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/application-intent.foundation-plan.json b/evals/create-full-stack-app/fixtures/application-intent.foundation-plan.json index 4d13961..ecbfe25 100644 --- a/evals/create-full-stack-app/fixtures/application-intent.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/application-intent.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/empty.foundation-plan.json b/evals/create-full-stack-app/fixtures/empty.foundation-plan.json index 32423cd..bf455b8 100644 --- a/evals/create-full-stack-app/fixtures/empty.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/empty.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/first-preview-movies.foundation-plan.json b/evals/create-full-stack-app/fixtures/first-preview-movies.foundation-plan.json index 5bf4671..b08605e 100644 --- a/evals/create-full-stack-app/fixtures/first-preview-movies.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/first-preview-movies.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" 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 7d5c601..e4df526 100644 --- a/evals/create-full-stack-app/fixtures/issues-found-analysis.json +++ b/evals/create-full-stack-app/fixtures/issues-found-analysis.json @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000901", "graph_version": 1, - "head_source_sha256": "cbd591ab9bc46a5efb76383aaec89c26d3d850e473519a5cdedb4e42734cabe0", + "head_source_sha256": "d35994ff8aeb595a17333dd5bb2bf851a617ebcfa1ff395792c306056374de6a", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" 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 84796f5..d0a3e14 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 @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000907", "graph_version": 1, - "head_source_sha256": "61a3fc005e97fd0e53f5dd799064b06ef17e0eed9dde4764990ab049f1a61c98", + "head_source_sha256": "e3c78b2e006f5cc12660133b85a14fbe821cdd1ca54b2193158755741d392aed", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/mixed-application-issues.foundation-plan.json b/evals/create-full-stack-app/fixtures/mixed-application-issues.foundation-plan.json index d92304c..88fc3f4 100644 --- a/evals/create-full-stack-app/fixtures/mixed-application-issues.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/mixed-application-issues.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/pwa-disabled.foundation-plan.json b/evals/create-full-stack-app/fixtures/pwa-disabled.foundation-plan.json new file mode 100644 index 0000000..9a168de --- /dev/null +++ b/evals/create-full-stack-app/fixtures/pwa-disabled.foundation-plan.json @@ -0,0 +1,51 @@ +{ + "format": "firstdraft.foundation-plan.sketch/0.22", + "target": { + "id": "rails", + "profile": "rails-sketch/2026-09" + }, + "application": { + "key": "movie_catalog", + "name": "Movie Catalog", + "domain": "movies.example.com", + "appearance": { + "theme": "toggle", + "tint_color": "#4F46E5" + }, + "native": { + "ios": {}, + "android": {} + }, + "delivery": {}, + "entities": [ + { + "subject_uuid": "019fb300-0000-7000-8000-000000000001", + "key": "movie", + "name": "Movie", + "icon": "film", + "primary_descriptor": { + "field": "movie.title" + }, + "fields": [ + { + "subject_uuid": "019fb300-0000-7000-8000-000000000002", + "key": "title", + "name": "Title", + "type": "short_text", + "required": true + } + ], + "scaffold": { + "resource_routes": [ + "index" + ], + "index": { + "authorization": "public" + } + } + } + ], + "pwa": false, + "home_index": "movie" + } +} diff --git a/evals/create-full-stack-app/fixtures/reconcile-habits.foundation-plan.json b/evals/create-full-stack-app/fixtures/reconcile-habits.foundation-plan.json index 2d6477d..0249107 100644 --- a/evals/create-full-stack-app/fixtures/reconcile-habits.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/reconcile-habits.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" 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 8bf6752..c145e8d 100644 --- a/evals/create-full-stack-app/fixtures/recurring-issues-analysis.json +++ b/evals/create-full-stack-app/fixtures/recurring-issues-analysis.json @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000904", "graph_version": 2, - "head_source_sha256": "5b7573b95bb3e1f6d4b92a564995ea6b8bb41084136120a0426f277ea8dd665a", + "head_source_sha256": "51254c335f53c5bb5b1621a97f47bd8f09820e4917d67ff6deac9d58c5eed2f3", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/recurring-issues.foundation-plan.json b/evals/create-full-stack-app/fixtures/recurring-issues.foundation-plan.json index 5501d7c..d3131e8 100644 --- a/evals/create-full-stack-app/fixtures/recurring-issues.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/recurring-issues.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/redirect-movies.foundation-plan.json b/evals/create-full-stack-app/fixtures/redirect-movies.foundation-plan.json index 78655c8..47b232c 100644 --- a/evals/create-full-stack-app/fixtures/redirect-movies.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/redirect-movies.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/reserved-constant.foundation-plan.json b/evals/create-full-stack-app/fixtures/reserved-constant.foundation-plan.json index ea60916..dbbde77 100644 --- a/evals/create-full-stack-app/fixtures/reserved-constant.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/reserved-constant.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/resume-current.foundation-plan.json b/evals/create-full-stack-app/fixtures/resume-current.foundation-plan.json index db675ef..43af86b 100644 --- a/evals/create-full-stack-app/fixtures/resume-current.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/resume-current.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/schema-invalid.foundation-plan.txt b/evals/create-full-stack-app/fixtures/schema-invalid.foundation-plan.txt index 2f09b04..2e819a1 100644 --- a/evals/create-full-stack-app/fixtures/schema-invalid.foundation-plan.txt +++ b/evals/create-full-stack-app/fixtures/schema-invalid.foundation-plan.txt @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/superseded-analysis.json b/evals/create-full-stack-app/fixtures/superseded-analysis.json index 4f89c98..4349fa8 100644 --- a/evals/create-full-stack-app/fixtures/superseded-analysis.json +++ b/evals/create-full-stack-app/fixtures/superseded-analysis.json @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000903", "graph_version": 2, - "head_source_sha256": "e5d6ed13362d9cddd16a4294239e52761791c2eb52a1a5dbfe4b74f03cad2457", + "head_source_sha256": "3bca1f3f5d5674857f7d3bc44fbd5100b8d4238f7ded4befd10731caac89fa81", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/supported-enum.foundation-plan.json b/evals/create-full-stack-app/fixtures/supported-enum.foundation-plan.json index 8a14cc5..2d02c9e 100644 --- a/evals/create-full-stack-app/fixtures/supported-enum.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/supported-enum.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/supported-scalars.foundation-plan.json b/evals/create-full-stack-app/fixtures/supported-scalars.foundation-plan.json index 5653af2..f0d9b49 100644 --- a/evals/create-full-stack-app/fixtures/supported-scalars.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/supported-scalars.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" 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 29a82d7..674c072 100644 --- a/evals/create-full-stack-app/fixtures/theme-toggle-analysis.json +++ b/evals/create-full-stack-app/fixtures/theme-toggle-analysis.json @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000909", "graph_version": 1, - "head_source_sha256": "15031de0872234349c4ddd7364735355a82c91a8d914d4f4465614bab6ea171a", + "head_source_sha256": "1ad1b25f36d26e7c9155272e1e632d39a64131639c2d3b818875aa1678695fd2", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -18,7 +18,7 @@ "gap_set": { "format": "firstdraft.foundation-gaps/2", "source": { - "sha256": "15031de0872234349c4ddd7364735355a82c91a8d914d4f4465614bab6ea171a" + "sha256": "1ad1b25f36d26e7c9155272e1e632d39a64131639c2d3b818875aa1678695fd2" }, "project": { "id": "01900000-0000-7000-8000-000000000024", @@ -27,7 +27,7 @@ "analysis": { "release": "foundation-plan-rails/application-2026-09-22-theme" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -55,7 +55,7 @@ } ] }, - "gap_set_sha256": "ecd8fb2c75bf06c4c131e28f15d1d6017cfa9abb444bbf17251b1ecebe05f186", + "gap_set_sha256": "f84e5eec06b443f412dc2aab011f18455f4b42f47eee19b40a8a993362e7ac24", "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/theme-toggle.foundation-plan.json b/evals/create-full-stack-app/fixtures/theme-toggle.foundation-plan.json index 980cf00..53de9a1 100644 --- a/evals/create-full-stack-app/fixtures/theme-toggle.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/theme-toggle.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/evals/create-full-stack-app/fixtures/unsupported-field-capabilities.foundation-plan.json b/evals/create-full-stack-app/fixtures/unsupported-field-capabilities.foundation-plan.json index 8ac136a..f61703b 100644 --- a/evals/create-full-stack-app/fixtures/unsupported-field-capabilities.foundation-plan.json +++ b/evals/create-full-stack-app/fixtures/unsupported-field-capabilities.foundation-plan.json @@ -1,5 +1,5 @@ { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" 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 656c031..f76bf35 100644 --- a/evals/create-full-stack-app/fixtures/unsupported-graph-analysis.json +++ b/evals/create-full-stack-app/fixtures/unsupported-graph-analysis.json @@ -6,9 +6,9 @@ "analysis": { "id": "01900000-0000-7000-8000-000000000908", "graph_version": 1, - "head_source_sha256": "021f33c112a683ec1d627518a647b7133bd1997257a436460c336c0d331ccaae", + "head_source_sha256": "c32b3491569c253958bc709d239831118f4838448f800376cab54aeb1933ac13", "analyzer_release": "foundation-plan-rails/application-2026-09-22-theme", - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -18,7 +18,7 @@ "gap_set": { "format": "firstdraft.foundation-gaps/2", "source": { - "sha256": "021f33c112a683ec1d627518a647b7133bd1997257a436460c336c0d331ccaae" + "sha256": "c32b3491569c253958bc709d239831118f4838448f800376cab54aeb1933ac13" }, "project": { "id": "01900000-0000-7000-8000-000000000023", @@ -27,7 +27,7 @@ "analysis": { "release": "foundation-plan-rails/application-2026-09-22-theme" }, - "compiler_release": "foundation-plan-rails/compiler-application-2026-09-22-theme", + "compiler_release": "foundation-plan-rails/compiler-application-2026-09-23-pwa", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -55,7 +55,7 @@ } ] }, - "gap_set_sha256": "998c0641b433adc20b76891bf3c4f30edecf1765510f7898cbb88bf0f3479bc5", + "gap_set_sha256": "08d89c69a49066160afa06957e3386d233f98589677ba9e6afdafca7bd14ec5e", "started_at": "2026-07-30T12:04:00.000Z", "completed_at": "2026-07-30T12:04:01.000Z" } diff --git a/packages/claude-plugin/.claude-plugin/plugin.json b/packages/claude-plugin/.claude-plugin/plugin.json index 496eaa8..338bb67 100644 --- a/packages/claude-plugin/.claude-plugin/plugin.json +++ b/packages/claude-plugin/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "firstdraft", "displayName": "First Draft", - "version": "0.5.0", + "version": "0.6.0", "description": "Author a Foundation Plan and compile a bounded Rails, iPhone, and Android application", "author": { "name": "First Draft", diff --git a/packages/claude-plugin/package.template.json b/packages/claude-plugin/package.template.json index 172e0f5..e8664d4 100644 --- a/packages/claude-plugin/package.template.json +++ b/packages/claude-plugin/package.template.json @@ -1,6 +1,6 @@ { "name": "@firstdraft.com/claude-code", - "version": "0.5.0", + "version": "0.6.0", "description": "First Draft Foundation Plan authoring and UI development for Claude Code and Codex", "license": "MIT", "type": "module", diff --git a/release/compatibility.json b/release/compatibility.json index ee8b63b..e0e6204 100644 --- a/release/compatibility.json +++ b/release/compatibility.json @@ -1,21 +1,21 @@ { "format": "firstdraft.release-compatibility/1", "component": "skills", - "version": "0.5.0", + "version": "0.6.0", "plugin_source": { "package": "@firstdraft.com/claude-code", - "tarball_sha256": "d31e5f2026d43a3eee014d41593deebde7e62bdc9e1d35eed2397d3da196533e" + "tarball_sha256": "23ca0098605ed1a2981648961fe7c5355b5b6280f3f3db6f4b957872778a9b08" }, "requires": { "api_contract": [ - ">= 0.5.0", - "< 0.6.0" + ">= 0.6.0", + "< 0.7.0" ], "cli": [ - "= 0.5.0" + "= 0.6.0" ], "foundation_plan_formats": [ - "firstdraft.foundation-plan.sketch/0.21" + "firstdraft.foundation-plan.sketch/0.22" ] } } diff --git a/script/check-cli-contract.mjs b/script/check-cli-contract.mjs index 047b19e..f96bdfc 100644 --- a/script/check-cli-contract.mjs +++ b/script/check-cli-contract.mjs @@ -47,7 +47,7 @@ assert.deepEqual( component: "cli", version: cliPackageVersion, requires: { - api_contract: [">= 0.5.0", "< 0.6.0"], + api_contract: [">= 0.6.0", "< 0.7.0"], foundation_plan_formats: [foundationPlanFormat], }, }, diff --git a/script/check-release-compatibility.mjs b/script/check-release-compatibility.mjs index 14dcdbf..173ea5b 100644 --- a/script/check-release-compatibility.mjs +++ b/script/check-release-compatibility.mjs @@ -119,7 +119,7 @@ export function assertSkillsReleaseCompatibility({ assertComparator(requirement); } assert.deepEqual(compatibility.requires, { - api_contract: [">= 0.5.0", "< 0.6.0"], + api_contract: [">= 0.6.0", "< 0.7.0"], cli: [`= ${cliPackageVersion}`], foundation_plan_formats: [foundationPlanFormat], }); diff --git a/script/claude-plugin-boundaries.mjs b/script/claude-plugin-boundaries.mjs index ebcb9a1..fb865bd 100644 --- a/script/claude-plugin-boundaries.mjs +++ b/script/claude-plugin-boundaries.mjs @@ -6,8 +6,8 @@ export const canonicalClaudePluginSkillFiles = Object.freeze([ "agents/openai.yaml", "references/diagnostics-and-recovery.md", "references/examples.md", - "references/foundation-plan-0.21.schema.json", - "references/foundation-plan-021.md", + "references/foundation-plan-0.22.schema.json", + "references/foundation-plan-022.md", "references/modeling-guide.md", "scripts/firstdraft.sh", ]); diff --git a/script/cli-contract/config.mjs b/script/cli-contract/config.mjs index e2e0f4e..932a8b5 100644 --- a/script/cli-contract/config.mjs +++ b/script/cli-contract/config.mjs @@ -1,8 +1,8 @@ -export const cliRevision = "013581baa91c1eff0bffafd6e9e20eccb1f24643"; +export const cliRevision = "38528f4402bc0a75ac855f0226569fbbc92488e3"; export const cliRuntimeSha256 = - "6386712548a9eb5d3d7c8dbdd031482e66af270cae45838056f72533979cae40"; + "648cc0b32520d11c36d2c434fd078c56ca7e60e3e7770c7bd9349d7b331c128d"; export const cliPackageName = "@firstdraft.com/cli"; -export const cliPackageVersion = "0.5.0"; +export const cliPackageVersion = "0.6.0"; export const safeGithubReasonCodes = Object.freeze([ "github.configuration_missing", @@ -40,11 +40,11 @@ export const storedApiUrl = "http://127.0.0.1:1"; export const configuredApiUrl = "http://127.0.0.1:2"; export const apiToken = "canary-private-api-token"; export const foundationPlanFormat = - "firstdraft.foundation-plan.sketch/0.21"; + "firstdraft.foundation-plan.sketch/0.22"; export const analyzerRelease = "foundation-plan-rails/application-2026-09-22-theme"; export const compilerRelease = - "foundation-plan-rails/compiler-application-2026-09-22-theme"; + "foundation-plan-rails/compiler-application-2026-09-23-pwa"; export const compilationTarget = { id: "rails", profile: "rails-sketch/2026-09", diff --git a/script/cli-contract/fixtures.mjs b/script/cli-contract/fixtures.mjs index ea384c9..7fce202 100644 --- a/script/cli-contract/fixtures.mjs +++ b/script/cli-contract/fixtures.mjs @@ -39,7 +39,7 @@ export function analysisProjection( graphVersion = 1, identifier = analysisId, headSourceSha256 = - "25f888477e4ecbbcdcdb8bc32314dab579fb4fe7218e2e4f9915a42ff5f4dc33", + "36c076540a7d0bfa7a78c315de44ca3f708bddd65957e93c125f061433886d7e", gaps = [], } = {}, ) { diff --git a/script/cli-contract/local-commands.mjs b/script/cli-contract/local-commands.mjs index f390135..04a2128 100644 --- a/script/cli-contract/local-commands.mjs +++ b/script/cli-contract/local-commands.mjs @@ -19,7 +19,7 @@ import { const planSchema = JSON.parse( readFileSync( - new URL("../../skills/create-full-stack-app/references/foundation-plan-0.21.schema.json", import.meta.url), + new URL("../../skills/create-full-stack-app/references/foundation-plan-0.22.schema.json", import.meta.url), "utf8", ), ); diff --git a/skills/create-full-stack-app/SKILL.md b/skills/create-full-stack-app/SKILL.md index e47b0a0..cfe048d 100644 --- a/skills/create-full-stack-app/SKILL.md +++ b/skills/create-full-stack-app/SKILL.md @@ -23,25 +23,25 @@ explicit handoff. Follow [writing notes](references/modeling-guide.md#retain-imp ## Current boundary -Targets plugin 0.5.0, CLI 0.5.0, API 0.5, and Plan 0.21; catalog selection is separate. +Targets plugin 0.6.0, CLI 0.6.0, API 0.6, Plan 0.22; catalog selection is separate. -- Bounded generation includes Web Accounts, Policies, Scaffolds, development data, and selected iPhone/Android - clients. [Appearance](references/foundation-plan-021.md#application-and-clients) selects theme, native colors, - and Web icons; browser preferences and native gaps differ. -- Bounded Account/Policy protects Web Scaffolds. Native clients require an admitted public index, are Account/Policy-free, - and do not inherit Web privacy. -- Preserve unsupported requests; report gaps. Never drop clients or weaken access to get `valid`. - Artifacts retain the submitted Plan and GapSet. -- For native preview, use local iOS Simulator or Android Studio Emulator when available. Follow the generated app's - guides; native builds and Revyl are not prerequisites for ordinary local Rails development. +- Bounded generation includes Web Accounts/Policies/Scaffolds, development data, and selected iPhone/Android + clients. [Appearance](references/foundation-plan-022.md#application-and-clients) controls theme, native colors, + and Web icons with explicit native gaps. +- [Add to Home Screen](references/foundation-plan-022.md#add-to-home-screen) metadata is on by default; + `application.pwa: false` opts out. +- Account/Policy protection applies to Web Scaffolds. Native clients require a public index and do not inherit Web privacy. +- Preserve unsupported requests and their gaps, including selected clients and access. Artifacts retain the Plan and GapSet. +- Native preview follows generated guides and local iOS Simulator or Android Studio Emulator; native builds and Revyl + are optional for Rails development. -Before support claims, read [current evidence](references/foundation-plan-021.md#current-evidence-boundary). +Before support claims, read [current evidence](references/foundation-plan-022.md#current-evidence-boundary). ## Load references only when needed For authoring, choose the relevant section: -- [Foundation Plan reference](references/foundation-plan-021.md): exact envelope, identity, ownership, presence, +- [Foundation Plan reference](references/foundation-plan-022.md): exact envelope, identity, ownership, presence, current evidence, and target support for Application/clients, Fields, relationships, Validations, Accounts/Policies, and Scaffolds. - [Modeling guide](references/modeling-guide.md): interview, Entities/Fields, validations, relationships, and @@ -60,7 +60,7 @@ For CLI work: read [ambiguous mutations](references/diagnostics-and-recovery.md#ambiguous-mutations) only when the named error requires it. -The bundled [JSON Schema](references/foundation-plan-0.21.schema.json) is machine-readable validator input, not prose. +The bundled [JSON Schema](references/foundation-plan-0.22.schema.json) is machine-readable validator input, not prose. Use a compatible JSON Schema 2020-12 command named by the user, exposed by the project, or found through a straightforward check of existing local commands. Pass only its path; never read it end to end. Do not install dependencies or add validation/build plumbing solely for this workflow. Otherwise rely on First Draft exact-byte @@ -82,7 +82,7 @@ firstdraft_cli --version firstdraft_cli --help ``` -Require the version probe to succeed with one exact `0.5.0` output line and no other output, and top-level help that +Require the version probe to succeed with one exact `0.6.0` output line and no other output, and top-level help that lists `generate`, `plan`, and `compilation`. Contract tests own separate stdout and stderr assertions for leaf commands; do not repeat them in a startup shell loop. The compatible CLI supplies these public commands: diff --git a/skills/create-full-stack-app/references/diagnostics-and-recovery.md b/skills/create-full-stack-app/references/diagnostics-and-recovery.md index 7e3048f..1ec77c6 100644 --- a/skills/create-full-stack-app/references/diagnostics-and-recovery.md +++ b/skills/create-full-stack-app/references/diagnostics-and-recovery.md @@ -8,8 +8,8 @@ JSON object. An unrecognized prefixed line, a progress line after the envelope, interleaved output fail closed. Branch on the object's stable `error` and structured fields rather than the human-readable `detail` or broad process exit status. -The source candidate uses `@firstdraft.com/cli@0.5.0`. Its exact reviewed revision and runtime digest are owned by -[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.5.0/script/cli-contract/config.mjs) +The source candidate uses `@firstdraft.com/cli@0.6.0`. Its exact reviewed revision and runtime digest are owned by +[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.6.0/script/cli-contract/config.mjs) at this plugin's protected release tag. Check the command surface rather than assuming the version alone establishes compatibility. These source checks do not prove plugin/catalog publication, service authentication, staging compatibility, or a complete user journey. @@ -133,7 +133,7 @@ local development is unsuitable, not a prerequisite. ### Direct local output -CLI 0.5.0 defaults to current-root adoption and archives the original workspace under `.firstdraft/design/`. +CLI 0.6.0 defaults to current-root adoption and archives the original workspace under `.firstdraft/design/`. CLI 0.3.0 supports the same archive via explicit `--output .`; older 0.2.2 uses top-level `design/`. Preserve the actual layout of an already materialized application; this change does not migrate it. @@ -456,7 +456,7 @@ or provenance-changing response requires reconciling the CLI and service contrac 4. requires the envelope's `head_source_sha256` to equal the retained `compilation.head_source_sha256`; 5. verifies transport metadata and exact bytes against the retained artifact digest, then validates Plan format - `firstdraft.foundation-plan.sketch/0.21`, target profile `rails-sketch/2026-09`, the canonical Foundation Plan + `firstdraft.foundation-plan.sketch/0.22`, target profile `rails-sketch/2026-09`, the canonical Foundation Plan digest, envelope, manifest, paths, modes, Base64 contents, and file digests; and 6. installs an absent private sibling tree with one atomic rename, or applies direct Compile's [current-root transaction and CLI compatibility boundary](#direct-local-output). diff --git a/skills/create-full-stack-app/references/examples.md b/skills/create-full-stack-app/references/examples.md index db437f6..688479b 100644 --- a/skills/create-full-stack-app/references/examples.md +++ b/skills/create-full-stack-app/references/examples.md @@ -1,6 +1,6 @@ # Examples -These examples teach `sketch/0.21` structure. UUIDs are fixed documentation data. Never choose them when authoring +These examples teach `sketch/0.22` structure. UUIDs are fixed documentation data. Never choose them when authoring new subjects in a real Project. Once an exact staged or resumed candidate already contains subject UUIDs, preserve them during read-back and diagnostics unless a user correction or demonstrated identity diagnostic requires a change. @@ -26,7 +26,7 @@ smallest subset accepted by the reviewed conditional PUT. ```json { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -42,6 +42,10 @@ smallest subset accepted by the reviewed conditional PUT. ``` An empty Plan is preferable to a fake Entity. Tell the user that the application model is still empty. +The omitted `application.pwa` keeps the default online Add to Home Screen metadata. An explicit `true` has the +same selected behavior; set `"pwa": false` inside `application` only for an intentional opt-out. This preserves +favicons and touch icons and does not stop a browser from saving the site. See the +[installation boundary](foundation-plan-022.md#add-to-home-screen) for the scope and device-qualification limits. ## Bounded web and iPhone application @@ -53,7 +57,7 @@ return `valid`; that result is still only the Compilation gate, not proof that o ```json { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -98,7 +102,7 @@ return `valid`; that result is still only the Compilation gate, not proof that o This example requests iPhone. Add `"android": {}` beside `"ios": {}` to request both, or replace the iOS member to request Android alone. Each client requires an admitted public-index Scaffold and an identity within its -[platform limits](foundation-plan-021.md#application-and-clients); otherwise a valid analysis records an unrealized-client +[platform limits](foundation-plan-022.md#application-and-clients); otherwise a valid analysis records an unrealized-client target gap. iPad remains outside this boundary. Domain configures the native origin/identifier and Rails production mailer host; it does not provision DNS, TLS, or deployment. Web-only plans may use the same exact Scaffold with `native: {}` and may include or omit a domain. The admitted Scaffold makes Movie records readable on @@ -106,8 +110,10 @@ the web without authentication. Confirm that exposure with the user before addin the iPhone navigation requirement or silently discard private or broader access intent. Adding `appearance` to this candidate selects the theme, native shell colors, and branding for the adaptive SVG -and deterministic PNG used by Rails favicon and PWA references. Omitted theme means fixed light; web components -keep stock Zinc tokens. Because this example also emits iOS, a matching valid AnalysisRun records the partial +and deterministic 512px PNG used by Rails favicons and touch icons. Enabled PWA output adds a real 192px PNG; its +manifest declares both PNG sizes and the SVG. Setting `application.pwa` to `false` retains the favicon and touch-icon +assets. Omitted theme means fixed light; web components keep stock Zinc tokens. Because this example also emits +iOS, a matching valid AnalysisRun records the partial `foundation_plan.gap.appearance.icon_assets.not_generated` record at `/application/appearance` for the stock AppIcon. With `toggle`, the browser offers Light/Dark/System while the iOS shell and embedded Rails responses stay automatic; the run also records `foundation_plan.gap.appearance.native_theme_preference.not_generated` at @@ -271,13 +277,13 @@ do not claim it works or silently replace it with a resource/default destination ## One Entity with required and optional scalar Fields -This complete document is structurally valid v0.21 and accepted by the reviewed bounded importer. That does not +This complete document is structurally valid v0.22 and accepted by the reviewed bounded importer. That does not prove complete semantic analysis, target support, Compilation, or generated output. `required` is mandatory even when the value is `false`; omitting it from the optional Details Field would be structurally invalid. ```json { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -323,12 +329,12 @@ UUID. The reviewed importer also accepts `boolean`, `date`, `datetime`, `decimal ## Ordinal enum Field -Use an enum for a closed set of named choices. This complete document is structurally valid v0.21 and accepted by +Use an enum for a closed set of named choices. This complete document is structurally valid v0.22 and accepted by the reviewed bounded importer. Here, priority order carries semantic rank, so `ordinal` is `true`. ```json { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -411,7 +417,7 @@ make this Account or profile available natively. ```json { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -561,13 +567,13 @@ GapSet consequences. ## Stored and reverse relationship -This complete document is structurally valid v0.21 and lies within the current ordinary Reference and direct inverse +This complete document is structurally valid v0.22 and lies within the current ordinary Reference and direct inverse Compiler subset. `Task` owns the stored `project` Reference. `Project` owns the meaningful reverse `tasks` Association. The forward `task.project` Association is derived and therefore omitted. ```json { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -660,5 +666,5 @@ The generated collection is distinct. In this example, the `through` step is an inverse, the `source` is an admitted mechanically derived forward Association, and both underlying References have `one_to_one: false`. This is one supported shape, not the current boundary statement: the reviewed Case Chat result also realizes selected predicated sources, several first-level indirect Associations, and one nested-through form. -Use [the exact Association reference](foundation-plan-021.md#references-and-associations) and the reviewed GapSet to decide whether +Use [the exact Association reference](foundation-plan-022.md#references-and-associations) and the reviewed GapSet to decide whether another authored path is realized. diff --git a/skills/create-full-stack-app/references/foundation-plan-0.21.schema.json b/skills/create-full-stack-app/references/foundation-plan-0.22.schema.json similarity index 99% rename from skills/create-full-stack-app/references/foundation-plan-0.21.schema.json rename to skills/create-full-stack-app/references/foundation-plan-0.22.schema.json index ebceef9..758ee02 100644 --- a/skills/create-full-stack-app/references/foundation-plan-0.21.schema.json +++ b/skills/create-full-stack-app/references/foundation-plan-0.22.schema.json @@ -1,8 +1,8 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://firstdraft.com/schemas/foundation-plan/sketch-0.21", + "$id": "https://firstdraft.com/schemas/foundation-plan/sketch-0.22", "title": "Foundation Plan", - "description": "The working sketch/0.21 JSON import and export representation for one First Draft Project graph.", + "description": "The working sketch/0.22 JSON import and export representation for one First Draft Project graph.", "type": "object", "additionalProperties": false, "required": [ @@ -14,7 +14,7 @@ "format": { "title": "Format", "description": "Names the Foundation Plan serialization vocabulary and its version.", - "const": "firstdraft.foundation-plan.sketch/0.21" + "const": "firstdraft.foundation-plan.sketch/0.22" }, "target": { "$ref": "#/$defs/target" @@ -134,6 +134,11 @@ "example.com" ] }, + "pwa": { + "title": "Add to Home Screen support", + "description": "Enables online browser installation metadata by default. False omits generated installation support; browsers may still save an ordinary site. This does not select offline behavior, Web Push, or a native client.", + "type": "boolean" + }, "home_index": { "title": "Home index", "description": "The Entity whose selected Web index is the home page. Omission uses the target's welcome page. The index retains its authored authorization.", diff --git a/skills/create-full-stack-app/references/foundation-plan-021.md b/skills/create-full-stack-app/references/foundation-plan-022.md similarity index 92% rename from skills/create-full-stack-app/references/foundation-plan-021.md rename to skills/create-full-stack-app/references/foundation-plan-022.md index a82979d..3976f5c 100644 --- a/skills/create-full-stack-app/references/foundation-plan-021.md +++ b/skills/create-full-stack-app/references/foundation-plan-022.md @@ -1,8 +1,8 @@ -# Foundation Plan 0.21 +# Foundation Plan 0.22 This reference and [Examples](examples.md) guide authoring for the experimental -`firstdraft.foundation-plan.sketch/0.21` boundary. The bundled -[exact JSON Schema](foundation-plan-0.21.schema.json) is the machine-readable structural contract. Never read it +`firstdraft.foundation-plan.sketch/0.22` boundary. The bundled +[exact JSON Schema](foundation-plan-0.22.schema.json) is the machine-readable structural contract. Never read it end to end. Use a compatible JSON Schema 2020-12 validator when the user names its command, the project exposes a specific validation command, or a straightforward check finds an existing compatible local command. Confirm that command is available, then pass the schema file to it without loading its contents into context. A declared library @@ -25,6 +25,7 @@ and inspect only that definition. Use server diagnostics for the exact bytes sub - [Presence](#presence) - [Current conditional PUT boundary](#current-conditional-put-boundary) - [Application and clients](#application-and-clients) + - [Add to Home Screen](#add-to-home-screen) - [Entities, descriptors, and Fields](#entities-descriptors-and-fields) - [Enums](#enums) - [Defaults](#defaults) @@ -42,9 +43,9 @@ deployed journey, and an older observation does not define current support. **Current design and machine authority** -- The bundled JSON Schema owns v0.21 transport shape. The Service's - [Rails target profile](https://github.com/firstdraft/firstdraft/blob/87760d2717d914b69a7bbf7e5d372ffe8aac50d2/docs/architecture/targets/rails/profile.md) - records lowering at selected Service source `87760d2717d914b69a7bbf7e5d372ffe8aac50d2`. +- The bundled JSON Schema owns v0.22 transport shape. The Service's + [Rails target profile](https://github.com/firstdraft/firstdraft/blob/4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0/docs/architecture/targets/rails/profile.md) + records lowering at selected Service source `4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0`. A source reference does not establish the deployed service revision. - Read `analyzer_release` and `compiler_release` from the matching Analysis result; Compilation results carry `compiler_release` only. @@ -53,11 +54,11 @@ deployed journey, and an older observation does not define current support. - The importer preserves each schema-valid exact source as the Project Head and imports a bounded relational graph. Meaning skipped before semantic analysis remains in the Head and appears as ordered `service_support_gap` records. Admitted meaning that the selected target cannot fully realize appears as `target_support_gap` records. -- Service API 0.5 returns the complete canonical `firstdraft.foundation-gaps/2` object and its SHA-256 for every valid +- Service API 0.6 returns the complete canonical `firstdraft.foundation-gaps/2` object and its SHA-256 for every valid AnalysisRun, including an empty `gaps` array. `valid` applies only to the admitted graph; it is not proof of Compilation or of meaning skipped before analysis. - Current public Compilation has bounded scalar, required-enum, relationship, Validation, Predicate, Ordering, - State Machine, Appearance theme/native-color/Web-icon, Web Account, Action Policy, generalized Web Scaffold, + State Machine, online browser installation, Appearance theme/native-color/Web-icon, Web Account, Action Policy, generalized Web Scaffold, development-data, and selected-iPhone/Android slices. Their prerequisites matter: unsupported children and consumers remain exact gaps rather than widening the supported shape. When Appearance is authored, emitted native clients retain a named partial gap for their stock launcher icons; `toggle` also records the absent native preference control. @@ -75,7 +76,7 @@ deployed journey, and an older observation does not define current support. **Implementation and observation evidence** - The Service's generated - [evidence index](https://github.com/firstdraft/firstdraft/blob/87760d2717d914b69a7bbf7e5d372ffe8aac50d2/docs/evidence/status.md) + [evidence index](https://github.com/firstdraft/firstdraft/blob/4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0/docs/evidence/status.md) distinguishes implemented, exercised, generated-output, hosted, and observed claims. The September 12 [Android qualification](https://github.com/firstdraft/firstdraft/blob/89a2d6866f9448f4e75b58cac26f61c52daaa0b0/docs/solutions/2026-09-12-generated-android-preview.md) records GitHub APK delivery and local Android public navigation, forms, theme, and layout checks. Revyl loaded @@ -88,7 +89,7 @@ deployed journey, and an older observation does not define current support. journey, physical-device installation, or production deployment. - The [iPhone Revyl receipt](https://github.com/firstdraft/firstdraft/blob/89a2d6866f9448f4e75b58cac26f61c52daaa0b0/docs/solutions/2026-09-12-revyl-preview-release.md) separately records an emitted iPhone index and live Rails refresh. It did not exercise native forms or Back in - Revyl. The [native target](https://github.com/firstdraft/firstdraft/blob/87760d2717d914b69a7bbf7e5d372ffe8aac50d2/docs/architecture/targets/rails/capabilities/native.md) + Revyl. The [native target](https://github.com/firstdraft/firstdraft/blob/4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0/docs/architecture/targets/rails/capabilities/native.md) owns generated iPhone detail/form routing and its separate source, Simulator, and browser evidence. Android runtime observations do not qualify iPhone runtime behavior. - The September 13 @@ -111,18 +112,18 @@ deployed journey, and an older observation does not define current support. The bundled schema was copied byte-for-byte from `docs/architecture/design/foundation-plan.schema.json` at Service revision -`87760d2717d914b69a7bbf7e5d372ffe8aac50d2` and has SHA-256 -`bef23a55f7d09509f0beab14df7620a49660908d7fb6c4435c99489a0dc9553d`. This is exact contract provenance, not +`4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0` and has SHA-256 +`fcd0123860d4f4a35bdfb9b97b17b6aa220bf5396fb471cf2e5f65d444c9843a`. 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 -[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.5.0/script/cli-contract/config.mjs) +[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.6.0/script/cli-contract/config.mjs) at this plugin's protected release tag, as contract provenance rather than release or execution evidence. The CLI exposes `generate uuid`, `generate application-key`, `plan init`, `plan push`, `plan status`, local `plan compile` (equivalent to `--output .`), explicit `plan compile --github`, `plan compile --output`, `compilation status`, and `compilation download`. It has no public `plan subject-id` or `plan publish`. The coordinated checkout declares the -`@firstdraft.com/cli@0.5.0` package. Direct output accepts the ordinary absent destination and, on POSIX, +`@firstdraft.com/cli@0.6.0` package. Direct output accepts the ordinary absent destination and, on POSIX, current-root adoption by default or with `--output .`; the recovery reference owns its preconditions. Check commands rather than inferring compatibility from a version number. These source checks do not prove plugin/catalog publication, authentication, staging compatibility, or a complete user journey. @@ -161,7 +162,7 @@ The root contains exactly three required properties: ```json { - "format": "firstdraft.foundation-plan.sketch/0.21", + "format": "firstdraft.foundation-plan.sketch/0.22", "target": { "id": "rails", "profile": "rails-sketch/2026-09" @@ -171,7 +172,7 @@ The root contains exactly three required properties: ``` The Application must contain `key`, `name`, `native`, `delivery`, and `entities`. It may also contain the optional -properties `domain`, `home_index`, `appearance`, and `development_data`. Objects are closed; do not add explanatory or +properties `domain`, `pwa`, `home_index`, `appearance`, and `development_data`. Objects are closed; do not add explanatory or tool-specific keys. The Project route and `.firstdraft/state.json` own Project identity and concurrency. Do not place `id`, @@ -244,7 +245,7 @@ App Schema artifact. ### Application and clients The reviewed importer accepts the required Application properties `key`, `name`, `native`, `delivery`, and -`entities`, plus optional `domain`, `home_index`, `appearance`, and `development_data`. Nonempty delivery remains +`entities`, plus optional `domain`, `pwa`, `home_index`, `appearance`, and `development_data`. Nonempty delivery remains in the exact Head and appears as a service-support gap instead of being silently discarded. Development data is admitted record by record when its assignments and dependencies are realizable; unsupported assignments remain precise gaps rather than causing the whole development-data graph to disappear. @@ -268,7 +269,7 @@ an explicit `.invalid` origin and `invalid.firstdraft` identifier prefix. Each selected client is emitted only when it has an admitted public-index Scaffold and its generated identity fits platform rules. Structurally valid long names can exceed those rules: the application key must form one DNS-safe label of at most 63 ASCII bytes, and Android application IDs must fit 223 bytes after domain conversion. -The [native target](https://github.com/firstdraft/firstdraft/blob/87760d2717d914b69a7bbf7e5d372ffe8aac50d2/docs/architecture/targets/rails/capabilities/native.md) +The [native target](https://github.com/firstdraft/firstdraft/blob/4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0/docs/architecture/targets/rails/capabilities/native.md) owns the full rules. A missing public entry or unusable identity omits that client and records `foundation_plan.gap.native_client.not_generated`; a missing domain alone does not. Preserve the user's requested clients and access rather than changing product meaning to avoid that gap. @@ -294,9 +295,10 @@ the browser selector/storage, and disclose the missing native preference control `partially_generated` status, naming the affected emitted targets. Preserve the authored choice and selected clients; no native settings bridge is generated. This source mapping does not establish native runtime qualification. -Native tint and background colors do not replace the stock Zinc web component tokens. Rails derives the adaptive -SVG and deterministic PNG used by its favicon and PWA references from the authored branding. When native clients -are emitted, their stock launcher icons are the reason for the precise +Native tint and background colors do not replace the stock Zinc web component tokens. Rails derives an adaptive +SVG and deterministic 512px PNG from the authored branding for favicon and touch-icon use. Enabled PWA output also +adds a real 192px PNG and declares both PNG sizes and the SVG in its manifest. The opt-out retains the favicon and +touch-icon assets. When native clients are emitted, their stock launcher icons are the reason for the precise `foundation_plan.gap.appearance.icon_assets.not_generated` partial gap. Web-only output has no Appearance icon-assets gap. Other admitted but unconsumed Application configuration remains a target gap. @@ -329,6 +331,25 @@ The smallest accepted Application remains: } ``` +### Add to Home Screen + +Optional `application.pwa` is a boolean. Omission and `true` enable ordinary online browser installation metadata; +`false` opts out. Preserve omission versus an explicit boolean when revising a Plan. This choice is independent of +Home, Accounts, Appearance, and `native`; do not select a native client or change access to enable installation. + +Enabled output keeps Rails' manifest controller and template, application name, actual 192px and 512px icons, +`start_url: "/"`, `scope: "/"`, and standalone display. The launch URL reaches the selected Home with its existing +authentication and authorization. Authored Appearance supplies derived Web icons; the default uses Core artwork. +The opt-out omits the manifest route/template/link, installation-capable metadata, related request spec, and 192px +installation icon together. Favicons, Apple touch icons, ordinary theme metadata, legal pages, and signup acceptance +remain. Browsers may still save an ordinary site, so `false` is not a way to prohibit Add to Home Screen. + +Use the browser's normal installation flow. This support adds no offline cache, service worker, Web Push, custom +installation prompt, or native build. Manifest/HTTP checks and browser promotion are separate from an actual +installation, standalone launch/navigation, or sign-in/relaunch. Identified physical iPhone Safari and Android +Chrome qualification remains pending under [Issue #661](https://github.com/firstdraft/firstdraft/issues/661); +desktop emulation is not device proof. Native launcher icons remain a separate support gap. + ### Entities, descriptors, and Fields A Primary Descriptor may select a required Field owned by that Entity or a schema-supported system Field. The @@ -361,7 +382,7 @@ multiple-per-Entity, guarded, cross-Entity, multi-effect, unresolved helper coll retain storage with behavior gaps. Failed transitions retain ordinary AASM/Rails object and transaction behavior; an application may need to reload or reset a failed object before reusing it. AASM 6.0.0 namespaced state scopes can query a prefixed value instead of the stored state. Consult the selected -Service's [automatic state scopes](https://github.com/firstdraft/firstdraft/blob/87760d2717d914b69a7bbf7e5d372ffe8aac50d2/docs/architecture/design/state-machines.md#automatic-state-scopes) +Service's [automatic state scopes](https://github.com/firstdraft/firstdraft/blob/4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0/docs/architecture/design/state-machines.md#automatic-state-scopes) guidance before using them in application work. `attachment` and `image` are schema-valid Field types, but they are skipped from the admitted graph and recorded as service-support gaps; they cannot reach diff --git a/skills/create-full-stack-app/references/modeling-guide.md b/skills/create-full-stack-app/references/modeling-guide.md index 018fbed..5bc9d3d 100644 --- a/skills/create-full-stack-app/references/modeling-guide.md +++ b/skills/create-full-stack-app/references/modeling-guide.md @@ -177,7 +177,7 @@ Selecting an index preserves its resource URL, query, and authorization. A prote A missing Entity or an Entity without a selected index is invalid. If the selected index is genuinely unsupported, keep that intended choice in the Plan and review the dependent Home gap; the residual app uses the welcome page. Do not substitute another index or weaken access. See the -[Application reference](foundation-plan-021.md#application-and-clients) for the serialized choice. +[Application reference](foundation-plan-022.md#application-and-clients) for the serialized choice. ## Model Entities and Fields @@ -199,7 +199,7 @@ storage using Rails `enum` with inclusion and presence validation plus native sc Compiler selects Rails prefix or suffix options when helper names would collide. Compatible in-domain literal-key defaults work regardless of whether the order has semantic rank. Database membership constraints, general rank semantics, optional enums, and unsupported consumers remain gaps. Preserve -product meaning instead of replacing an enum with a scalar; the [enum reference](foundation-plan-021.md#enums) +product meaning instead of replacing an enum with a scalar; the [enum reference](foundation-plan-022.md#enums) owns the exact lowering. ### Choose text normalization @@ -224,7 +224,7 @@ also applies when `downcase` occurs between them: `["trim", "downcase", "blank_t This rule gives `downcase` no fixed position. Do not silently reorder an existing pipeline or repeat it until stable. `trim` also removes invisible edge characters that `collapse_whitespace` preserves; both preserve interior joiners. -The Service's [Field catalog](https://github.com/firstdraft/firstdraft/blob/87760d2717d914b69a7bbf7e5d372ffe8aac50d2/docs/architecture/design/field-catalog.md#normalization-and-comparison) +The Service's [Field catalog](https://github.com/firstdraft/firstdraft/blob/4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0/docs/architecture/design/field-catalog.md#normalization-and-comparison) owns the exact character policies and operation semantics. These are authoring choices, not automatic Compiler defaults; include consequential choices in the semantic read-back. @@ -250,7 +250,7 @@ custom validator: ordinary Rails I18n can customize application error copy after comparison or uniqueness remains on the Entity when appropriate, independently of that error target. These examples explain kind selection, not a promise that every shape emits today. Read the compatible -[Validation support reference](foundation-plan-021.md#validations), use the bundled schema for exact syntax, and +[Validation support reference](foundation-plan-022.md#validations), use the bundled schema for exact syntax, and inspect the real analysis result. Schema-valid cross-field comparisons, conditions, and exclusions can still be service or target gaps. No general Rails `validates` option or custom Ruby callback becomes Plan syntax merely because Rails supports it. @@ -311,9 +311,15 @@ Web Account/Policy slice can protect supported surfaces and provide a Web-only A and dependent consumers remain exact gaps. Read the Foundation Plan reference for the current prerequisites. Do not silently narrow a broader requested Scaffold or make it public merely to obtain a gap-free result. +Online Add to Home Screen metadata is on by default. Leave `application.pwa` omitted for that default, preserve an +explicit boolean in a revised Plan, and use `false` when the user wants to omit generated installation support. +This does not prevent a browser from saving the site, and does not request offline behavior, push, or native output. +Home, access rules, and Appearance remain independent. See [the installation boundary](foundation-plan-022.md#add-to-home-screen) +before promising browser promotion, installed-device behavior, or sign-in persistence. + Select `native.ios` and `native.android` independently when the user wants those owned projects. Ordinary Compilation emits each with at least one admitted public navigation entry and an identity that fits its -[platform limits](foundation-plan-021.md#application-and-clients); otherwise the valid run records an unrealized-client +[platform limits](foundation-plan-022.md#application-and-clients); otherwise the valid run records an unrealized-client target gap. Domain supplies a native HTTPS origin and platform identifier; it also configures the Rails production mailer host. It does not provision DNS, deployment, TLS, or mail delivery. Without a domain the native identifiers are explicit placeholders. Semantic icons inform Web, SF Symbol, and @@ -330,11 +336,11 @@ Native `toggle` output stays automatic with one reviewed gap for the absent nati the authored choice and requested clients instead of replacing `toggle` with `auto` or dropping a client. Web components retain the stock Zinc theme, and native launcher icons remain stock. Android shows one stack, up to five tabs, or four tabs plus More for every overflow destination. After Compilation, follow the -emitted platform preview guide and the [native preview boundary](foundation-plan-021.md#preview-generated-native-apps). +emitted platform preview guide and the [native preview boundary](foundation-plan-022.md#preview-generated-native-apps). Use local Android Studio Emulator or iOS Simulator for native checks when available. Ordinary Rails iteration uses the local web app. Revyl is an optional preview destination, not a release or development prerequisite. Nonempty delivery, broader Account/Policy shapes, and broader clients remain unsupported or incomplete. Requirements without a -v0.21 shape, including notification trigger/template definitions, deployment, and iPad, remain in +v0.22 shape, including notification trigger/template definitions, deployment, and iPad, remain in [implementation notes](#retain-implementation-requirements) and the semantic read-back as currently unplannable rather than being invented as Plan JSON or promised a GapSet record. The authored `delivery` channel block itself remains in the Plan and receives its expected service-support gap. diff --git a/test/release-compatibility.test.mjs b/test/release-compatibility.test.mjs index 107de87..41cc865 100644 --- a/test/release-compatibility.test.mjs +++ b/test/release-compatibility.test.mjs @@ -27,7 +27,7 @@ test("release compatibility matches the installable plugin manifest", async () = const cliConfigurationUrl = `https://github.com/firstdraft/skills/blob/claude-v${compatibility.version}/script/cli-contract/config.mjs`; - for (const name of ["diagnostics-and-recovery.md", "foundation-plan-021.md"]) { + for (const name of ["diagnostics-and-recovery.md", "foundation-plan-022.md"]) { const reference = await readText(`skills/create-full-stack-app/references/${name}`); assert.equal( [...reference.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)] @@ -42,13 +42,13 @@ test("release compatibility matches the installable plugin manifest", async () = assert.deepEqual(compatibility, { format: "firstdraft.release-compatibility/1", component: "skills", - version: "0.5.0", + version: "0.6.0", plugin_source: { package: "@firstdraft.com/claude-code", tarball_sha256: compatibility.plugin_source.tarball_sha256, }, requires: { - api_contract: [">= 0.5.0", "< 0.6.0"], + api_contract: [">= 0.6.0", "< 0.7.0"], cli: [`= ${cliPackageVersion}`], foundation_plan_formats: [foundationPlanFormat], }, diff --git a/test/repository.test.mjs b/test/repository.test.mjs index 1c66b78..02072c6 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 = "87760d2717d914b69a7bbf7e5d372ffe8aac50d2"; +const currentFoundationPlanServiceBaseline = "4a89f5e3ab027c9099c21ff4912b3eb4af5c93e0"; const foundationPlanSchemaDigest = - "bef23a55f7d09509f0beab14df7620a49660908d7fb6c4435c99489a0dc9553d"; + "fcd0123860d4f4a35bdfb9b97b17b6aa220bf5396fb471cf2e5f65d444c9843a"; const currentFoundationPlanSchemaBaseline = currentFoundationPlanServiceBaseline; const prettyJsonSha256 = (value) => createHash("sha256") @@ -215,8 +215,8 @@ test("Claude Code packaging selects canonical authoring source exactly once", as version: marketplace.plugins[0].version, registry: "https://registry.npmjs.org/", }); - assert.equal(packageTemplate.version, "0.5.0"); - assert.equal(installableManifest.version, "0.5.0"); + assert.equal(packageTemplate.version, "0.6.0"); + assert.equal(installableManifest.version, "0.6.0"); assert.equal(packageTemplate.dependencies, undefined); assert.deepEqual(installableManifest.skills, checkoutManifest.skills); assert.equal(installableManifest.userConfig, undefined); @@ -648,7 +648,7 @@ test("authored JSON examples parse and retain the pinned Plan contract", async ( skillsDirectory, "create-full-stack-app", "references", - "foundation-plan-021.md", + "foundation-plan-022.md", ), ); const fixture = JSON.parse( @@ -1122,7 +1122,7 @@ test("validator evals stage the required Plan and private state", async () => { test("complete examples and eval Plans validate against the bundled exact schema", async () => { const skillDirectory = path.join(skillsDirectory, "create-full-stack-app"); const schemaSource = await readFile( - path.join(skillDirectory, "references", "foundation-plan-0.21.schema.json"), + path.join(skillDirectory, "references", "foundation-plan-0.22.schema.json"), "utf8", ); assert.equal( @@ -1130,7 +1130,7 @@ test("complete examples and eval Plans validate against the bundled exact schema foundationPlanSchemaDigest, ); const referenceSource = await readFile( - path.join(skillDirectory, "references", "foundation-plan-021.md"), + path.join(skillDirectory, "references", "foundation-plan-022.md"), "utf8", ); assert(referenceSource.includes(foundationPlanSchemaDigest)); diff --git a/test/ui-foundation-authoring.test.mjs b/test/ui-foundation-authoring.test.mjs index 25de3ff..06b9211 100644 --- a/test/ui-foundation-authoring.test.mjs +++ b/test/ui-foundation-authoring.test.mjs @@ -8,7 +8,7 @@ import Ajv2020 from "ajv/dist/2020.js"; import { canonicalPluginSkillNames } from "../script/claude-plugin-boundaries.mjs"; const schema = JSON.parse(await readFile( - new URL("../skills/create-full-stack-app/references/foundation-plan-0.21.schema.json", import.meta.url), + new URL("../skills/create-full-stack-app/references/foundation-plan-0.22.schema.json", import.meta.url), "utf8", )); const ajv = new Ajv2020({ allErrors: true, strict: true, strictRequired: false }); @@ -81,12 +81,33 @@ test("Appearance offers only the authored theme choices", () => { test("the bundled Plan contract replaces the previous input identity", () => { const validatePlan = ajv.getSchema(schema.$id); const plan = { - format: "firstdraft.foundation-plan.sketch/0.21", + format: "firstdraft.foundation-plan.sketch/0.22", target: { id: "rails", profile: "rails-sketch/2026-09" }, application: { key: "theme_app", name: "Theme App", native: {}, delivery: {}, entities: [], appearance: { theme: "toggle" } }, }; assert(validatePlan(plan)); - assert(!validatePlan({ ...plan, format: "firstdraft.foundation-plan.sketch/0.20" })); + assert(!validatePlan({ ...plan, format: "firstdraft.foundation-plan.sketch/0.21" })); +}); + +test("Application PWA selection is optional, boolean, and independent of other choices", () => { + const base = { + key: "movie_catalog", + name: "Movie Catalog", + native: {}, + delivery: {}, + entities: [], + }; + for (const selection of [{}, { pwa: true }, { pwa: false }]) { + for (const configuration of [{}, { appearance: { theme: "toggle" }, native: { ios: {}, android: {} } }]) { + const application = { ...base, ...configuration, ...selection }; + const original = structuredClone(application); + assert(validate("application", application)); + assert.deepEqual(application, original); + } + } + for (const pwa of [null, "true", "false", 0, 1, {}, []]) { + assert(!validate("application", { ...base, pwa })); + } });