forgedock 1.0.21 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +227 -174
  2. package/bin/cinema.mjs +320 -0
  3. package/bin/demo.mjs +386 -0
  4. package/bin/engine/phases.mjs +148 -0
  5. package/bin/engine/projector.mjs +32 -0
  6. package/bin/engine/reconcile.mjs +11 -0
  7. package/bin/engine/runlog.mjs +79 -0
  8. package/bin/engine/state.mjs +27 -0
  9. package/bin/engine-cli.mjs +50 -0
  10. package/bin/engine.mjs +138 -0
  11. package/bin/forgedock.mjs +693 -1393
  12. package/bin/init-detect.mjs +95 -0
  13. package/bin/journey.mjs +783 -0
  14. package/bin/runner.mjs +847 -0
  15. package/bin/settings-hook.mjs +112 -0
  16. package/bin/tui.mjs +6 -3
  17. package/commands/autopilot.md +4 -4
  18. package/commands/cleanup.md +85 -39
  19. package/commands/deploy-info.md +11 -5
  20. package/commands/design-bench.md +1 -0
  21. package/commands/design-render-critique-loop.md +1 -0
  22. package/commands/design.md +1 -0
  23. package/commands/forge-stats.md +2 -1
  24. package/commands/issue.md +1 -1
  25. package/commands/optimize.md +1 -1
  26. package/commands/orchestrate.md +346 -34
  27. package/commands/quality-gate.md +168 -80
  28. package/commands/review-pr-agents.md +69 -45
  29. package/commands/review-pr.md +54 -6
  30. package/commands/scope.md +11 -3
  31. package/commands/security-audit.md +24 -16
  32. package/commands/sync-ecosystem.md +2 -2
  33. package/commands/work-on/build/architect.md +47 -3
  34. package/commands/work-on/build/context.md +36 -5
  35. package/commands/work-on/build/implement.md +16 -4
  36. package/commands/work-on/build/validate.md +54 -8
  37. package/commands/work-on/build.md +27 -9
  38. package/commands/work-on/close.md +131 -3
  39. package/commands/work-on/investigate.md +24 -1
  40. package/commands/work-on/review.md +24 -0
  41. package/commands/work-on-monolithic.md +1 -0
  42. package/commands/work-on.md +271 -11
  43. package/docs/BRAND.md +6 -2
  44. package/docs/CONFIG.md +39 -34
  45. package/docs/site/.vitepress/config.mjs +85 -0
  46. package/docs/site/annotations-explained.md +55 -0
  47. package/docs/site/command-learning-path.md +104 -0
  48. package/docs/site/command-reference.md +3 -0
  49. package/docs/site/getting-started.md +42 -13
  50. package/docs/site/index.md +1 -1
  51. package/docs/site/troubleshooting.md +208 -0
  52. package/docs/spec/forge-protocol-v1.md +768 -0
  53. package/docs/superpowers/plans/2026-07-03-cinematic-onboarding-tui.md +2339 -0
  54. package/docs/superpowers/plans/2026-07-03-durable-execution-engine.md +1223 -0
  55. package/docs/superpowers/specs/2026-07-03-cinematic-onboarding-tui-design.md +157 -0
  56. package/docs/superpowers/specs/2026-07-03-durable-execution-engine-design.md +194 -0
  57. package/examples/forgedock-demo/README.md +113 -0
  58. package/examples/forgedock-demo/bootstrap.sh +125 -0
  59. package/examples/forgedock-demo/issues/01-bug-delete-missing-auth.md +37 -0
  60. package/examples/forgedock-demo/issues/02-feature-filter-notes.md +37 -0
  61. package/examples/forgedock-demo/issues/03-refactor-router-table.md +33 -0
  62. package/examples/forgedock-demo/issues/04-perf-index-lookup.md +35 -0
  63. package/examples/forgedock-demo/issues/05-docs-api-reference.md +39 -0
  64. package/examples/forgedock-demo/labels.json +21 -0
  65. package/examples/forgedock-demo/package.json +16 -0
  66. package/examples/forgedock-demo/scripts/smoke.js +61 -0
  67. package/examples/forgedock-demo/src/auth.js +28 -0
  68. package/examples/forgedock-demo/src/db.js +66 -0
  69. package/examples/forgedock-demo/src/routes/notes.js +64 -0
  70. package/examples/forgedock-demo/src/server.js +79 -0
  71. package/package.json +13 -3
  72. package/scripts/verify-env-vars.sh +18 -2
  73. package/scripts/verify-host-headers.sh +4 -4
  74. package/scripts/verify-route-registration.sh +2 -2
  75. package/scripts/worktree-lifecycle.sh +202 -0
  76. package/templates/forge.yaml.minimal +44 -0
@@ -0,0 +1,157 @@
1
+ # Cinematic Onboarding TUI — Design
2
+
3
+ **Date:** 2026-07-03
4
+ **Status:** Approved (design); pending implementation plan
5
+ **Supersedes/extends:** `2026-06-09-intelligent-onboarding-design.md` (Pillars A & B were built as modules but never wired into the CLI; this design closes that seam and defines the visual/experiential layer on top)
6
+
7
+ ## Problem
8
+
9
+ The onboarding audit (2026-07-03) found two classes of failure:
10
+
11
+ 1. **The intelligent onboarding is dead code.** `init-detect.mjs` (confidence-scored detection), `annotatedReviewScreen` (tui.mjs:835-1170), `init-enrich-api.mjs` (BYO-key AI enrichment), `registry.mjs`, and `bin/hooks/session-start.mjs` all exist with tests — and none are reachable. `forgedock.mjs`'s `init()` uses its own inline template; `install()` never registers the SessionStart hook; `enable`/`disable`/`status` don't exist.
12
+ 2. **The funnel lies.** README advertises `npx forgedock demo` (dead — `Unknown command`), `integrate` (dead), and a CLAUDE.md injection that never happens. `getting-started.md` sends users to `/forgedock-init`, which hard-errors without a forge.yaml. `CONFIG.md` documents `--manual`/`--verbose` flags that parse nowhere. `plugin.json` says 1.0.1 while `package.json` says 1.0.8.
13
+
14
+ Beyond correctness, the shipped experience is plain warnings-and-homework. The goal is an install that feels revolutionary.
15
+
16
+ ## Decisions (locked with user)
17
+
18
+ | Decision | Choice |
19
+ | --- | --- |
20
+ | TUI stack | Zero-dependency, hand-rolled — extend existing `tui.mjs`, no frameworks |
21
+ | Scope | Full onboarding arc: preflight → install → detect → review → celebrate, plus SessionStart hook wiring and `enable`/`disable`/`status`. `forgedock demo` is **out of scope** (own milestone) |
22
+ | Motion | Full cinematic, **always** — no first-run-only fast path; every command plays its full choreography. Automatic degradation (non-TTY/CI/`NO_COLOR`) and explicit escape hatches (`--fast`, `FORGE_NO_MOTION=1`). Note: an already-configured bare invocation shows the status screen (per approved storyboard) — that screen has its own full choreography; it doesn't replay the install acts |
23
+ | Visual identity | **Molten Forge**: fire-orange → amber gradients, "forging" language |
24
+ | Logo treatment | **Chrome & Ember**: block-art F mark keeps the official metallic chrome/champagne gradient; ember lives in the wordmark and UI. Hero mark on install; compact 4-row lockup on all other commands |
25
+ | Flow shape | **One continuous journey**: bare `npx forgedock` plays the whole arc; `install`/`init` are aliases that jump into the relevant acts |
26
+ | Architecture | **Cinema layer + journey orchestrator** (new `bin/cinema.mjs` + `bin/journey.mjs`; `forgedock.mjs` becomes a thin router) |
27
+
28
+ ## The Experience — five acts, one keypress
29
+
30
+ The only interaction in the happy path is a single Enter at Act IV. Total added latency from theater: motion overlays real work wherever work exists; pure-theater beats (shimmer, quench flash) total ≈1.2s.
31
+
32
+ ### Act I — Ignition (~2.5s)
33
+
34
+ Chrome block-art mark materializes with a one-pass heat-shimmer sweep (~800ms); ember gradient wordmark `F O R G E D O C K` + version; rule line "lighting the forge". Preflight rows appear one-by-one, each live (`○` → braille spinner → `✔`):
35
+
36
+ - Node ≥18 (version shown)
37
+ - git present + repo detected
38
+ - Claude Code (`~/.claude` exists)
39
+ - GitHub CLI installed + authenticated (account shown)
40
+
41
+ A failed check renders an ember-bordered **fix card** with the exact copy-paste remedy (e.g. `winget install GitHub.cli`, `gh auth login`) and the journey **continues** with whatever still works. Preflight failures are advisory, not fatal — missing `gh` degrades detection confidence, it does not stop the forge.
42
+
43
+ ### Act II — Forging (real work)
44
+
45
+ - Progress bar with molten leading edge (`█▓▒░`) and per-command name ticker while symlinking the 24 commands into `~/.claude/commands` (existing logic, restyled).
46
+ - **Seam-closing (new):** merge a SessionStart hook entry into `~/.claude/settings.json` (idempotent read-modify-write; preserves existing hooks; removed on `uninstall`; malformed JSON → skip with fix card, never clobber). Write the registry entry marking the directory forge-managed.
47
+ - Settle lines: `✔ 24 slash commands linked`, `✔ SessionStart hook registered`, `✔ directory forge-managed`.
48
+
49
+ ### Act III — Reading your repository
50
+
51
+ Each detection field resolves as a live row: value + confidence badge + source, e.g.
52
+ `✔ owner/repo RapierCraftStudios/ForgeDock [high] git remote`.
53
+ Driven by the existing `detectConfig()` (init-detect.mjs). Fields: owner/repo, default branch, staging branch, project name, description, worktree base, project board (via `gh` when available). If `ANTHROPIC_API_KEY` is set, `init-enrich-api.mjs` fills review/verification sections (`✦ enriching with AI…`); if not, one quiet skip line. Spinners here cover *actual* latency (git/gh/API calls) — nothing is artificially slowed.
54
+
55
+ ### Act IV — The Review (the single interaction)
56
+
57
+ The existing `annotatedReviewScreen` (tui.mjs), wired in at last and restyled to ember: numbered field table with `[high]/[med]/[low]` badges, confidence legend, low-confidence warning. `Press Enter to forge, or a number to edit a field`. Low-confidence fields are written with `# TODO(forgedock:<field>)` comments instead of blocking. Existing forge.yaml → overwrite banner + backup to `forge.yaml.bak` before write (existing behavior preserved).
58
+
59
+ ### Act V — Forged (celebration)
60
+
61
+ Compact mark flashes white-hot once (quench, ~400ms), then settles:
62
+
63
+ - `Forged.` + **real elapsed time** (`install → config in 34s`) — proof, not hype
64
+ - Receipt lines: forge.yaml written (with TODO count), commands + hook active
65
+ - Boxed "what's next" card: `1. open claude in this repo` / `2. run /work-on next`
66
+ - Footer: docs link + star nudge
67
+
68
+ Next Claude Code session, the now-installed SessionStart hook greets the user with the command table — the experience continues past the terminal.
69
+
70
+ ### Re-runs and aliases
71
+
72
+ - Bare `npx forgedock` in an already-configured repo → short status screen (compact lockup, current config summary, hook state) + offer to review config. Not the full movie.
73
+ - `npx forgedock install` → Acts I–II (+ III–V only if unconfigured, matching today's auto-init).
74
+ - `npx forgedock init` → Acts III–V.
75
+ - `enable` / `disable` / `status` → registry operations, compact lockup, instant.
76
+ - `update` / `uninstall` → existing logic, compact restyle; `uninstall` also removes the hook entry and registry entries.
77
+
78
+ ## Architecture
79
+
80
+ ### `bin/cinema.mjs` (new — animation engine)
81
+
82
+ Kept separate from tui.mjs (already 1,171 lines). Exports:
83
+
84
+ - Truecolor per-cell gradient rendering with 256-color fallback (`COLORTERM`/`TERM` detection); gradient text lines and gradient block art
85
+ - `renderMark(size)` — the chrome F mark, hero (8-row) and compact (4-row); block-art geometry approved v1 from mockups, may be pixel-tuned during implementation
86
+ - `shimmer(lines, opts)` — one-pass highlight sweep
87
+ - `reveal(rows, opts)` — staged live rows (`○` → spinner → `✔`/`✖`/fix card)
88
+ - `progressBar` molten variant with ticker
89
+ - `motionEnabled()` — the single motion gate: TTY ∧ ¬`NO_COLOR` ∧ ¬`FORGE_NO_MOTION` ∧ ¬`--fast` ∧ ¬CI
90
+
91
+ **No alternate screen buffer.** Animations redraw in place (cursor-up) only while active, then settle to static lines — scrollback keeps a complete plain receipt of the run.
92
+
93
+ ### `bin/journey.mjs` (new — choreographer)
94
+
95
+ Five act functions — `preflight()`, `forge()`, `read()`, `review()`, `celebrate()` — over a shared context object; each act independently callable (for aliases) and unit-testable. Orchestrates the existing modules: `init-detect.mjs`, `init-enrich-api.mjs`, `annotatedReviewScreen`, `registry.mjs`. The forge.yaml writer moves here, driven from the detection `ConfigDraft` (single source of truth; deletes the duplicate inline template in forgedock.mjs) and emits `# TODO(forgedock:<field>)` for low-confidence values.
96
+
97
+ ### `bin/forgedock.mjs` (shrinks to router)
98
+
99
+ Command dispatch → journey acts as above. Fixes: `HOME` check gains `USERPROFILE`/`os.homedir()` fallback (currently hard-fails on bare Windows shells); flag parsing added (`--fast`, `--manual`, `--verbose`). `--manual` = plain-prompt path (no detection auto-accept), making CONFIG.md's documented flags real.
100
+
101
+ ### Hook wiring (the seam)
102
+
103
+ `forge()` merges into `~/.claude/settings.json`:
104
+
105
+ ```json
106
+ { "hooks": { "SessionStart": [ { "hooks": [ { "type": "command",
107
+ "command": "node \"$FORGE_HOME/bin/hooks/session-start.mjs\"" } ] } ] } }
108
+ ```
109
+
110
+ The command path is resolved to the absolute installed package location at install time (the same resolution `install()` already performs for symlink sources) — written as an absolute path, not a literal env reference, so the hook works regardless of shell profile.
111
+
112
+ Idempotent (detects its own entry, never duplicates), preserves unrelated hooks, guards malformed JSON (skip + fix card, never overwrite), and is removed by `uninstall`. This makes the existing `session-start.mjs` (managed-active greeting / missing-config nudge / one-time unmanaged nudge) actually fire.
113
+
114
+ ## Degradation ladder
115
+
116
+ 1. Full cinematic — interactive TTY, truecolor
117
+ 2. 256-color — same choreography, quantized gradients
118
+ 3. `NO_COLOR` — monochrome, motion intact
119
+ 4. `--fast` / `FORGE_NO_MOTION=1` — instant renders, no animation frames
120
+ 5. Non-TTY/CI — deterministic plain sequential log, same information, zero ANSI animation; review screen auto-accepts nothing — in non-TTY, detection results are written with TODO flags and the run says so (preserves current "never block CI" behavior while still protecting existing configs via the existing non-interactive abort on overwrite)
121
+ 6. Narrow terminals (<60 cols) — compact lockup replaces hero mark; tables wrap to stacked rows
122
+
123
+ ## Error handling
124
+
125
+ - **Never a dead stop, never a lie.** Preflight fix cards + continue; degraded steps stated inline at the moment they happen and echoed in the Act V receipt (e.g. `2 fields flagged # TODO`). No silently skipped sections.
126
+ - **Ctrl-C per act:** restore cursor/style, print partial-state summary (`commands installed, config not written — run npx forgedock init to finish`), exit cleanly. No half-drawn screens, no orphaned temp files.
127
+ - **Filesystem safety unchanged or better:** forge.yaml backup before overwrite; settings.json read-modify-write with parse guard; registry writes stay atomic (existing).
128
+
129
+ ## Testing
130
+
131
+ Extends the existing `node --test` suite in `bin/tests/`:
132
+
133
+ - **Acts:** unit tests with injected exec/IO stubs — preflight verdict matrix, hook-merge idempotency (twice → one entry), unrelated-hook preservation, TODO-flag emission, ConfigDraft→YAML writer output.
134
+ - **Degradation:** snapshot tests of the non-TTY plain log (`FORGE_NO_MOTION=1`, piped stdout) — deterministic byte-for-byte.
135
+ - **Cinema:** frame-function tests (gradient math, color-mode fallback selection), not timing tests — motion verified by eye; everything beneath it deterministic.
136
+ - **Windows first-class:** path handling, `USERPROFILE` fallback, symlink-vs-copy behavior — primary dev platform is Windows.
137
+
138
+ ## Docs truth-pass (ships in this milestone)
139
+
140
+ - **README:** remove `demo` (deferred) and `integrate`; replace CLAUDE.md-injection claim with the (now true) hook story; collapse the two-step install to one `npx forgedock`; requirements list gains Node ≥18.
141
+ - **getting-started.md:** fix the `/forgedock-init`-without-forge.yaml dead end; document the journey.
142
+ - **CONFIG.md:** `--manual`/`--verbose` now real; describe actual flags only.
143
+ - **Versioning:** reconcile `plugin.json` (1.0.1) with `package.json` (1.0.8); ship as **1.1.0**.
144
+ - **demo.tape:** re-record after implementation so the README gif shows the real flow.
145
+
146
+ ## Out of scope
147
+
148
+ - `npx forgedock demo` (sandbox pipeline showcase) — its own future milestone
149
+ - Self-healing `validate` (spec 2026-06-09 Pillar C, AI-fix loop) — future
150
+ - Skill-backend enrichment inside Claude Code — future; API backend + deterministic baseline only for now
151
+
152
+ ## Success criteria
153
+
154
+ - Fresh `npx forgedock` on a clean machine: zero commands to remember, one Enter, ends with a valid forge.yaml, installed commands, live SessionStart hook, and next-step card — in under ~60s on a normal repo.
155
+ - Re-run is a status screen, not a movie sit-through beyond its own short choreography.
156
+ - CI/non-TTY runs produce a stable plain log and never hang on a prompt.
157
+ - No user-facing doc advertises a command or behavior that doesn't exist.
@@ -0,0 +1,194 @@
1
+ # Durable Execution Engine — Design Spec
2
+
3
+ **Date:** 2026-07-03
4
+ **Status:** Implemented (headless engine core) on branch feat/durable-execution-engine — see docs/superpowers/plans/2026-07-03-durable-execution-engine.md
5
+ **Tracking:** #1256 (keystone), epic #1320 (five foundations of autonomy)
6
+ **Scope of this spec:** the durable engine **core** only — phase state machine, run-log, resume, and orchestrator integration for **headless / orchestrated** runs. Verification, learning, economics, and provenance (#1315–#1319) are separate specs that build on this substrate.
7
+
8
+ ---
9
+
10
+ ## 1. Motivation
11
+
12
+ The pipeline today is a **prose-driven state machine**. `work-on.md` describes a linear walk (Phase 0→7) whose only continuation mechanism is imperative text ("do NOT stop"), and its subcommands reference a numbered state router that `work-on.md` never implements (#1308). Phase transitions therefore depend on an LLM *choosing* not to emit `end_turn` at a Skill-return boundary. This produces the recurring failure classes:
13
+
14
+ - **review→close stalls** (~half of runs merge the PR but leave the issue open).
15
+ - **quality-gate→3H stalls.**
16
+ - **"Prompt is too long" deaths** mid-run (#1305), patched with a self-counted "≥20 Skill calls" heuristic.
17
+ - **Lossy state** — GitHub prose comments are the de-facto database; resume re-parses them.
18
+ - **Blind observability** — progress is inferred from JSONL output files, which are 0-byte on Windows (#1307).
19
+
20
+ The insight that makes a fix cheap: **`bin/runner.mjs` already owns the Anthropic tool-use loop** (issue #1151). ForgeDock is not locked inside Claude Code's opaque agent loop — it drives its own loop. So the work is not "adopt a runtime from scratch," it is "make the loop we already own durable."
21
+
22
+ ## 2. Goals / Non-goals
23
+
24
+ **Goals**
25
+ - Phase transitions are decided by **deterministic code**, never by an LLM's choice to continue.
26
+ - A run survives crash / kill / compaction / rate-limit and **resumes from the last committed phase** in a fresh context.
27
+ - State is **machine-readable and shared**, so any machine or the orchestrator can resume and observe a run.
28
+ - Eliminate the stall and "prompt too long" classes structurally, not with prose.
29
+
30
+ **Non-goals (explicitly out; each is its own spec / increment)**
31
+ - The interactive Claude Code `/work-on` path (a later increment adopts the same core via hooks).
32
+ - Per-turn / mid-phase durability (this spec is **per-phase** granularity).
33
+ - The verification gate, learning memory, economic governance, provenance (#1315–#1319).
34
+ - Full per-command parity for the standalone runtime (#1151 follow-up).
35
+
36
+ ## 3. Locked decisions
37
+
38
+ These were settled during design and are not open:
39
+
40
+ 1. **Durability granularity: per-phase.** The engine checkpoints each pipeline *phase*. A mid-phase crash re-runs that one phase (idempotently). Per-turn durability is rejected as over-complex (partial file edits / half-made commits).
41
+ 2. **State authority: hybrid (local hot + GitHub mirror), GitHub wins on divergence.** A local append-only run-log is the crash-safe hot path; a structured `FORGE:STATE` block on the issue is the durable shared mirror. On conflict the GitHub mirror is authoritative and invalidates the local cache.
42
+ 3. **First surface: headless / orchestrated.** The engine drives runs through `runner.mjs` — where autonomy matters most and there is no human to un-stick a stall. Interactive Claude Code adopts the same core later.
43
+ 4. **`FORGE:STATE` is a compact index, not self-describing.** It carries the resume pointer (phase, `committed[]`, branch, pr, version, lease) — **not** the rich per-phase outputs. Rich outputs live in the existing `FORGE:INVESTIGATOR` / `ARCHITECT` / etc. structured comments (which the engine writes anyway). A fresh-clone resume reads the index for *where*, then reads those comments for *content*. This avoids duplicating data and preserves human-readable artifacts.
44
+
45
+ ## 4. Architecture
46
+
47
+ Five components, each with one responsibility and a clean seam so the interactive path can later slot in as a second execution adapter.
48
+
49
+ | Component | File | Responsibility |
50
+ |---|---|---|
51
+ | **Phase state machine** | `bin/engine/phases.mjs` | Declarative table encoding the canonical phase sequence (today's prose in `work-on.md`). Each entry: `id`, `entryCondition(state)`, `command` (spec to run), `terminal?`, `reconcile(state)`. |
52
+ | **Engine loop** | `bin/engine.mjs` | The durable driver. Loads state, picks the next phase deterministically, invokes the phase, commits, handles failure. |
53
+ | **LLM-execution adapter** | `bin/runner.mjs` *(existing)* | Runs one phase = one bounded `runCommand()` with a **fresh context window**. Loop unchanged; new caller. |
54
+ | **Run-log store** | `bin/engine/runlog.mjs` | Append-only `.forge/runs/{issue}.jsonl`. Crash-safe local hot path. |
55
+ | **GitHub adapter (projector)** | `bin/engine/projector.mjs` | Reads/writes the `FORGE:STATE` block; manages the lease. Durable shared mirror + data source for `forgedock watch` (#1312). |
56
+
57
+ The engine core (`phases`, `engine`, `runlog`, `projector`) is runtime-agnostic; `runner.mjs` is the LLM adapter; the projector is the GitHub adapter.
58
+
59
+ ### Engine loop (shape)
60
+
61
+ ```
62
+ state = load(issue) // local log → else FORGE:STATE → else fresh
63
+ while (next = pickPhase(state)) { // ENGINE decides; never the LLM
64
+ if (next.terminal) break
65
+ reconcile(state, next) // make re-entry safe (idempotency)
66
+ result = runner.runCommand(next.command, ctx(state)) // fresh context per phase
67
+ switch (result.status) {
68
+ case 'committed': commit(state, next, result.outputs); break // append log + mirror
69
+ case 'failed': if (attempts(next) < maxAttempts) retry; else block(state, next); break
70
+ case 'blocked': terminate(state, 'needs-human', result.detail); return
71
+ }
72
+ } // no event on crash → next run drops + re-runs
73
+ ```
74
+
75
+ ## 5. State model
76
+
77
+ ### 5.1 Run-log (`.forge/runs/{issue}.jsonl`)
78
+
79
+ Append-only, one JSON event per line. `seq` is a monotonic per-issue version counter.
80
+
81
+ ```jsonc
82
+ {"seq":1,"event":"RUN_START","issue":42,"run":"r_a1","lane":"staging","baseSha":"9ea38…"}
83
+ {"seq":2,"event":"PHASE_START","phase":"investigate"}
84
+ {"seq":3,"event":"PHASE_COMMIT","phase":"investigate","outputs":{"verdict":"CONFIRMED","decompose":false}}
85
+ {"seq":4,"event":"PHASE_START","phase":"build"}
86
+ {"seq":5,"event":"PHASE_FAILED","phase":"build","attempt":1,"reason":"tsc error"}
87
+ {"seq":6,"event":"PHASE_START","phase":"build"}
88
+ {"seq":7,"event":"PHASE_COMMIT","phase":"build","outputs":{"branch":"fix/pay-42","headSha":"…","skipped":false}}
89
+ {"seq":8,"event":"RUN_TERMINAL","reason":"merged"}
90
+ ```
91
+
92
+ Events: `RUN_START`, `PHASE_START`, `PHASE_COMMIT`, `PHASE_FAILED`, `RUN_TERMINAL`.
93
+
94
+ **The commit rule (the entire durability story):** a phase is *committed* iff a `PHASE_COMMIT` follows its latest `PHASE_START`. A `PHASE_START` with no following `PHASE_COMMIT` = a crashed / in-flight phase → **dropped and re-run**.
95
+
96
+ **Corruption tolerance:** a crash mid-write leaves a truncated final line; the reader ignores any final line that does not parse as complete JSON.
97
+
98
+ ### 5.2 GitHub mirror (`FORGE:STATE`)
99
+
100
+ One machine-readable HTML-comment block the projector **overwrites in place** (not appends) after each `PHASE_COMMIT`:
101
+
102
+ ```html
103
+ <!-- FORGE:STATE
104
+ {"v":7,"run":"r_a1","phase":"review","committed":["investigate","context","architect","build"],
105
+ "branch":"fix/pay-42","pr":null,"lane":"staging","terminal":false,
106
+ "lease":{"by":"agent_7","until":"2026-07-03T04:10Z"}}
107
+ -->
108
+ ```
109
+
110
+ `v` mirrors the run-log `seq` at last commit — the shared version used for divergence detection. It carries the **compact index only** (see locked decision 4).
111
+
112
+ ### 5.3 Resume + divergence (`GitHub wins`)
113
+
114
+ ```
115
+ onStart(issue):
116
+ local = readRunLog(issue) // absent on fresh clone / other machine
117
+ remote = projector.read(issue) // FORGE:STATE, may be absent
118
+ state = reconcile(local, remote)
119
+
120
+ reconcile(local, remote):
121
+ if !remote: return local ?? fresh
122
+ if !local: return hydrateFromRemote(remote) // fresh clone / other machine
123
+ if remote.v > local.lastSeq: return hydrateFromRemote(remote) // advanced elsewhere → GitHub wins, discard local
124
+ if remote.v < local.lastSeq: projector.write(fromLocal(local)); return local // we crashed pre-mirror → re-mirror, keep local
125
+ return local // in sync
126
+ ```
127
+
128
+ `hydrateFromRemote` reconstructs working state from the compact index plus, where a phase's rich outputs are needed downstream, the existing `FORGE:*` structured comments on the issue.
129
+
130
+ ## 6. Deterministic transitions & idempotency
131
+
132
+ **`pickPhase(state)`** = the first phase in the `phases.mjs` table whose `entryCondition(state)` holds and whose `id` is not in `state.committed`. If none qualify or a terminal condition holds → terminal. No LLM involvement — this is the fix for the stall class.
133
+
134
+ **`phase.reconcile(state)`** runs *before* the LLM on every entry, because a dropped phase re-runs and must be safe to re-enter:
135
+
136
+ - `build.reconcile`: branch already exists and is ahead of base → resume rather than re-create.
137
+ - `review.reconcile`: a PR already exists for the branch → adopt it, do not open a second.
138
+ - `close.reconcile`: PR already merged → proceed straight to close steps.
139
+
140
+ These leverage the existing `FORGE:*` markers and `gh` queries; the engine makes idempotency a required step rather than an ad-hoc convention.
141
+
142
+ ## 7. Failure policy
143
+
144
+ Each phase returns a typed result; the engine acts on the **type** (replacing prose "try again / do not stop"):
145
+
146
+ | Result | Meaning | Engine action |
147
+ |---|---|---|
148
+ | `committed` | phase produced outputs | write `PHASE_COMMIT`, advance |
149
+ | `failed` | transient (rate-limit, network, `gh` timeout, context-budget hit mid-phase) | `PHASE_FAILED`; retry same phase with backoff up to `maxAttempts` (default 3); on exhaustion → treat as `blocked` |
150
+ | `blocked` | deterministic human-needed (gate failed after loop, contract wrong, ancestry violation, push rejected) | `RUN_TERMINAL{needs-human}`, set label via projector, **stop visibly** |
151
+ | *(no event)* | crash / kill / compaction | next run drops the uncommitted `PHASE_START` and re-runs the phase |
152
+
153
+ **Visible skips (closes #1306):** a phase that no-ops (e.g. missing `verification.commands`) must emit `outputs:{skipped:true, which:"typecheck"}` into its `PHASE_COMMIT` and `FORGE:STATE`. A skip is surfaced (PR body / trajectory / review verdict), never a silent pass.
154
+
155
+ **"Prompt too long" (dissolves #1305):** fresh context per phase removes the dominant cause. If a *single* phase still exhausts context mid-run, it is a `failed` attempt whose `reconcile` resumes from committed artifacts on retry. Note honestly: an oversized single-phase input (e.g. a 10k-line review diff) remains the review phase's own concern — the engine bounds it (`runner` tool-result caps + iteration budget) but does not shrink it; that is the verification/review spec's responsibility.
156
+
157
+ ## 8. Concurrency & orchestrator integration
158
+
159
+ **Lease.** `FORGE:STATE.lease = {by: agentId, until: ts}` is written on `RUN_START`, renewed on each `PHASE_COMMIT`, released on `RUN_TERMINAL`. On start, if a live lease held by another agent exists, the caller defers. This is best-effort mutual exclusion (checked on start; not an atomic CAS) — GitHub issue-edit has no compare-and-swap, so a concurrent start can still race between the lease read and the next write. It replaces the orchestrator's fuzzy "already in progress" label heuristic with a much tighter, race-*reducing* primitive.
160
+
161
+ **`/orchestrate` becomes a thin driver:**
162
+ - Decomposes into the wave/DAG (unchanged), then calls `engine.run(issue)` per issue.
163
+ - Watches `FORGE:STATE` per issue for progress — which also feeds `forgedock watch` (#1312) and removes any dependence on JSONL output files (killing the Windows-blind audit problem #1307 for orchestrated runs).
164
+ - **Exact stall detection:** a lease whose `until` expired on a non-terminal state = a dead agent → re-spawn `engine.run(issue)`, which resumes from the last `PHASE_COMMIT`. This replaces "resume up to 3× with nagging."
165
+ - File-overlap serialization remains the orchestrator's responsibility (unchanged), aided by the engine's `baseSha`/branch tracking.
166
+
167
+ ## 9. Testing strategy
168
+
169
+ Correctness of a durable engine is won in tests. `runner.mjs` already lazy-imports the SDK and separates pure helpers, so the engine is tested with a **fake `runCommand`** that returns scripted phase outputs — no live LLM.
170
+
171
+ - **State-machine units** (`bin/tests/phases.test.mjs`, `engine.test.mjs`): table-driven `pickPhase` over crafted states; `reconcile` across all divergence cases (local-only, remote-only, remote-ahead, local-ahead, in-sync); the commit-detection rule.
172
+ - **Crash-injection harness** (the durability proof): run the engine against the fake runner, kill after each phase boundary *and* mid-phase, assert resume lands on the correct phase with no double-PR / double-commit.
173
+ - **Run-log** (`runlog.test.mjs`): append/read/replay; truncated-final-line tolerance.
174
+ - **Projector** (`projector.test.mjs`): `FORGE:STATE` serialize/parse round-trip; overwrite-in-place; lease acquire / renew / expire against a mocked `gh`.
175
+ - **Per-phase `reconcile`** idempotency tests (build resumes, review adopts existing PR, close handles already-merged).
176
+
177
+ ## 10. Open implementation-time decision (defer to the plan)
178
+
179
+ Whether the engine runs concurrent issues as **separate processes** (matches today's background-agent model; strong isolation) or an **in-process worker pool** (one place to observe; shared rate-limit backpressure). Recommendation: **in-process pool + a git worktree per issue** for filesystem isolation (worktrees are already the pattern). Decide during planning.
180
+
181
+ ## 11. Relationship to existing code & the epic
182
+
183
+ - **Reuses:** `runner.mjs` (`runCommand`, tool handlers, result caps, lazy SDK) as the unmodified LLM adapter; the `work-on` phase sequence as the seed for `phases.mjs`; the existing `FORGE:*` structured comments as rich-output storage; git worktrees for isolation.
184
+ - **Retires / fixes:** the phantom numbered-state router (#1308), the "≥20 Skill calls" heuristic and "prompt too long" deaths (#1305), silent verification skips (#1306), and — for orchestrated runs — the JSONL-based audit blindness (#1307).
185
+ - **Enables (as substrate, not in scope):** the run-log's structured `outputs` and `FORGE:STATE` are deliberately the foundation for verification (#1315), learning memory (#1316), economics (#1317), and provenance (#1318).
186
+
187
+ ## 12. Definition of done
188
+
189
+ - A headless `engine.run(issue)` drives an issue through the full phase sequence with the engine deciding every transition.
190
+ - Killing the process at any phase boundary or mid-phase resumes correctly on the next run, with no duplicated PRs/commits (crash-injection suite green).
191
+ - A fresh clone / second machine resumes a run from `FORGE:STATE` alone.
192
+ - `/orchestrate` drives issues via `engine.run`, coordinates via the lease, and detects/recovers stalls via lease expiry.
193
+ - Skipped verification is visible on the PR; a `blocked` result stops the run with `needs-human`, never silently.
194
+ - Unit + crash-injection + projector/lease test suites pass with a mocked runner and mocked `gh`.
@@ -0,0 +1,113 @@
1
+ # ForgeDock Demo — A Risk-Free First Pipeline Run
2
+
3
+ A tiny **Notes API** (~300 lines, zero dependencies) with five pre-written
4
+ issues. It exists so you can watch ForgeDock investigate, build, review, and
5
+ merge a change **without touching your real codebase**.
6
+
7
+ > Clone → `npx forgedock` → open Claude Code → `/work-on 1` → watch it go.
8
+
9
+ ---
10
+
11
+ ## Why this exists
12
+
13
+ Trying ForgeDock on your own project means trusting an AI pipeline with real
14
+ code on your very first run. This demo removes that risk: it's a self-contained
15
+ sandbox where every issue is small, fast, and safe to break.
16
+
17
+ The API has a couple of **intentional, clearly-labelled flaws** (a missing auth
18
+ check, an unsafe query path) so that ForgeDock's `/review-pr` agents have real
19
+ findings to surface — that's the part that makes the review feel impressive.
20
+
21
+ ---
22
+
23
+ ## What's inside
24
+
25
+ ```
26
+ forgedock-demo/
27
+ ├── src/
28
+ │ ├── server.js # built-in http server + router (no Express)
29
+ │ ├── router.js # (added by Issue #3)
30
+ │ ├── routes/notes.js # CRUD handlers — has the intentional flaws
31
+ │ ├── db.js # in-memory store
32
+ │ └── auth.js # bearer-token check
33
+ ├── scripts/smoke.js # dependency-free smoke test (npm run smoke)
34
+ ├── issues/ # the 5 pre-written issue specs
35
+ ├── forge.yaml # minimal working ForgeDock config
36
+ ├── labels.json # workflow + priority labels
37
+ └── bootstrap.sh # one command to stand up the live repo
38
+ ```
39
+
40
+ No database. No Docker. No API keys. Runs on Node 18+.
41
+
42
+ ---
43
+
44
+ ## Run it locally (optional)
45
+
46
+ ```bash
47
+ node src/server.js # starts on http://localhost:3000
48
+ # in another terminal:
49
+ curl http://localhost:3000/notes
50
+ npm run smoke # runs the smoke test
51
+ ```
52
+
53
+ ---
54
+
55
+ ## The five demo issues
56
+
57
+ Each issue targets a different pipeline strength and is scoped to finish fast.
58
+
59
+ | # | Type | Title | What it shows |
60
+ |---|------|-------|---------------|
61
+ | 1 | Bug / security | DELETE is missing an auth check | Simplest fix — fastest run |
62
+ | 2 | Feature / security | Safe filtering for `GET /notes` | Investigation + architecture |
63
+ | 3 | Refactor | Extract the router module | "No behavior change" review |
64
+ | 4 | Performance | O(1) `findById` | Investigation phase tracing the hot path |
65
+ | 5 | Docs | Add an API reference | Pipeline handles non-code work |
66
+
67
+ Full specs live in [`issues/`](./issues). When you run `bootstrap.sh`, these
68
+ become real GitHub issues #1–#5 in your demo repo, in this order.
69
+
70
+ ---
71
+
72
+ ## Stand up your own live demo repo
73
+
74
+ The scaffold is complete and ready — the only step that needs your GitHub
75
+ credentials is creating the actual repo. One command does it:
76
+
77
+ ```bash
78
+ # requires an authenticated gh CLI (gh auth login)
79
+ ./bootstrap.sh # creates <your-user>/forgedock-demo (public)
80
+ # or:
81
+ ./bootstrap.sh my-org/forgedock-demo # custom owner/repo
82
+ ```
83
+
84
+ `bootstrap.sh` will:
85
+
86
+ 1. Create the GitHub repo (skips if it already exists).
87
+ 2. Push this codebase to `main`.
88
+ 3. Create the workflow + priority labels (via `npx forgedock labels setup`,
89
+ falling back to `labels.json`).
90
+ 4. File the five issues from [`issues/`](./issues).
91
+
92
+ Then:
93
+
94
+ ```bash
95
+ git clone https://github.com/<you>/forgedock-demo.git
96
+ cd forgedock-demo
97
+ npx forgedock # install the ForgeDock commands
98
+ # open Claude Code in this directory, then run:
99
+ /work-on 1
100
+ ```
101
+
102
+ Watch ForgeDock investigate the missing-auth bug, write the fix, run the quality
103
+ gate, open a PR, review it, and merge — all on a repo you can safely throw away.
104
+
105
+ ---
106
+
107
+ ## After your first run
108
+
109
+ Try `/work-on 2` through `/work-on 5` to see the other pipeline phases, or run
110
+ `/orchestrate` to let ForgeDock work several issues in parallel.
111
+
112
+ When you're comfortable, point ForgeDock at your real project — you've already
113
+ seen exactly what it does.
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # bootstrap.sh — stand up a live ForgeDock demo repo from this scaffold.
4
+ #
5
+ # This is the ONE step that needs a human: it creates a real GitHub repo (which
6
+ # requires your credentials), pushes this code, sets up the workflow labels, and
7
+ # files the five demo issues. Everything else in the scaffold is ready to go.
8
+ #
9
+ # Usage:
10
+ # ./bootstrap.sh # creates <your-user>/forgedock-demo (public)
11
+ # ./bootstrap.sh my-org/forgedock-demo # custom owner/repo
12
+ # ./bootstrap.sh my-org/forgedock-demo --private
13
+ #
14
+ # Requirements: an authenticated `gh` CLI (`gh auth status`) and `git`.
15
+
16
+ set -euo pipefail
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
+ cd "$SCRIPT_DIR"
20
+
21
+ # ---- args -------------------------------------------------------------------
22
+ REPO_SLUG="${1:-}"
23
+ VISIBILITY="--public"
24
+ for arg in "$@"; do
25
+ case "$arg" in
26
+ --private) VISIBILITY="--private" ;;
27
+ --public) VISIBILITY="--public" ;;
28
+ esac
29
+ done
30
+
31
+ if [ -z "$REPO_SLUG" ] || [[ "$REPO_SLUG" == --* ]]; then
32
+ GH_USER="$(gh api user --jq '.login')"
33
+ REPO_SLUG="${GH_USER}/forgedock-demo"
34
+ fi
35
+
36
+ echo "==> Target repo: $REPO_SLUG ($VISIBILITY)"
37
+
38
+ command -v gh >/dev/null || { echo "ERROR: gh CLI not found"; exit 1; }
39
+ gh auth status >/dev/null || { echo "ERROR: run 'gh auth login' first"; exit 1; }
40
+
41
+ # ---- 1. create the repo -----------------------------------------------------
42
+ REPO_CREATED=false
43
+ if gh repo view "$REPO_SLUG" >/dev/null 2>&1; then
44
+ echo "==> Repo already exists, skipping creation."
45
+ else
46
+ echo "==> Creating repo..."
47
+ gh repo create "$REPO_SLUG" $VISIBILITY \
48
+ --description "Try ForgeDock risk-free — a tiny Notes API with pre-written issues." \
49
+ --disable-wiki
50
+ REPO_CREATED=true
51
+ fi
52
+
53
+ # ---- 2. push the scaffold ---------------------------------------------------
54
+ TMP_DIR="$(mktemp -d)"
55
+ echo "==> Staging scaffold in $TMP_DIR"
56
+ # Copy everything except bootstrap.sh and the issues/ specs (issues become GH issues).
57
+ cp -r src scripts package.json forge.yaml labels.json README.md .gitignore "$TMP_DIR/" 2>/dev/null || true
58
+ # Ship the issue specs too, for reference inside the repo.
59
+ cp -r issues "$TMP_DIR/issues"
60
+
61
+ (
62
+ cd "$TMP_DIR"
63
+ git init -q
64
+ git checkout -q -b main
65
+ git add .
66
+ git -c user.name="forgedock-demo" -c user.email="demo@forgedock.dev" \
67
+ commit -q -m "chore: initial demo scaffold"
68
+ git remote add origin "https://github.com/${REPO_SLUG}.git"
69
+ if [ "$REPO_CREATED" = "true" ]; then
70
+ # Fresh repo we just created — safe to push.
71
+ git push -q -u origin main
72
+ else
73
+ # Repo pre-existed. Never force-push over someone's existing main; do a
74
+ # non-destructive push and let it fail loudly if the repo already has commits.
75
+ if ! git push -q -u origin main 2>/dev/null; then
76
+ echo "ERROR: '$REPO_SLUG' already has content on main. Refusing to overwrite it." >&2
77
+ echo " Use a fresh/empty repo, or push manually if you intend to replace it." >&2
78
+ exit 1
79
+ fi
80
+ fi
81
+ )
82
+ echo "==> Pushed scaffold to $REPO_SLUG"
83
+
84
+ # ---- 3. labels --------------------------------------------------------------
85
+ echo "==> Creating labels..."
86
+ # Prefer the ForgeDock CLI if available; otherwise create from labels.json.
87
+ if command -v npx >/dev/null && npx --no-install forgedock --help >/dev/null 2>&1; then
88
+ npx forgedock labels setup --repo "$REPO_SLUG" || true
89
+ else
90
+ node -e '
91
+ const fs = require("fs");
92
+ const labels = JSON.parse(fs.readFileSync("labels.json", "utf8"));
93
+ for (const l of labels) console.log([l.name, l.color, l.description].join("\t"));
94
+ ' labels.json | while IFS=$'\t' read -r name color desc; do
95
+ gh label create "$name" --color "$color" --description "$desc" --force -R "$REPO_SLUG" >/dev/null 2>&1 || true
96
+ done
97
+ fi
98
+
99
+ # ---- 4. issues --------------------------------------------------------------
100
+ echo "==> Creating issues..."
101
+ for f in issues/*.md; do
102
+ title="$(grep -m1 '^title:' "$f" | sed -E 's/^title:[[:space:]]*"?([^"]*)"?[[:space:]]*$/\1/')"
103
+ labels="$(grep -m1 '^labels:' "$f" | sed -E 's/^labels:[[:space:]]*\[(.*)\][[:space:]]*$/\1/' | tr -d '"' | tr -d ' ')"
104
+ # Body = everything after the closing front-matter '---'.
105
+ body="$(awk 'f{print} /^---[[:space:]]*$/{c++; if(c==2) f=1}' "$f")"
106
+ label_args=()
107
+ if [ -n "$labels" ]; then
108
+ IFS=',' read -ra parts <<< "$labels"
109
+ for l in "${parts[@]}"; do label_args+=(--label "$l"); done
110
+ fi
111
+ echo " - $title"
112
+ gh issue create -R "$REPO_SLUG" --title "$title" --body "$body" "${label_args[@]}" >/dev/null \
113
+ || gh issue create -R "$REPO_SLUG" --title "$title" --body "$body" >/dev/null
114
+ done
115
+
116
+ rm -rf "$TMP_DIR"
117
+
118
+ echo ""
119
+ echo "Done. Your demo repo is ready:"
120
+ echo " https://github.com/${REPO_SLUG}"
121
+ echo ""
122
+ echo "Next:"
123
+ echo " git clone https://github.com/${REPO_SLUG}.git && cd forgedock-demo"
124
+ echo " npx forgedock # install the commands"
125
+ echo " # open Claude Code here, then run: /work-on 1"
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: "fix(notes): DELETE /notes/:id is missing an auth check"
3
+ labels: ["bug", "security", "priority:P1"]
4
+ ---
5
+
6
+ ## Problem
7
+
8
+ `DELETE /notes/:id` lets **anyone** delete any note. The other mutating route
9
+ (`POST /notes`) requires a bearer token via `requireToken(req)`, but the delete
10
+ handler skips that check entirely. This is an authorization gap.
11
+
12
+ ## Steps to Reproduce
13
+
14
+ ```bash
15
+ # No token, yet it succeeds:
16
+ curl -X DELETE http://localhost:3000/notes/1
17
+ # => {"deleted":1}
18
+ ```
19
+
20
+ ## Affected Files
21
+
22
+ 1. `src/routes/notes.js` — `deleteNote()` is missing a `requireToken(req)` call.
23
+
24
+ ## Expected Behavior
25
+
26
+ `DELETE /notes/:id` returns `401 Unauthorized` when no valid bearer token is
27
+ supplied, matching the protection on `POST /notes`.
28
+
29
+ ## Acceptance Criteria
30
+
31
+ - [ ] `deleteNote()` calls `requireToken(req)` before deleting.
32
+ - [ ] Unauthenticated delete returns 401.
33
+ - [ ] Authenticated delete (`Authorization: Bearer demo-token`) still works.
34
+ - [ ] No regression in `npm run smoke`.
35
+
36
+ > Good first run: this is the simplest issue — expect the pipeline to finish in
37
+ > a couple of minutes.