diff --git a/.devcontainer/agent-versions.env b/.devcontainer/agent-versions.env index ecdc7d1..448418c 100644 --- a/.devcontainer/agent-versions.env +++ b/.devcontainer/agent-versions.env @@ -1,11 +1,12 @@ -# Last reviewed together on 2026-08-30. Re-review every pin before each template release. +# Tool/runtime pins retained from the 2026-08-30 review. Re-review before each template release. CLAUDE_CODE_VERSION=2.1.226 CODEX_VERSION=0.147.0 FIRSTDRAFT_CLI_VERSION=0.2.2 # CLI 0.2.x requires staging to serve First Draft API 0.3.x before template promotion. FIRSTDRAFT_CLI_DEFAULT_API_URL=https://firstdraft.com FIRSTDRAFT_STAGING_API_URL=https://staging.firstdraft.com -FIRSTDRAFT_SKILLS_REVISION=0a765f88d1cd500168e18ce1adda03802773f35e +# Source-only root-handoff update reviewed 2026-09-05; CLI and package versions are unchanged. +FIRSTDRAFT_SKILLS_REVISION=8ae02160b44b40d21ec432cf2d1ab2772f9aae6b FIRSTDRAFT_CLAUDE_SKILL_NAME=create-full-stack-app # Codex namespaces the canonical source checkout with its root plugin manifest. FIRSTDRAFT_CODEX_SKILL_NAME=firstdraft:create-full-stack-app diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 174aba3..085ba89 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -35,5 +35,5 @@ "remoteUser": "vscode", "onCreateCommand": "sudo chown -R vscode:vscode /home/vscode/.claude /home/vscode/.codex /home/vscode/.cache", "postCreateCommand": ".devcontainer/setup-agents", - "postAttachCommand": "script/refresh-codespaces-private-port" + "postAttachCommand": "if [ -x script/refresh-codespaces-private-port ]; then exec script/refresh-codespaces-private-port; else exec design/script/refresh-codespaces-private-port; fi" } diff --git a/.devcontainer/setup-agents b/.devcontainer/setup-agents index 86ea69d..351892e 100755 --- a/.devcontainer/setup-agents +++ b/.devcontainer/setup-agents @@ -128,5 +128,6 @@ firstdraft --version printf '\nDrawing Board setup complete.\n' printf 'Next: paste your staging First Draft token into the `FIRSTDRAFT_API_TOKEN` entry in `.env`.\n' printf 'Then run `claude` or `codex` and describe the app you want.\n' -printf 'The ordinary Compile creates `./application`; the agent initializes and verifies it before continuing there.\n' +printf 'The internal-alpha path is `--output .`: approve moving this Drawing Board into `design/`, then continue in the app at the root.\n' +printf 'Keep the same Git repository; never run the nested application initializer after root adoption.\n' printf 'Ask explicitly for a private GitHub repository if you want the separate Publication mode.\n' diff --git a/AGENTS.md b/AGENTS.md index b9865e3..1eedeac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,50 +1,52 @@ # AGENTS.md — First Draft Drawing Board -This repository root is a pre-compilation workspace. Help the user describe one application and maintain its current -Foundation Plan through the installed `create-full-stack-app` Skill. A generated Rails Foundation may later appear -under the ignored `application/` directory; keep its source and commands within that directory rather than mixing -them into the Drawing Board root. +This repository starts as a planning workspace. Help the user describe one application and maintain its current +Foundation Plan through the installed `create-full-stack-app` Skill. The internal-alpha path compiles into this +root, preserving its Git history and remote while moving the original Drawing Board material under `design/`. A plain request such as "Make me an app that tracks my inventory" is enough to begin; do not require the user to name the Skill or translate the request into a command. ## Working boundary - Follow the installed Skill for interviewing, Plan structure, validation, submission, Compilation, and recovery. -- The current candidate is `.firstdraft/foundation-plan.json`. Its sibling `state.json` is private CLI concurrency - state: never print, paste, commit, edit as Plan content, or expose it to another service. -- `.firstdraft/` is deliberately ignored by Git. Review the current Plan directly; do not infer its state from a - Git diff. -- `application/` is deliberately ignored by the Drawing Board. When present, it is the generated Rails Foundation; - run `bin/setup`, `bin/dev`, Rails, and application tests from there. The root `bin/` remains Drawing Board tooling. - Direct-download output has no nested Git repository; run root `script/initialize-application` once before entering - it so Git-sensitive application checks cannot resolve to the enclosing Drawing Board repository. Run it before - setup or edits; it stops without deleting ignored local state rather than risking that state in the first commit. -- In this Drawing Board, direct local output is the default completion mode. From the physical workspace root, the - installed Skill runs `bin/firstdraft plan compile --output ./application` after the user approves that exact mode. - The destination must still be absent. On success, run root `script/initialize-application application` before - setup or edits, then `script/application-smoke`; do not manufacture Git metadata or repair generated bytes by hand. +- Before Compile, the current candidate is `.firstdraft/foundation-plan.json`. Its sibling `state.json` is private + CLI concurrency state: never print, paste, commit, edit as Plan content, or expose it to another service. +- The planning `.firstdraft/` is ignored by Git. Review the current Plan directly; do not infer its state from a + Git diff. Root adoption moves this private state into `design/.firstdraft/`; the new root `.firstdraft/` contains + the generated app's submitted Plan and reviewed GapSet instead. +- Present `bin/firstdraft plan compile --output .` as the internal-alpha completion mode. Obtain explicit approval + of the reviewed Plan, gaps, and relocation of this Drawing Board into `design/` before running it from the physical + workspace root. A generic Compile request does not authorize that relocation. If root eligibility fails, preserve + the workspace and explain the exact refusal; do not delete files or switch modes to force it through. +- After root adoption, keep the existing `.git` and remote. Inspect and commit the staged generated baseline before + setup or source edits, checking that no credentials are staged. Never run `script/initialize-application` or + `script/application-smoke`, including their copies under `design/`: those helpers require a separate nested app. + Run ordinary Rails commands at the new root. For later First Draft authoring commands, enter `design/` and use its + `bin/firstdraft`; do not rely on the original container's bare `firstdraft` PATH after relocation. +- If the user chooses the optional nested mode, run `bin/firstdraft plan compile --output ./application` into an + absent destination. Then run root `script/initialize-application application` before setup or edits, followed by + `script/application-smoke`. Only in this mode is `application/` an ignored, separate Git repository with no remote; + continue with Rails commands inside it. Do not manufacture Git metadata or repair generated bytes by hand. - If `application/` already exists, preserve it. Never delete, overwrite, or move it merely to satisfy the absent-path precondition. Stop and ask the user whether to continue in the existing application, push its nested `main` to an approved remote when that repository exists, or preserve it under the Drawing Board's ignored, bind-mounted `tmp/` before starting a different Compile. Do not use `/tmp`, the container home, or another path that a Codespace rebuild discards. -- The pinned CLI also supports explicit POSIX current-root adoption with `--output .`, but that is not this - template's default. Select it only when the user asks to turn this exact root into the application and has approved - moving the existing Drawing Board material under `design/`; never infer it from a generic request to Compile. - If a Codespaces forwarded-port URL reaches Rails' **Blocked hosts** page, preserve the exact generated revision, report the observed response, and stop. Do not edit or clear `config.hosts`, or add host-admission environment variables to the Drawing Board or the generated application. - Zero-flag `bin/firstdraft plan compile` is a separate mode for a user who explicitly asks for a private GitHub - repository. Never switch modes to recover from an ambiguous start. Neither mode deploys the application. + repository. Never switch modes to recover from an ambiguous start. No Compile mode deploys the application. - Use `bin/firstdraft` for every First Draft command. When the installed Skill shows `firstdraft ...`, pass those same arguments to this repository wrapper. It loads the ignored `.env`, requires staging, and launches the exact pinned standalone CLI for both Claude and Codex. Do not bypass it, call the service with improvised HTTP, or install another CLI version. If Codex's sandbox blocks the expected staging request, request approval for that exact wrapper command; do not work around the sandbox or broaden network access. -- Setup creates `.env` from `.env.example` without overwriting it. The user pastes the staging First Draft token +- Drawing Board setup creates `.env` from `.env.example` without overwriting it. The user pastes the staging token there once. Never read, print, edit, or commit `.env`; do not ask for `/plugin` configuration, Codespaces secrets, shell exports, or a GitHub PAT. Use `bin/agent-doctor --installation-only` for installation diagnostics and the - full `bin/agent-doctor` to add validation of the shared wrapper and `.env` without printing the token. + full `bin/agent-doctor` to add validation of the shared wrapper and `.env` without printing the token. These are + pre-Compile diagnostics; after root adoption, use the generated README and the exact Rails error. ## Collaboration and credentials @@ -57,11 +59,12 @@ name the Skill or translate the request into a command. private repository, not a branch or pull request in this Drawing Board. Route an installation-readiness failure back to First Draft's **Connect GitHub App** flow; do not improvise credentials. - Do not publish or release packages from this repository. -- After direct Compile and initialization succeed, report `application/`, its nested initial commit, and its - `.firstdraft/submitted-foundation-plan.json` and `.firstdraft/gaps.json`. State that the parent Drawing Board - ignores this local repository and it has no remote; before the Codespace is deleted, offer to create and push a - remote only with the user's approval. Continue inside that directory. After explicit Publication succeeds, report - the validated private GitHub URL and continue only in a separate checkout when the user asks. +- After root Compile succeeds, report the generated-baseline commit, existing remote, and root + `.firstdraft/submitted-foundation-plan.json` and `.firstdraft/gaps.json`. Help save later edits in a separate commit + and push to that same repository when authorized. In nested mode, report the nested initial commit and no-remote + boundary; before the Codespace is deleted, offer to create and push a remote only with the user's approval. + After explicit Publication succeeds, report the validated private GitHub URL and continue only in a separate + checkout when the user asks. ## Continue after Compile @@ -70,9 +73,9 @@ in the generated application with its normal Rails tools, tests, and README. Com for an edited application. Keep missing Plan meaning visible in the retained GapSet, but do not require every gap to be closed before ordinary development or handoff. -Follow the [beginner guide](README.md#8-save-your-app-to-github) for saving the separate application repository and -deployment. Deployment is an ordinary application-development step when the user requests it, not another Compile -or a prerequisite for continuing locally. Inspect that application's production configuration and current provider +Follow the [beginner guide](README.md#8-save-your-app-to-github) for saving to the existing repository after root +adoption. Deployment is optional application-development work when the user requests it, not another Compile +or a prerequisite for the internal-alpha test. Inspect that application's production configuration and current provider guides; prefer Rails conventions over new First Draft deployment machinery. Never infer permission to spend money, publish source, or expose private data from an earlier Compile approval. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3f806d..4c9e157 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,13 +15,15 @@ A repository created from this template must provide one ready-to-use workspace - bare `firstdraft` on the Codespace PATH resolves to `bin/firstdraft`, and AGENTS.md routes Skill-issued commands through that wrapper; and - the same container carries the current generated Foundation's Ruby and Node toolchain plus healthy PostgreSQL; - generated browser tests start the pinned Selenium service on demand, so an ignored application under - `./application` can be developed without a second Codespace. - -The template does not commit generated application source. `application/` is ignored local output; its Rails source -remains distinct from the Drawing Board even though both use one container. The pinned CLI and Skill make direct -materialization available, and Drawing Board's `AGENTS.md` selects the absent `application/` directory as this -template's default completion mode. Zero-flag GitHub Publication remains a separate explicit mode. The accepted + generated browser tests start the pinned Selenium service on demand, so the generated application can be + developed without a second Codespace. + +The template itself does not contain generated application source. For the internal alpha, Drawing Board's +`AGENTS.md` selects explicit `--output .` approval: the application replaces the workspace layout, original material +moves under `design/`, and the same Git repository and remote hold both. Inspect and commit the staged baseline +before setup or edits. Do not run the nested initializer or application smoke after root adoption, including their +relocated copies. The optional `--output ./application` mode keeps an ignored, separate nested application; only that +mode uses the initializer and application smoke. Zero-flag GitHub Publication remains another explicit mode. The accepted cross-repository sequence and its safety boundaries live in [DIRECT_COMPILATION_PLAN.md](DIRECT_COMPILATION_PLAN.md). @@ -49,7 +51,7 @@ cross-repository sequence and its safety boundaries live in | `script/selenium` | On-demand Selenium start, status, and stop inside the Dev Container | | `script/application-smoke` | Setup, PostgreSQL, readiness, and full CI proof for a generated `./application` | -The initializer follows the generated application's own ignore rules. The only artifact-owned paths allowed to +The nested initializer follows the generated application's own ignore rules. The only artifact-owned paths allowed to bypass those rules are `.firstdraft/submitted-foundation-plan.json` and `.firstdraft/gaps.json`. Any other ignored path is preserved and stops initialization; a future generated ignored file must update this narrow allowlist and its exact-byte fixture in the same coordinated release. Canonical `0644` and `0755` modes are part of the generated @@ -145,6 +147,17 @@ as the runtime discriminator and the current CLI's visibility command is the supported control surface. This is a containment for an observed provider registration defect, not a custom tunnel or application workaround. +The `postAttachCommand` resolves that same helper at `script/refresh-codespaces-private-port` before root adoption +and `design/script/refresh-codespaces-private-port` afterward. This uses the standard +[Dev Container shell lifecycle](https://containers.dev/implementors/json_reference/#lifecycle-scripts), not a new +service: the [reference implementation](https://github.com/devcontainers/cli/blob/main/src/spec-common/injectHeadless.ts) +runs a string command in `/bin/sh` with the workspace as its working directory. An inline path selection survives +the move even when the already-running container retains its original lifecycle configuration. Helper errors still +propagate, including its active-listener refusal; no port policy changes with the path. The focused +`script/check-codespaces-private-port.mjs` exercises the configured command before and after a fixture's `script/` +directory moves under `design/`, alongside the existing private-port and listener cases. That local proof is not a +new Codespaces observation. + The repaired tunnel exposed the already-recorded generated Rails HostAuthorization boundary. Do not copy the student template's broad `config.hosts.clear` or disabled origin check into Drawing Board. Generated-app host and Origin handling remain target-owned. The [successor qualification](DIRECT_COMPILATION_PLAN.md#observed-successor-qualification-on-2026-09-0102) @@ -166,13 +179,15 @@ are owned by [firstdraft/firstdraft](https://github.com/firstdraft/firstdraft); Keep [README.md](README.md) focused on the beginner journey. Put maintainer commands and implementation details here, and keep agent-only guardrails in [AGENTS.md](AGENTS.md). If a workflow change affects what a tester must do, update -the README and verify the affected journey before landing it: template-to-`application/` for direct mode, or the -separate-repository journey for Publication. [DIRECT_COMPILATION_PLAN.md](DIRECT_COMPILATION_PLAN.md) owns the +the README and verify the affected journey before landing it: root adoption for the internal-alpha path, nested +`application/` when selected, or the separate-repository journey for Publication. +[DIRECT_COMPILATION_PLAN.md](DIRECT_COMPILATION_PLAN.md) owns the current direct-journey acceptance steps and every explicitly unfinished step; do not call that journey complete until those steps are observed. The internal-alpha delivery scope is the editor-first loop in the README: Codespace, installed Skill, existing -agent, direct Compile, boot, ordinary source iteration, and an application deployment. A separate Plan web editor, +agent, approved root Compile, boot, source inspection, ordinary source iteration, and saving to the same repository. +Deployment is optional follow-on work, not a pre-send gate for that code-sharing test. A separate Plan web editor, public plugin promotion, and completion of all realization gaps are not prerequisites. The existing web surface supplies access and credentials; an explorable read-only Plan view can improve independently. diff --git a/DIRECT_COMPILATION_PLAN.md b/DIRECT_COMPILATION_PLAN.md index 8334495..9471388 100644 --- a/DIRECT_COMPILATION_PLAN.md +++ b/DIRECT_COMPILATION_PLAN.md @@ -2,13 +2,26 @@ ## Goal -Let an agent author a Foundation Plan in a Drawing Board Codespace, compile it into `./application`, and continue -developing and testing the generated Rails Foundation in that same Codespace. This is an additive path. GitHub +Let an agent author a Foundation Plan in a Drawing Board Codespace, compile it into the workspace root, and continue +developing and testing the generated Rails Foundation in that same Codespace and Git repository. GitHub remains the authentication provider, and the existing GitHub Publication flow remains available for callers that want a separate repository. -The beginner/default slice is `./application`. CLI 0.2.2 also supports explicit current-root adoption, but Drawing -Board qualifies the nested flow first and does not select root adoption unless the user deliberately chooses it. +The internal-alpha guide now selects CLI 0.2.2's `--output .` mode after explicit approval of the Plan, gaps, and +move of existing Drawing Board files into `design/`. Git history and the remote remain in place; inspect and commit +the staged baseline before setup or edits. Never run the nested initializer or application smoke after that move. +The optional `./application` and Publication paths remain available. The packets and dated receipts below preserve +the earlier nested-first delivery sequence; they are not instructions to initialize a nested app after root Compile. + +## Current Skills source pin + +The 2026-09-05 root-handoff update selects Skills source +[`8ae02160b44b40d21ec432cf2d1ab2772f9aae6b`](https://github.com/firstdraft/skills/commit/8ae02160b44b40d21ec432cf2d1ab2772f9aae6b), +tree `735a9bc933a3d6e6062f36252750a16ce844c28e`. Its handoff keeps ordinary Rails feature work at the generated root +and forbids the moved nested-only helpers after root adoption. Compatibility still requires exact CLI `0.2.2`; +all CLI, agent, and runtime version pins are unchanged. Setup fetches this exact Skills Git revision, not a new npm +plugin release. This source pin does not claim a new installation, Codespaces, or published-plugin qualification; +the dated receipts below retain the earlier Skills SHA they actually exercised. ## Packet 1: direct artifact output in the CLI @@ -141,9 +154,11 @@ The 2026-08-30 successor pin candidate uses: `4a6c87a5853d13332f7a4b04be01ed46c3e08605`, candidate package SHA-256 `6ba0efb4fcb2dbf06d412ea8847593593fa832dc9cbcb419857a74c42e6cf74f`, requiring exact CLI `0.2.2`. -Drawing Board installs Skills from that exact source revision, so the unpublished plugin package does not block this -template. CLI 0.2.2 retains absent `./application` output and zero-flag Publication while adding explicit current-root -adoption. The beginner journey continues to select `./application`; root adoption remains a deliberate alternative. +Drawing Board installed Skills from that exact source revision at that boundary, so the unpublished plugin package +did not block the template. CLI 0.2.2 retains absent `./application` output and zero-flag Publication while adding +explicit current-root +adoption. At that successor-pin boundary, the beginner journey still selected `./application`; the current guide's +root-first choice is recorded above. ## Packet 3: one real non-prebuilt Codespace journey @@ -290,13 +305,13 @@ identity, abort the qualification, preserve its Project/request/response/timing to reconcile it; do not retry, switch modes, or create a replacement Project as an improvised recovery. Stop the Codespace after proof or a recorded abort. Do not treat a local Docker rehearsal as the Codespace observation. -## Available alternative: compile into an existing root +## Compile into an existing root CLI 0.2.2 implements POSIX current-root adoption in any eligible real directory rather than recognizing Drawing Board specially: ```sh -firstdraft plan compile --output . +bin/firstdraft plan compile --output . ``` The CLI reserves the root before network work, verifies the artifact outside it, and journals the installation. @@ -311,14 +326,15 @@ Root adoption rejects unsupported platforms, nested worktrees, unsafe entry type A failed transaction either restores the original identities or retains its private journal for explicit recovery. It never creates a Git repository, starts Publication, deploys, or substitutes for absent `./application` output. -Drawing Board deliberately keeps `./application` as the beginner default because it preserves a visible boundary -between design material and generated source and has a qualified initializer/smoke workflow. The successor -qualification should prove that default path first. A separate, explicit root-adoption observation may then verify -the current-root result without turning it into the template default. +Drawing Board now selects root adoption for the internal-alpha handoff so the same conversation can move from Plan +to inspectable source and an ordinary feature commit in one repository. The older nested path retains its separate +initializer/smoke workflow. Root mode instead uses generated `bin/setup` and `bin/ci` at the root, with +`design/script/selenium` for browser checks in the original running container; see the current README. ### Observed current-root qualification on 2026-09-02 -One fresh, non-prebuilt Codespace completed that separate observation without changing the beginner default: +One fresh, non-prebuilt Codespace completed that separate observation; it did not change the beginner default at +that time: - Drawing Board main `6f36fa22901ff818b7d369fb92ce042ec62a6a6f`, tree `4b15c5ade7465e16e7c922b996470b88b082a23e`, was copied byte-for-byte into parentless test-repository commit @@ -360,7 +376,7 @@ One fresh, non-prebuilt Codespace completed that separate observation without ch cold image pull. The unchanged CI then passed in 66.71 seconds internally and 76.81 seconds including wrapper cleanup: 60 Rails tests with 247 assertions and seven system tests with 34 assertions. The helper stopped Selenium. This required no generated-source patch, custom browser service, or Codespace rebuild, but root adoption does not - yet have the default path's one-command `script/application-smoke` orchestration inside the still-running container. + yet have the nested path's one-command `script/application-smoke` orchestration inside the still-running container. - Root `bin/dev` reached readiness through the ordinary private forwarded URL. A genuine Place form POST returned 303 and committed exactly one row; a missing-CSRF-token POST returned 422 with state unchanged; the exact forwarded Host returned 200 and an altered Host returned 403. After shutdown, the guarded port refresh completed in ten @@ -380,9 +396,9 @@ The comparison supports the existing mode split. Current-root adoption preserves carry the reviewed design directly into ordinary Rails work without a nested repository or second workspace. It also replaces the workspace layout in place, moves First Draft commands under `design/`, requires an immediate inspection and commit, and currently needs the relocated Selenium helper when qualification continues inside the container that -predated the move. `./application` remains the clearer beginner default and the only path with a dedicated nested -repository initializer followed by one-command smoke orchestration; `--output .` is a qualified, deliberate -alternative for a user who values one repository and understands the structural transition. +predated the move. `./application` has a dedicated nested repository initializer followed by one-command smoke +orchestration. The internal-alpha guide now favors `--output .` for its one-repository handoff and teaches the +structural transition explicitly; no new CLI transaction or orchestration layer is implied by that choice. ## Ownership and sequencing @@ -391,7 +407,7 @@ alternative for a user who values one repository and understands the structural Ruby/Node/PostgreSQL bump, requires a coordinated Drawing Board update to its `.firstdraft` allowlist, exact-byte fixture, container pins, and smoke assertions in the same candidate. - CLI owns direct mode, output-path validation, polling, artifact verification, exact materialization, and the - current-root relocation transaction. Drawing Board selects the absent `application/` path by default and must not + current-root relocation transaction. Drawing Board selects approved root adoption for the internal alpha and must not restate or reimplement the root transaction. - Drawing Board owns its combined Dev Container and nested-repository initialization. - The authoring Skill teaches the coherent command sequence only after the CLI contract lands; it does not duplicate @@ -400,8 +416,8 @@ alternative for a user who values one repository and understands the structural - Broad Foundation Plan realization gaps and the documentation/website audit are separate work lanes. Land packet 1 and packet 2 independently after their repository checks and reviews. Complete packet 2.5 and prove -its exact released/pinned tuple before packet 3. Qualify the successor `./application` journey before making any -Drawing Board claim about the optional root-output experience. +its exact released/pinned tuple before packet 3. The dated nested and root observations above remain separate proof; +changing the guide's preferred mode does not expand either observation to a new candidate or a container rebuild. ## Review questions diff --git a/README.md b/README.md index b5dc4c5..0e45d5a 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ # Build an app with First Draft This repository is a workspace for building an app with Claude or Codex. Describe your idea, review the plan with -your agent, and First Draft generates a Rails starting point in the `application` folder. Keep working with the same -agent to make it your own. +your agent, and First Draft generates a Rails starting point at this repository's root. The original Drawing Board +files move into `design/`; your Git history and remote stay in place. Keep working with the same agent to make it +your own. You do not need to install programming tools on your computer. The GitHub Codespace created from this template contains everything the agent needs. -The path is: **describe → review → generate → open → change → share**. You stay in the same Codespace throughout. +The path is: **describe → review → generate → open → inspect → change → save**. You stay in the same Codespace throughout. First Draft is an internal alpha: use test projects and sample data. The generated app is a head start, not a -finished product. +finished product. Sharing code is enough for this test; deployment is optional. ## Before you start @@ -17,7 +18,7 @@ You will need: - a personal GitHub account; - access to ; and -- a Claude or Codex account. +- a Claude or Codex account with access to its coding agent. Use the same personal GitHub account for the Drawing Board, Codespace, and First Draft sign-in. @@ -25,10 +26,9 @@ Use the same personal GitHub account for the Drawing Board, Codespace, and First 1. Open [firstdraft/drawing-board](https://github.com/firstdraft/drawing-board). 2. Select **Use this template**, then **Create a new repository**. -3. Give the repository a name for your app idea and select **Create repository**. +3. Give the repository a name for your app idea, choose **Private**, and select **Create repository**. -This repository is your Drawing Board. The generated application will appear later in its ignored `application` -folder, separate from the Drawing Board source. +This repository starts as your Drawing Board and becomes your application after you approve root Compile. ## 2. Open the Codespace @@ -77,6 +77,21 @@ For Claude, run this and follow its sign-in prompts: claude ``` +Use the account that already has Claude Code access. If the browser does not open, click the printed login URL, or +select the full URL and open it in your browser. You can also press `c` at the login prompt to copy the URL. If the +browser shows a login code, paste it into the terminal's `Paste code here if prompted` field, not into chat. Wait +for the terminal to confirm the login. + +If pasting into that interactive field does nothing, return to the shell and run: + +```sh +claude auth login +``` + +Open the URL it prints and paste the returned code into its terminal prompt; this command reads the code from +standard input. Then run `claude` again. Do not copy credentials between machines or create a new account to work +around a login problem. See [Claude's official container-login fallback](https://code.claude.com/docs/en/troubleshoot-install#oauth-login-fails-in-wsl2-ssh-or-containers). + For Codex, sign in the first time, then start the agent: ```sh @@ -96,44 +111,57 @@ Then describe the app in ordinary language. For example: Answer the agent's follow-up questions. It will turn your answers into a Foundation Plan, ask you to review the important choices, and show you anything the generated application will leave for later work. -When the Plan looks right, approve the in-workspace Compile. First Draft will put the generated application in -`application`, and the agent will initialize and verify it before continuing there. +When the Plan looks right, explicitly approve the root transition: + +> I approve this Plan and the gaps you showed me. Compile with --output . from this Drawing Board root. I approve +> moving the existing Drawing Board material into design/ while preserving this repository's Git history and remote. +> Inspect and commit the staged generated baseline before setup or source edits. Do not deploy. + +The agent runs `bin/firstdraft plan compile --output .`. Tracked Git changes must be clean, and `design/` and the +root-output recovery directory must not already exist. If the CLI refuses, preserve the workspace and ask the agent +to explain the exact reason; do not delete files to force it through. + +Root Compile moves the original planning files, including private CLI state and `.env`, into `design/`. It stages +the tracked moves and generated source in the **existing Git repository**; it does not create a second repository. +After inspecting that staged result for credentials, the agent commits it as the untouched generated baseline. +The root `bin/` now belongs to the generated app, so bare `firstdraft` no longer runs the wrapper that loads `.env` +and requires staging. For later First Draft authoring commands, change into `design/` and use `bin/firstdraft`. If you want a separate private GitHub repository instead, say so before approving the Compile. The agent will use the distinct Publication mode and give you that repository's URL. This mode requires the **Connect GitHub App** step -from §3. Neither mode deploys the application. +from §3. The [optional nested-output path](#optional-keep-the-app-in-application) is also available. No Compile mode +deploys the application. Codex may ask permission for an exact `bin/firstdraft ...` command to contact `staging.firstdraft.com`. Approve that command; do not grant unrelated network access. ## 6. Open your app -For the ordinary in-workspace path, the agent runs these Drawing Board commands after Compile: +After the generated baseline is committed, run the generated application's setup from the repository root: ```sh -script/initialize-application application -script/application-smoke +bin/setup --skip-server ``` -The first command saves the untouched generated source as its own initial Git version. The second sets up and checks -the app. You can ask: +Do **not** run `script/initialize-application` or `script/application-smoke` after root Compile, including their +copies under `design/`. They are only for the optional nested application. You can ask: > Set up and check the generated application, then start it and help me open its preview. To start it yourself, open a **new terminal** in the Codespace and run: ```sh -cd /workspaces/drawing-board/application +cd /workspaces/drawing-board bin/dev ``` Leave that terminal running. Open the Codespace's **Ports** tab, find port **3000**, and select **Open in Browser** (the globe icon). Keep the port **Private**. This is your development preview, available while the Codespace and app -are running; it is not the deployed app you will share later. +are running; it is not a deployed website. Try a simple action, such as adding a plant. It is normal for parts of your idea to be missing: the agent should explain what was generated and what remains. You do not need to finish every feature before beginning to use and -change the source. +change the source. Also check required-field validation and reopen, edit, and delete a sample record. The generated application includes: @@ -141,7 +169,21 @@ The generated application includes: - the exact submitted Foundation Plan at `.firstdraft/submitted-foundation-plan.json`; and - the reviewed list of remaining work at `.firstdraft/gaps.json`. -You can open either JSON file in the editor to inspect it. You do not need a separate First Draft web editor. +Before changing anything, open those JSON files and read the generated models, controllers, views, routes, +migrations, and tests. Ask which Rails APIs and established gems the app uses, and whether you would write it this +way by hand. You do not need a separate First Draft web editor. + +Run the broader checks against the committed baseline. In the still-running Drawing Board container, start its +existing browser-test service through the relocated helper: + +```sh +design/script/selenium start +CI=1 bin/ci +design/script/selenium stop +``` + +Stop Selenium after the run, including after a failure. Its first image download can take a few minutes. This is +the observed current-container path; do not rebuild the container or add a custom browser service to use it. If you explicitly chose Publication, open the private repository from the URL the agent provides. Create a Codespace there and follow its README instead; the original Drawing Board remains a separate planning workspace. @@ -154,8 +196,9 @@ Return to the **same agent conversation**. It still has the context from plannin > and help me check the change in the browser. Then try a change that matters to your idea: a clearer form, a useful field, or the next missing feature. The agent -works directly in `application`, using its README and normal Rails tools. You can keep editing, testing, and -refreshing the preview this way. +works directly in the application at the root, using its README and normal Rails tools. Review the diff and save +the feature in a **separate commit**. Run its focused tests and the broader checks above from that clean checkpoint; +record baseline and feature results separately. You can keep editing, testing, and refreshing the preview this way. You do **not** need another Compile for ordinary development. Compile creates a new starting point; it does not merge changes into the app you have been editing. Keep the planning files, but do not overwrite your application @@ -169,17 +212,34 @@ preserves its files; deleting it does not. Do this before relying on the Codespace as your only copy: -> Help me save the application to its own private GitHub repository. Show me the account and repository name before -> creating it. Commit the application changes and push them, without including credentials. +> Show me this repository's existing GitHub remote. Push the generated-baseline and separate feature commits there, +> without including credentials, and verify that both commits arrived. -The `application` folder has its **own** Git history, separate from the Drawing Board. Pushing the Drawing Board does -not save the application. Ask the agent to show you the application repository's URL and verify that your changes -are there. Continue committing and pushing from `application` as you work. +Root Compile keeps **one Git history and the same remote**. The repository you created in step 1 now contains the +application and the retained `design/` material. Continue committing and pushing from the application root as you +work. If GitHub requests authorization, use its normal sign-in; do not put tokens in source or chat. This uses ordinary GitHub tools after direct Compile; it does not require First Draft's optional Publication mode. Your source is yours to work on with another editor, agent, or developer. -## 9. Deploy when you want to share +## Optional: keep the app in `application/` + +Choose this mode **before** Compile if you want the Drawing Board to remain at the root. Approve +`bin/firstdraft plan compile --output ./application` into an absent directory, then have the agent run these commands +from the Drawing Board root before setup or source edits: + +```sh +script/initialize-application application +script/application-smoke +``` + +The initializer gives `application/` its own initial Git commit. Continue inside that directory with `bin/dev` and +normal Rails commands. The parent Drawing Board ignores it, so pushing the Drawing Board does **not** save the app. +Ask the agent to create and push an approved private application repository before deleting the Codespace. +If `application/` already exists, preserve it and ask the agent how to continue; do not overwrite or delete it to +make room for another Compile. Do not run these nested helpers after root adoption. + +## Optional: deploy later You can deploy after your first small change and keep developing afterward. Deployment gives the app a separate web address that does not depend on your Codespace being open. @@ -202,7 +262,8 @@ The steps you and your agent will follow are: the supplied Render configuration. Use its **direct connection** with connection pooling switched off. 2. Have the agent set `plan: free` on the web service in `render.yaml`, then commit and push the application. [Leaving the plan unspecified can select a paid instance](https://render.com/docs/blueprint-spec#plan). -3. In Render, choose **New → Blueprint** and connect the **application repository**, not the Drawing Board repository. +3. In Render, choose **New → Blueprint** and connect the repository containing the application at its root + (the existing repository after root Compile, or the separate application repository in nested mode). Enter the Neon connection string in the prompted `DATABASE_URL` field. Do not paste it into chat or commit it. Confirm the proposed web instance is **Free** before creating it. 4. After deployment, open the app's new web address. Have the agent check `/ready`, then create and reopen a sample @@ -214,7 +275,7 @@ sharing an app that stores uploads or sends email, have your agent configure tho ## Troubleshooting -If the initial Codespace setup did not finish, run: +Before Compile, if the initial Codespace setup did not finish, run: ```sh bin/agent-doctor --installation-only @@ -232,6 +293,11 @@ bin/agent-doctor The doctor reports whether the token is present without showing it. If it reports an `.env` permissions problem, run `chmod 600 .env` and try again. +After root Compile, use the generated application's README and the exact Rails error instead of rerunning Drawing +Board setup or its doctor. The old tooling is under `design/`, and the existing container's PATH still reflects its +pre-Compile layout. If a reconnect says the private-port refresh found an active listener, stop `bin/dev` before +rerunning `design/script/refresh-codespaces-private-port`; do not weaken its listener guard. + If a Codespaces forwarded-port URL reaches Rails' **Blocked hosts** page, stop and tell your agent. Do not disable Rails host checks; the generated target must own that correction. diff --git a/script/check b/script/check index 5c51bde..c3e4bab 100755 --- a/script/check +++ b/script/check @@ -144,7 +144,6 @@ required(JSON.stringify(configuration.customizations?.vscode?.settings) === required(configuration.containerEnv?.DB_HOST === "postgres", "Generated Foundations must reach PostgreSQL by service name."); required(configuration.containerEnv?.SELENIUM_HOST === "selenium", "Generated system tests must reach Selenium by service name."); required(JSON.stringify(configuration.forwardPorts) === JSON.stringify([3000, 5432]), "Rails and PostgreSQL ports must be forwarded."); -required(configuration.postAttachCommand === "script/refresh-codespaces-private-port", "Codespaces must refresh the private Rails port on attach."); required(compose.includes("- ..:/workspaces/drawing-board:cached"), "Compose must mount the complete Drawing Board."); required(compose.includes(`${imageReceipt.publication.package}@${imageReceipt.publication.manifest}`), "The workspace image must match its immutable receipt."); required(imageReceipt.publication.visibility === "public", "The workspace image must retain its observed public visibility."); diff --git a/script/check-codespaces-private-port.mjs b/script/check-codespaces-private-port.mjs index 7dddc3f..bbaa650 100755 --- a/script/check-codespaces-private-port.mjs +++ b/script/check-codespaces-private-port.mjs @@ -10,7 +10,10 @@ import {fileURLToPath} from "node:url"; const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const refresher = path.join(repositoryRoot, "script", "refresh-codespaces-private-port"); +const {postAttachCommand} = JSON.parse(fs.readFileSync(path.join(repositoryRoot, ".devcontainer", "devcontainer.json"), "utf8")); +assert.equal(typeof postAttachCommand, "string", "The post-attach command must use the Dev Container shell lifecycle."); const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), "drawing-board-port-refresh-test-")); +const workspaceRoot = path.join(temporaryRoot, "workspace with spaces"); const mockBin = path.join(temporaryRoot, "bin"); const statePath = path.join(temporaryRoot, "visibility"); const logPath = path.join(temporaryRoot, "gh.log"); @@ -27,8 +30,8 @@ function writeExecutable(name, contents) { } function run(changes = {}, pathValue = `${mockBin}:/usr/bin:/bin`) { - return spawnSync(refresher, [], { - cwd: repositoryRoot, + return spawnSync("/bin/sh", ["-c", postAttachCommand], { + cwd: workspaceRoot, encoding: "utf8", env: { ...process.env, @@ -53,6 +56,9 @@ function logLines() { } try { + fs.mkdirSync(path.join(workspaceRoot, "script"), {recursive: true}); + fs.copyFileSync(refresher, path.join(workspaceRoot, "script", "refresh-codespaces-private-port")); + fs.chmodSync(path.join(workspaceRoot, "script", "refresh-codespaces-private-port"), 0o755); fs.mkdirSync(mockBin); writeExecutable( "gh", @@ -277,6 +283,38 @@ esac assert.equal(realNoListener.status, 0, realNoListener.stderr); assert.equal(fs.readFileSync(statePath, "utf8"), "private"); } + + fs.mkdirSync(path.join(workspaceRoot, "design")); + fs.renameSync(path.join(workspaceRoot, "script"), path.join(workspaceRoot, "design", "script")); + for (let attach = 0; attach < 2; attach += 1) { + fs.writeFileSync(statePath, "private"); + fs.writeFileSync(logPath, ""); + const adoptedRoot = run(); + assert.equal(adoptedRoot.status, 0, adoptedRoot.stderr); + assert.match(adoptedRoot.stdout, /private visibility confirmed/); + assert.equal(fs.readFileSync(statePath, "utf8"), "private"); + assert.equal(logLines().filter((line) => line.includes(" visibility ")).length, 2); + } + + fs.writeFileSync(logPath, ""); + const adoptedListener = run({MOCK_LISTENER: "true"}); + assert.notEqual(adoptedListener.status, 0); + assert.match(adoptedListener.stderr, /Refusing to re-register port 3000 while a listener is active/); + assert.match(adoptedListener.stderr, /rerun .*design\/script\/refresh-codespaces-private-port/); + assert.equal(logLines().some((line) => line.includes(" visibility ")), false); + assert.equal(fs.readFileSync(statePath, "utf8"), "private"); + + const adoptedOutside = run({CODESPACES: "false"}); + assert.equal(adoptedOutside.status, 0, adoptedOutside.stderr); + assert.match(adoptedOutside.stdout, /skipped outside GitHub Codespaces/); + + fs.renameSync(path.join(workspaceRoot, "design", "script"), path.join(workspaceRoot, "design", "missing-script")); + fs.writeFileSync(logPath, ""); + const missingHelper = run(); + assert.notEqual(missingHelper.status, 0); + assert.match(missingHelper.stderr, /design\/script\/refresh-codespaces-private-port/); + assert.deepEqual(logLines(), []); + console.log("Codespaces post-attach contracts passed before and after root adoption, including listener guards."); } finally { fs.rmSync(temporaryRoot, {recursive: true, force: true}); } diff --git a/script/refresh-codespaces-private-port b/script/refresh-codespaces-private-port index 1bd499e..368deb4 100755 --- a/script/refresh-codespaces-private-port +++ b/script/refresh-codespaces-private-port @@ -169,7 +169,7 @@ if ! read_listener_state; then fi current_listener_state="${listener_state_result}" if [[ "${current_listener_state}" == "active" ]]; then - echo "Refusing to re-register port ${port} while a listener is active; stop the server and rerun script/refresh-codespaces-private-port." >&2 + printf 'Refusing to re-register port %s while a listener is active; stop the server and rerun %q.\n' "${port}" "${BASH_SOURCE[0]}" >&2 exit 1 fi