diff --git a/README.md b/README.md index 4000f8c..45a9364 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ install the CLI and Skill, then compile into your current folder with `firstdraf clone or GitHub push is required. The [Drawing Board guide](https://github.com/firstdraft/drawing-board#build-an-app-with-first-draft) is the Codespaces fallback. -CLI 0.4 makes `--output .` the default. Keep the explicit flag with CLI 0.3, whose zero-flag command selects GitHub +CLI 0.4 and later make `--output .` the default. Keep the explicit flag with CLI 0.3, whose zero-flag command selects GitHub publication. ## What this repository owns diff --git a/RELEASING.md b/RELEASING.md index 6d53f32..a71ed4e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -16,10 +16,14 @@ release tag. An unpublished, untagged candidate can retain its proposed version The published version may remain in source during documentation and test maintenance; recording its release history does not require preparing another version. Choose an unused version when preparing the next publication. -CLI `0.4.x` makes `firstdraft plan compile` equivalent to `firstdraft plan compile --output .`; the former GitHub -default becomes explicit `--github`. This is a breaking CLI change from `0.3.x`, without a Service API change. -Both lines use API `0.4.x`, Plan `firstdraft.foundation-plan.sketch/0.20`, target `rails-sketch/2026-09`, and the -`.firstdraft/design` root archive. Existing applications and old Plans are not migrated. +CLI `0.5.x` requires API `0.5.x` and Plan `firstdraft.foundation-plan.sketch/0.21`. Plan `0.21` adds the authored +`appearance.theme: "toggle"` choice and replaces the sole accepted `0.20` input format. The new API and CLI minor +versions record that input and artifact compatibility break; they add no migration or compatibility bridge. +The target stays `rails-sketch/2026-09`. + +Local output remains the default introduced in CLI `0.4.x`: `firstdraft plan compile` is equivalent to +`firstdraft plan compile --output .`, with GitHub publication selected by explicit `--github`. The root archive +remains `.firstdraft/design`. Existing applications and old Plans are not migrated. `release/compatibility.json` declares the package version, accepted API-contract range, and accepted Plan formats. It is source-only metadata, validated by the normal test suite and absent from the npm tarball. Coordinate the diff --git a/docs/commands.md b/docs/commands.md index 66a8ca9..4ce7f4d 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -3,10 +3,10 @@ This page owns the detailed public semantics of the current command surface. Run `firstdraft --help` or a command group's `--help` for concise executable syntax. See [Errors and recovery](errors.md) before retrying a failed mutation. -The current `0.4.x` source line contains the auditable command shell, local Foundation Plan initialization, local +The current `0.5.x` source line contains the auditable command shell, local Foundation Plan initialization, local application-key and UUID generation, conditional whole-document push, whole-graph analysis status polling, direct -Compile-and-materialize and private publish orchestration, and retained-Compilation inspection. CLI `0.4.x` -requires the service's `0.4.x` API contract. See the [release policy](../RELEASING.md) for versioning and channel +Compile-and-materialize and private publish orchestration, and retained-Compilation inspection. CLI `0.5.x` +requires the service's `0.5.x` API contract. See the [release policy](../RELEASING.md) for versioning and channel semantics and [release history](release-history.md) for the transition from prereleases. ## Command map @@ -45,7 +45,7 @@ From the project that the Plan describes: firstdraft plan init --name "Oscar Party" ``` -This creates an empty `firstdraft.foundation-plan.sketch/0.20` Plan targeting `rails-sketch/2026-09` and a +This creates an empty `firstdraft.foundation-plan.sketch/0.21` Plan targeting `rails-sketch/2026-09` and a client-generated Project ID under `.firstdraft/`. A nested ignore file keeps that local scratch area out of Git without changing the project's own `.gitignore`. Initialization makes no network request and refuses to replace an existing `.firstdraft` path. @@ -145,7 +145,7 @@ This is equivalent to `firstdraft plan compile --output .`. Use `--output ./appl directory, or `--github` to publish to a private GitHub repository. `--github` and `--output` are mutually exclusive. No GitHub connection, repository clone, or push is required for local compilation. Compilation runs on the First Draft service; output and the application runtime are local. CLI `0.3.x` used GitHub Publication as its default; -scripts that require that behavior must add `--github` when upgrading to `0.4.x`. +scripts that require that behavior must add `--github` when upgrading to `0.4.x` or later. Both `plan compile` modes first push the exact current bytes in `.firstdraft/foundation-plan.json`, even when those bytes are unchanged, and save the accepted ETag using the same @@ -167,7 +167,7 @@ destination is checked again after analysis; root adoption instead holds its own pre-move identity recheck described below. Other existing destinations remain invalid, so `--output ./application` retains its absent-directory contract. -The nested archive layout below is shared by CLI `0.3.x` and `0.4.x`. +The nested archive layout below is shared by CLI `0.3.x` and later. [Published CLI `0.2.2`](release-history.md#022-publication-and-registry-observation) archives at top-level `design/`; existing applications are not migrated automatically. @@ -296,7 +296,7 @@ local paths, or environment values. Success writes exactly the validated private newline to stdout. If the command fails after progress has begun, its structured JSON error envelope is the final stderr document after the progress lines. -The closed API `0.4.x` progress-reason allowlist is `github.configuration_missing`, `github.oauth_unavailable`, +The closed API `0.5.x` progress-reason allowlist is `github.configuration_missing`, `github.oauth_unavailable`, `github.api_unavailable`, `github.reauthorization_required`, `github.account_mismatch`, `github.installation_unavailable`, `github.installation_not_ready`, `github.preflight_unavailable`, the legacy-only `github.preflight_unclassified`, and these stage-specific fallbacks: `github.preflight_unavailable.configuration`, @@ -345,7 +345,7 @@ when a retained direct Compilation succeeded but an earlier root materialization never starts replacement work. An incomplete rollback leaves `.firstdraft-root-output` and requires journal reconciliation before this command can run again. -Artifact validation accepts only Plan format `firstdraft.foundation-plan.sketch/0.20` and target profile +Artifact validation accepts only Plan format `firstdraft.foundation-plan.sketch/0.21` and target profile `rails-sketch/2026-09`, in addition to matching the retained Compilation's provenance and verifying file integrity. An earlier format or profile is rejected before materialization, even when the retained status names that profile. diff --git a/package-lock.json b/package-lock.json index 16b6f91..acc53f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@firstdraft.com/cli", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@firstdraft.com/cli", - "version": "0.4.0", + "version": "0.5.0", "license": "MIT", "bin": { "firstdraft": "bin/firstdraft.js" diff --git a/package.json b/package.json index cc461dd..efce6c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@firstdraft.com/cli", - "version": "0.4.0", + "version": "0.5.0", "description": "Command-line interface for First Draft", "license": "MIT", "type": "module", diff --git a/release/compatibility.json b/release/compatibility.json index fc6e5ae..1cc860b 100644 --- a/release/compatibility.json +++ b/release/compatibility.json @@ -1,9 +1,9 @@ { "format": "firstdraft.release-compatibility/1", "component": "cli", - "version": "0.4.0", + "version": "0.5.0", "requires": { - "api_contract": [">= 0.4.0", "< 0.5.0"], - "foundation_plan_formats": ["firstdraft.foundation-plan.sketch/0.20"] + "api_contract": [">= 0.5.0", "< 0.6.0"], + "foundation_plan_formats": ["firstdraft.foundation-plan.sketch/0.21"] } } diff --git a/scripts/smoke-package.js b/scripts/smoke-package.js index d26eb21..dc10565 100644 --- a/scripts/smoke-package.js +++ b/scripts/smoke-package.js @@ -149,7 +149,7 @@ try { name: initializedPlan.application.name, }, { - format: "firstdraft.foundation-plan.sketch/0.20", + format: "firstdraft.foundation-plan.sketch/0.21", target: { id: "rails", profile: "rails-sketch/2026-09" }, key: "oscar_party", name: "Oscar Party", @@ -399,7 +399,7 @@ async function exercisePackedCompilation(projectDirectory) { graph_version: 1, head_source_sha256: headSha256, foundation_plan: { - format: "firstdraft.foundation-plan.sketch/0.20", + format: "firstdraft.foundation-plan.sketch/0.21", sha256: foundationPlanSha256, }, analysis: { @@ -533,7 +533,7 @@ async function exercisePackedCompilation(projectDirectory) { { project: { id: projectId, graph_version: 1 }, foundation_plan: { - format: "firstdraft.foundation-plan.sketch/0.20", + format: "firstdraft.foundation-plan.sketch/0.21", source_sha256: headSha256, }, diagnostics: [], diff --git a/src/compilation-artifact.js b/src/compilation-artifact.js index 7ff0592..f9547c2 100644 --- a/src/compilation-artifact.js +++ b/src/compilation-artifact.js @@ -26,7 +26,7 @@ import { export const ARTIFACT_MEDIA_TYPE = "application/vnd.firstdraft.compilation-artifact+json"; export const ARTIFACT_FORMAT = "firstdraft.compilation-artifact/1"; -export const FOUNDATION_PLAN_FORMAT = "firstdraft.foundation-plan.sketch/0.20"; +export const FOUNDATION_PLAN_FORMAT = "firstdraft.foundation-plan.sketch/0.21"; export const RAILS_TARGET_PROFILE = "rails-sketch/2026-09"; export const MAX_ARTIFACT_BYTES = 128 * 1024 * 1024; diff --git a/test/compilation-artifact.test.js b/test/compilation-artifact.test.js index e39c5b2..158073a 100644 --- a/test/compilation-artifact.test.js +++ b/test/compilation-artifact.test.js @@ -36,7 +36,7 @@ const SUBJECT_ID = "01900000-0000-7000-8000-000000000804"; const HEAD_SHA256 = "1".repeat(64); const FOUNDATION_PLAN_SHA256 = "5".repeat(64); const COMPILER_RELEASE = - "foundation-plan-rails/compiler-application-2026-09-19-conventions"; + "foundation-plan-rails/compiler-application-2026-09-22-theme"; const TARGET = { id: "rails", profile: "rails-sketch/2026-09" }; const EXPECTED = { projectId: PROJECT_ID, @@ -262,7 +262,7 @@ test("pins external provenance identities and validates nested metadata", () => artifactFixture({ provenance: { foundation_plan: { - format: "firstdraft.foundation-plan.sketch/0.19", + format: "firstdraft.foundation-plan.sketch/0.20", sha256: FOUNDATION_PLAN_SHA256, }, }, @@ -279,7 +279,7 @@ test("pins external provenance identities and validates nested metadata", () => provenance: { analysis: { id: "01900000-0000-7000-8000-000000000899", - release: "foundation-plan-rails/application-2026-09-19-conventions", + release: "foundation-plan-rails/application-2026-09-22-theme", }, }, }), @@ -482,7 +482,7 @@ function artifactFixture(changes = {}) { }, analysis: { id: ANALYSIS_ID, - release: "foundation-plan-rails/application-2026-09-19-conventions", + release: "foundation-plan-rails/application-2026-09-22-theme", }, compiler_release: COMPILER_RELEASE, target: TARGET, diff --git a/test/compilation.test.js b/test/compilation.test.js index fec81d9..2ce24e1 100644 --- a/test/compilation.test.js +++ b/test/compilation.test.js @@ -33,14 +33,14 @@ const CREATED_AT = "2026-08-04T12:00:00.000000Z"; const STARTED_AT = "2026-08-04T12:00:01.000000Z"; const COMPLETED_AT = "2026-08-04T12:00:02.000000Z"; const COMPILER_RELEASE = - "foundation-plan-rails/compiler-application-2026-09-19-conventions"; + "foundation-plan-rails/compiler-application-2026-09-22-theme"; const TARGET = { id: "rails", profile: "rails-sketch/2026-09" }; const STATUS_PATH = `/v1/projects/${PROJECT_ID}/compilations/${COMPILATION_ID}`; const ARTIFACT_PATH = `${STATUS_PATH}/artifact`; test("direct Compilation pins the reviewed Plan and materializes one retained artifact", async (context) => { const planSource = Buffer.from( - '{"format":"firstdraft.foundation-plan.sketch/0.20"}\n', + '{"format":"firstdraft.foundation-plan.sketch/0.21"}\n', ); const headSourceSha256 = sha256(planSource); const etag = `"sha256:${headSourceSha256}"`; @@ -671,7 +671,7 @@ function artifactFixture(changes = {}) { }, analysis: { id: ANALYSIS_ID, - release: "foundation-plan-rails/application-2026-09-19-conventions", + release: "foundation-plan-rails/application-2026-09-22-theme", }, compiler_release: COMPILER_RELEASE, target: TARGET, diff --git a/test/plan-compile.test.js b/test/plan-compile.test.js index 1d9fd2a..a0efcab 100644 --- a/test/plan-compile.test.js +++ b/test/plan-compile.test.js @@ -28,7 +28,7 @@ const COMPILATION_ID = "01900000-0000-7000-8000-000000002003"; const PUBLICATION_ID = "01900000-0000-7000-8000-000000002004"; const API_TOKEN = `fd_${"a".repeat(43)}`; const PLAN_SOURCE = Buffer.from( - '{"format":"firstdraft.foundation-plan.sketch/0.20","application":{"key":"movie_catalog","name":"Movie Catalog"}}\n', + '{"format":"firstdraft.foundation-plan.sketch/0.21","application":{"key":"movie_catalog","name":"Movie Catalog"}}\n', ); const HEAD_SHA256 = sha256(PLAN_SOURCE); const ETAG = `"sha256:${HEAD_SHA256}"`; @@ -36,10 +36,9 @@ const CREATED_AT = "2026-08-04T12:00:00.000Z"; const STARTED_AT = "2026-08-04T12:00:01.000Z"; const COMPLETED_AT = "2026-08-04T12:00:02.000Z"; const REPOSITORY_URL = "https://github.com/octocat/movie-catalog"; -const ANALYZER_RELEASE = - "foundation-plan-rails/application-2026-09-19-conventions"; +const ANALYZER_RELEASE = "foundation-plan-rails/application-2026-09-22-theme"; const COMPILER_RELEASE = - "foundation-plan-rails/compiler-application-2026-09-19-conventions"; + "foundation-plan-rails/compiler-application-2026-09-22-theme"; const TARGET = { id: "rails", profile: "rails-sketch/2026-09" }; const SUCCESS_PROGRESS = `First Draft: Analyzing Foundation Plan... First Draft: Foundation Plan analysis valid. @@ -1101,7 +1100,7 @@ function acceptedPlanBody(graphVersion = 1) { return { project: { id: PROJECT_ID, graph_version: graphVersion }, foundation_plan: { - format: "firstdraft.foundation-plan.sketch/0.20", + format: "firstdraft.foundation-plan.sketch/0.21", source_sha256: HEAD_SHA256, }, diagnostics: [], diff --git a/test/plan-init.test.js b/test/plan-init.test.js index fdc052b..b306aac 100644 --- a/test/plan-init.test.js +++ b/test/plan-init.test.js @@ -62,7 +62,7 @@ const PLAN_INIT_ERROR = jsonOutput({ }); const EXPECTED_PLAN = `{ - "format": "firstdraft.foundation-plan.sketch/0.20", + "format": "firstdraft.foundation-plan.sketch/0.21", "target": { "id": "rails", "profile": "rails-sketch/2026-09" diff --git a/test/plan-publish.test.js b/test/plan-publish.test.js index 920493c..13598a5 100644 --- a/test/plan-publish.test.js +++ b/test/plan-publish.test.js @@ -15,7 +15,7 @@ const ANALYSIS_ID = "01900000-0000-7000-8000-000000001004"; const PUBLICATION_ID = "01900000-0000-7000-8000-000000001005"; const OTHER_PUBLICATION_ID = "01900000-0000-7000-8000-000000001006"; const PLAN_SOURCE = Buffer.from( - '{"format":"firstdraft.foundation-plan.sketch/0.20"}\n', + '{"format":"firstdraft.foundation-plan.sketch/0.21"}\n', ); const HEAD_SHA256 = sha256(PLAN_SOURCE); const ETAG = `"sha256:${HEAD_SHA256}"`; @@ -24,10 +24,9 @@ const CREATED_AT = "2026-08-01T12:00:00.000Z"; const STARTED_AT = "2026-08-01T12:00:01.000Z"; const COMPLETED_AT = "2026-08-01T12:00:02.000Z"; const RETRY_AT = "2026-08-07T16:15:00.000000Z"; -const ANALYZER_RELEASE = - "foundation-plan-rails/application-2026-09-19-conventions"; +const ANALYZER_RELEASE = "foundation-plan-rails/application-2026-09-22-theme"; const COMPILER_RELEASE = - "foundation-plan-rails/compiler-application-2026-09-19-conventions"; + "foundation-plan-rails/compiler-application-2026-09-22-theme"; const TARGET = { id: "rails", profile: "rails-sketch/2026-09" }; const ARTIFACT = { sha256: "1".repeat(64), diff --git a/test/plan-push.test.js b/test/plan-push.test.js index 2605c9c..08efb40 100644 --- a/test/plan-push.test.js +++ b/test/plan-push.test.js @@ -1274,7 +1274,7 @@ function acceptedBody(source, diagnostics = []) { return { project: { id: PROJECT_ID, graph_version: 1 }, foundation_plan: { - format: "firstdraft.foundation-plan.sketch/0.20", + format: "firstdraft.foundation-plan.sketch/0.21", source_sha256: sha256(source), }, diagnostics, diff --git a/test/release-compatibility.test.js b/test/release-compatibility.test.js index a905eb6..869097c 100644 --- a/test/release-compatibility.test.js +++ b/test/release-compatibility.test.js @@ -35,8 +35,8 @@ test("release compatibility declares the coordinated CLI contract", () => { "foundation_plan_formats", ]); assert.deepEqual(compatibility.requires.api_contract, [ - ">= 0.4.0", - "< 0.5.0", + ">= 0.5.0", + "< 0.6.0", ]); assert.deepEqual(compatibility.requires.foundation_plan_formats, [ FOUNDATION_PLAN_FORMAT,