forge-orkes 0.62.0 → 0.64.0

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 (56) hide show
  1. package/bin/create-forge.js +250 -99
  2. package/experimental/conventions/README.md +87 -0
  3. package/experimental/conventions/install.sh +179 -0
  4. package/experimental/conventions/source/rules/code-quality.md +17 -0
  5. package/experimental/conventions/source/rules/laravel-http.md +21 -0
  6. package/experimental/conventions/source/rules/laravel-jobs.md +19 -0
  7. package/experimental/conventions/source/rules/laravel-migrations.md +19 -0
  8. package/experimental/conventions/source/rules/laravel-services.md +21 -0
  9. package/experimental/conventions/source/rules/python.md +14 -0
  10. package/experimental/conventions/source/rules/testing.md +15 -0
  11. package/experimental/conventions/source/rules/vue-inertia.md +24 -0
  12. package/experimental/conventions/source/skills/laravel-conventions/SKILL.md +124 -0
  13. package/experimental/conventions/source/skills/python-conventions/SKILL.md +100 -0
  14. package/experimental/conventions/source/skills/testing-standards/SKILL.md +142 -0
  15. package/experimental/conventions/source/skills/vue-conventions/SKILL.md +92 -0
  16. package/experimental/conventions/uninstall.sh +97 -0
  17. package/experimental/m10/README.md +130 -0
  18. package/experimental/m10/install.sh +198 -0
  19. package/experimental/m10/source/hooks/forge-branch-guard.sh +61 -0
  20. package/experimental/m10/source/hooks/forge-claim-check-doctor.sh +77 -0
  21. package/experimental/m10/source/hooks/forge-claim-check.sh +113 -0
  22. package/experimental/m10/source/hooks/forge-session-id.sh +22 -0
  23. package/experimental/m10/source/mcp-server/README.md +74 -0
  24. package/experimental/m10/source/mcp-server/example.mcp.json +8 -0
  25. package/experimental/m10/source/mcp-server/index.js +460 -0
  26. package/experimental/m10/source/mcp-server/package.json +17 -0
  27. package/experimental/m10/source/skills/orchestrating/SKILL.md +223 -0
  28. package/experimental/m10/source/skills/orchestrating/bootstrap-checks.md +70 -0
  29. package/experimental/m10/uninstall.sh +69 -0
  30. package/package.json +3 -2
  31. package/template/.claude/hooks/forge-release-fold.sh +9 -1
  32. package/template/.claude/settings.json +1 -7
  33. package/template/.claude/skills/jarvis/SKILL.md +204 -0
  34. package/template/.claude/skills/upgrading/SKILL.md +105 -176
  35. package/template/.forge/FORGE.md +2 -2
  36. package/template/.forge/checks/forge-board-render.sh +11 -5
  37. package/template/.forge/checks/forge-jarvis-answer.sh +79 -0
  38. package/template/.forge/checks/forge-jarvis-awareness.sh +128 -0
  39. package/template/.forge/checks/forge-jarvis-bridge-clean.sh +130 -0
  40. package/template/.forge/checks/forge-jarvis-dispatch.sh +176 -0
  41. package/template/.forge/checks/forge-jarvis-instruments.sh +146 -0
  42. package/template/.forge/checks/forge-jarvis-notify-logonly.sh +40 -0
  43. package/template/.forge/checks/forge-jarvis-promote.sh +103 -0
  44. package/template/.forge/checks/forge-jarvis-prwatch.sh +92 -0
  45. package/template/.forge/checks/forge-jarvis-relay.sh +215 -0
  46. package/template/.forge/checks/forge-jarvis.sh +160 -0
  47. package/template/.forge/checks/tests/forge-jarvis-answer.test.sh +75 -0
  48. package/template/.forge/checks/tests/forge-jarvis-bridge-clean.test.sh +94 -0
  49. package/template/.forge/checks/tests/forge-jarvis-dispatch.test.sh +162 -0
  50. package/template/.forge/checks/tests/forge-jarvis-instruments.test.sh +64 -0
  51. package/template/.forge/checks/tests/forge-jarvis-promote.test.sh +102 -0
  52. package/template/.forge/checks/tests/forge-jarvis-prwatch.test.sh +66 -0
  53. package/template/.forge/checks/tests/forge-jarvis-relay.test.sh +135 -0
  54. package/template/.forge/checks/tests/forge-jarvis.test.sh +122 -0
  55. package/template/.forge/migrations/0.63.0-origin-first-upgrading.md +49 -0
  56. package/template/.forge/templates/project.yml +11 -0
@@ -1,231 +1,160 @@
1
1
  ---
2
2
  name: upgrading
3
- description: "Sync Forge framework files from a local dev repo or NPM. Use when developing Forge itself or applying updates to an installed project."
3
+ description: "Upgrade Forge framework files from the npm registry (origin-first default), or sync from a local dev repo in explicit dev mode. Use when applying updates to an installed project or developing Forge itself."
4
4
  ---
5
5
 
6
- # Upgrading: Local Dev Sync
6
+ # Upgrading: Origin-First Registry Upgrade
7
7
 
8
- Sync framework files from a local Forge source repo into the current project for testing without publishing to NPM.
8
+ The default upgrade path is the npm registry. This skill wraps the published bin (`forge-orkes`) as the **one sync engine** and layers the agent-only steps on top: the bin computes and applies the file sync; the skill renders the report and drives the consent-gated conversations. One computation, two renderings — never re-detect what the bin already reported. Local-clone sync survives only as explicit **Dev Mode** (`/upgrading dev`).
9
9
 
10
- For published upgrades, use `npx forge-orkes upgrade`.
10
+ ## Mode Gate (first)
11
11
 
12
- ## Step 1: Resolve Source Path
12
+ | Invocation | Mode |
13
+ |---|---|
14
+ | `/upgrading dev` | **Dev Mode** (below) — local-clone sync for developing Forge itself |
15
+ | `/upgrading X.Y.Z` | Default path with the engine pinned: `npx forge-orkes@{version} upgrade --yes --json` |
16
+ | `/upgrading` | Default path at `@latest` |
13
17
 
14
- Check if `.forge/dev-source` exists in the project root.
18
+ ## Default Path (registry)
15
19
 
16
- - **Exists:** Read the path (first line, trimmed). Confirm it contains `packages/create-forge/template/`.
17
- - **Missing:** Ask the user for the local Forge repo path, validate it has `packages/create-forge/template/`, save to `.forge/dev-source`.
20
+ ### 1. Preflight
18
21
 
19
- Template directory: `{source}/packages/create-forge/template/`.
22
+ - Read the installed version: `.claude/settings.json` → `forge.version` (absent → treat as `unknown`).
23
+ - `.forge/dev-source` present → emit exactly ONE notice line — *"dev mode available — `/upgrading dev`"* — and continue on the registry path. Presence of `.forge/dev-source` NEVER selects dev mode.
20
24
 
21
- ### Harvest upstream desire paths (advisory)
22
-
23
- `upgrading` is the one skill that bridges this project and a local Forge clone (`{source}`), so it harvests framework-scope desire paths the project has queued (ADR-012). After resolving `{source}`:
24
-
25
- 1. Glob this project's `.forge/state/desire-paths/upstream/*.md`. None → skip silently.
26
- 2. Any present → list them and offer to copy each into `{source}/docs/desire-paths-inbox/` (create the dir if missing) — a **tracked** location in the Forge repo where it won't be lost on machine swap.
27
- 3. On copy → move the project-side block to `.forge/state/desire-paths/upstream/harvested/` so it isn't re-offered. Report counts like other steps.
28
-
29
- Advisory — never blocks the sync. This is the **convenience tier**: the primary close is the GH issue offered by `forge` review at capture time. Harvest just catches anything written when `gh`/the repo URL wasn't available.
30
-
31
- ### Downgrade guard
32
-
33
- Read the source version (`{source}/packages/create-forge/package.json` `version`) and the installed version (`.claude/settings.json` `forge.version`). **If source < installed, STOP** — do not sync. Report: *"Refusing to downgrade: installed v{installed} is newer than source v{source}. Point dev-source at a newer checkout, or confirm an intentional downgrade."* Only proceed on explicit user override. (Rolling backward overwrites newer framework files and deletes files newer versions added — and with `.claude/` often gitignored, it is unrecoverable.)
34
-
35
- ## Step 2: File Classification
36
-
37
- | Category | Paths | Behavior |
38
- |----------|-------|----------|
39
- | **Framework-owned** | `.claude/agents/*.md`, `.claude/skills/*/SKILL.md`, `.forge/FORGE.md` | Overwrite |
40
- | **Import-managed** | `CLAUDE.md` | Never overwrite content; migrate legacy forge sections + guarantee the `@.forge/FORGE.md` import line (Step 5) |
41
- | **Merge-owned** | `.claude/settings.json` | Smart-merge `forge.*` keys only (Step 5) |
42
- | **Template-only** | `.forge/templates/**`, `.forge/migrations/**`, `.forge/gitignore` → `.forge/.gitignore` | Overwrite |
43
- | **Additive-sync** | `.claude/hooks/**` (incl. `tests/`), `.claude/rules/**`, `.forge/checks/**` (incl. `tests/`) | Add + overwrite template-shipped files; never delete local extras (Step 4a) |
44
-
45
- **Never touch** user-generated files: `.forge/project.yml`, `.forge/state/`, `.forge/constitution.md`, `.forge/context.md`, `.forge/requirements/`, `.forge/roadmap.yml`, `.forge/design-system.md`, `.forge/refactor-backlog.yml`.
46
-
47
- **Experimental skills — re-sync, don't blanket-skip.** Opt-in skills installed separately (e.g. `.claude/skills/orchestrating/` from `experimental/m10/`) are not in the base template, so they never appear in the Step 3 base sync. Earlier guidance was to leave them *untouched* — but that silently froze them at install-time content, so a base-template migration that changes behavior living only in an experimental skill became a no-op (the version stamp advanced and the migration doc shipped, yet the behavior never landed — e.g. the 0.28.0 repo-scoped worktree root). Step 3b now gives experimental skills their own **detect-and-offer-re-sync** pass against their `experimental/*/source/` origin. Never auto-overwrite and never flag them "removed from template" — but never leave staleness invisible either.
48
-
49
- ## Step 3: Sync Framework-Owned Files
50
-
51
- For each framework-owned file in the source template:
52
-
53
- 1. Read source and local content
54
- 2. Different → overwrite local, report **updated**
55
- 3. Same → report **unchanged**
56
- 4. Source has new file → copy, report **added**
57
- 5. Local has file not in source → it may be an **experimental** skill — defer the verdict to Step 3b, which distinguishes "experimental, has a known source" from "genuinely removed from template." Only skills with no experimental source are reported **removed from template** (don't delete -- let user decide).
58
-
59
- ## Step 3b: Re-sync Experimental Skills (detect + offer, never blanket-skip)
60
-
61
- Experimental skills are installed from `{source}/packages/create-forge/experimental/<pkg>/source/skills/<name>/`, not the base template — so Step 3 never sees them. Without this pass they freeze at install-time content forever (issue #5). This pass makes staleness **visible and one-click-fixable** while staying non-destructive.
62
-
63
- For each skill dir present in the project's `.claude/skills/` but **absent from the base template**:
64
-
65
- 1. Search the source for a matching experimental origin: `{source}/packages/create-forge/experimental/*/source/skills/<name>/`.
66
- 2. **No match** → genuinely local / removed-from-template. Report **removed from template** (don't delete) as before. Done.
67
- 3. **Match found** → it is an experimental skill with a known upstream. Diff each markdown file in the installed dir against the source dir (`SKILL.md` and any siblings, e.g. `bootstrap-checks.md`):
68
- - **All identical** → report `{name}: experimental, unchanged`.
69
- - **Differs** → report `{name}: experimental, STALE (pinned at install; source has changes)` and **offer** a re-sync with the same preview/confirm UX as base skills:
70
-
71
- ```
72
- {name} is an experimental skill (from experimental/{pkg}/) pinned at install time.
73
- The release ships newer content. Files that differ: {list}.
74
- Re-sync from experimental/{pkg}/source? (yes / show diff / no)
75
- ```
76
-
77
- - **yes** → overwrite the installed skill's markdown files from source; report **updated (experimental)**.
78
- - **show diff** → display the diff(s), then re-ask.
79
- - **no** → leave untouched; note in the report that it stays pinned.
25
+ ### 2. Run the engine
80
26
 
81
- **Default is non-destructive** — never overwrite an experimental skill without an explicit `yes`. **Scope:** this pass re-syncs the skill's **markdown** (the behavior-bearing prose). It does **not** touch the experimental package's hooks, MCP server, or `settings.json`/`.mcp.json` wiring — those are owned by the package's own installer. When a re-sync lands, add one line: *"Hooks / MCP server for {pkg} are not re-synced here — re-run `experimental/{pkg}/install.sh` if the release changed them."*
27
+ One Bash invocation:
82
28
 
83
- ## Step 4: Sync Template-Only Files
84
-
85
- Same process as Step 3 for `.forge/templates/**`, `.forge/migrations/**`, and `.forge/adapters/**`. (Matches the npm bin's template-only dirs — keeps migration guides installed so the Step 7 pointers resolve via the in-Claude sync route, not just `npx forge-orkes upgrade`. `.forge/adapters/**` ships the platform-adapter generation protocols, e.g. `codex-generation.md`, so the Step 4b Codex step can hand it to `quick-tasking`.)
86
-
87
- **Also sync the forge gitignore:** the source ships it as `.forge/gitignore` (npm strips files literally named `.gitignore` from a published tarball). Copy/refresh the source's `.forge/gitignore` into the project as `.forge/.gitignore` (overwrite — it is framework-owned). Report added/updated/unchanged like any template-only file.
88
-
89
- **Sync the framework prose file `.forge/FORGE.md`** (framework-owned single file — it holds the `# Forge` prose, formerly embedded in CLAUDE.md). Copy/refresh `{source}/packages/create-forge/template/.forge/FORGE.md` → `.forge/FORGE.md` (overwrite when different; report added/updated/unchanged). **Run this BEFORE the Step 5 CLAUDE.md pass** so a migrated import line never points at a missing file.
90
-
91
- ## Step 4a: Sync Hooks + Rules + Checks (additive — mirrors the npm bin)
29
+ ```bash
30
+ npx forge-orkes@latest upgrade --yes --json
31
+ # pinned: npx forge-orkes@{version} upgrade --yes --json
32
+ ```
92
33
 
93
- The template ships the safety-guardrail hook scripts (ADR-017), the always-on rules layer (ADR-018), and — since 0.58.2 — the merge-floor CI check scripts: `.claude/hooks/**` — **including the `tests/` subtree** — `.claude/rules/**`, and `.forge/checks/**` (incl. `tests/`). The npm bin has additively synced hooks + rules since those ADRs landed (`upgradeAdditiveDir`); this skill previously synced neither, so a project upgraded only via `/upgrading` never received `.claude/hooks/` at all while its settings.json wires guards like `protect-files.sh` and skills invoke `forge-release-fold.sh`, none of which existed on disk.
34
+ - stdout is exactly ONE JSON document; stderr is human proseignore it.
35
+ - `--yes` auto-confirms stale-framework-file removal ONLY; it never bypasses the downgrade guard.
36
+ - **Non-zero exit** → the bin refused or failed and still emitted `{error, reason}` on stdout. Surface the reason and **STOP**. The downgrade-guard refusal names `--force`; that stays a human decision — never re-run with `--force` yourself.
94
37
 
95
- For every file under `{source}/packages/create-forge/template/.claude/hooks/`, `.../.claude/rules/`, and `.../.forge/checks/` (recursive):
38
+ ### 3. Parse the JSON
96
39
 
97
- 1. Absent locally copy, report **added**.
98
- 2. Present but differs → overwrite, report **updated**.
99
- 3. Identical → report **unchanged**.
100
- 4. **Additive only** — never delete a local file the template lacks. Project-local and experimental hooks (e.g. M10 `forge-claim-check*.sh`) and pack-installed path-scoped rules are preserved; no "removed from template" verdicts in this pass.
101
- 5. `chmod +x` every synced `*.sh` (a copy can strip the execute bit; guard hooks and CI checks must be runnable).
40
+ Top-level keys (all always present; `schemaVersion: 1`): `installedVersion` (pre-stamp; `"unknown"` if unstamped), `packageVersion`, `packageRoot` (the bin's own package dir — an npx cache path, valid at emit time only), `results` (`{updated, added, unchanged, removed, preserved}`, repo-relative paths), `claudeMd` (`created|migrated|appended|unchanged`), `settings` (`updated|unchanged|missing`), `experimental` (`[{name, pkg, status: unchanged|stale, files}]` — detection only; the bin never rewrites), `migrations` (`[{version, guide, reason}]` — applying guides only, pre-filtered), `codexAdapterPresent`, `worktrees` (`[{path, branch}]` — live `forge/m-*`).
102
41
 
103
- The two subtrees differ in tracked-ness, and the sync respects it: `.forge/checks/**` is **committed** project content (CI must clone it; the hold check's protected set guards `.forge/checks/**`), while `.claude/hooks/**` is a **machine-local carve-out** in many projects (gitignored as per-developer files host-verified on ptnrkit). Sync both to disk identically; stage only what the project's own `.gitignore` admits — never `git add -f` a hook file past a project's ignore rules. The old `.claude/hooks/` copies of `forge-hold-check.sh` / `forge-check-lint.sh` (+ their two `tests/*.test.sh` suites) are relocation leftovers on upgraded projects — the migration guide `0.58.2-merge-floor-checks-relocation.md` covers removing them and re-pointing CI.
42
+ `schemaVersion` 1 warn (*"engine emitted schema {n}; this skill reads 1"*) and render best-effort — never silently misread.
104
43
 
105
- The settings.json *wiring* for these hooks is not written here — Step 5's smart-merge owns it.
44
+ ### 4. Render the report
106
45
 
107
- ## Step 4b: Codex Adapter (presence-gated detect + offer, never auto)
46
+ From the JSON onlyno re-scanning:
108
47
 
109
- Forge ships a **Codex/AGENTS adapter** (`.agents/skills/` + `.codex/` wiring) for projects run on Codex. It is a *derived translation* of the canonical `.claude/` skills (ADR-014) and drifts whenever core moves — but `upgrading` syncs only `.claude/`, so the Codex adapter would silently rot. This step makes that staleness visible and one-keystroke-fixable, **without** ever generating it here (only an agent-as-Codex can author it natively).
48
+ ```
49
+ Forge Upgrade Complete (registry)
50
+ ─────────────────────────────────
51
+ Source: npm registry — forge-orkes@{packageVersion}
52
+ Version: {installedVersion} → {packageVersion}
53
+
54
+ Updated / Added: listed from results
55
+ Experimental skills: {N} (name, pkg, status)
56
+ Codex adapter: present / not present (layer c updates this line)
57
+ Removed from template: {N} (removal confirmed via --yes)
58
+ CLAUDE.md: {claudeMd} (migrated/appended render as Migrated:/Restored:)
59
+ Settings: {settings}
60
+ Unchanged: {N} · Preserved: {N} (user-owned, never touched)
61
+ ```
110
62
 
111
- 1. **Presence gate.** Glob the project root for `.agents/` and `.codex/`. **Neither exists → skip silently** no prose, no prompt (Claude-only / greenfield projects are unaffected).
112
- 2. **Either exists → report + offer.** The Codex adapter is present and may be stale relative to the `.claude/` source just synced in Steps 3–4. Offer regeneration:
113
- ```
114
- Codex adapter detected (.agents/ / .codex/). It may be stale vs the .claude/
115
- source just synced. Regenerate it now? (yes / no)
116
- ```
117
- - **yes** → invoke `quick-tasking`, handing it `.forge/adapters/codex-generation.md` as the task definition (the **same delegation pattern as Step 7 migrations**). The protocol instructs an agent-as-Codex to author `.agents/` + `.codex/` from source using native primitives; a human reviews the diff before commit. `upgrading` itself **never** writes `.agents/` and never performs the translation.
118
- - **no** → record `Codex adapter: stale, declined` for the Step 6 report.
119
- 3. **Report line.** Add a `Codex adapter:` line to the Step 6 report — one of `regenerated` / `stale, declined` / `not present`.
63
+ ## Agent Layers (after the reportconsent-gated, keyed to the JSON)
120
64
 
121
- Presence-gated, never auto-runs, regeneration is agent-run via `quick-tasking`. The protocol doc itself reaches the project via the Step 4 `.forge/adapters/**` sync.
65
+ The engine changed; the conversation did not. Each layer consumes one JSON field and preserves the legacy consent UX exactly. Trust the JSON never re-run detection the bin already did (no second worktree scan, no second migration Detection pass).
122
66
 
123
- ## Step 5: Handle Import-Managed + Merge-Owned Files
67
+ ### a. Migrations `migrations[]`
124
68
 
125
- **`CLAUDE.md` (import-managed one-pass auto-migration, no prompt):** mirror the npm bin's `ensureClaudeMdImport()` case-for-case. The framework prose lives in `.forge/FORGE.md` (synced in Step 4) — never write prose into CLAUDE.md, and never modify user content outside a legacy forge section.
69
+ For each entry in the migrations array `[{version, guide, reason}]` from the JSON, the uniform prompt:
126
70
 
127
- 1. **No `CLAUDE.md`** → write the stub: one explanatory comment + the `@.forge/FORGE.md` import line. Report **created**.
128
- 2. **Legacy `<!-- forge:start -->` / `<!-- forge:end -->` markers present** → replace the marker section *in place* with the `@.forge/FORGE.md` import line (position preserved), collapsing blank-line runs only at the two seams never globally. Report **migrated**.
129
- 3. **No markers, but a legacy `# Forge` header** (old installs appended prose to EOF) → strip from the header to end-of-file, append the import line. Report **migrated**.
130
- 4. **Import line already present** (tolerating trailing whitespace / CRLF) → leave untouched. Report **unchanged**.
131
- 5. **User-authored CLAUDE.md, no forge content, no import line** → append the comment + import line at EOF. Report **restored** (the import-line guarantee — Forge always loads its context).
71
+ ```
72
+ {version} migration available{reason}
73
+ Guide: {guide}
74
+ Migrate now? (yes/no/show guide)
75
+ ```
132
76
 
133
- No manual-merge prompt for forge sections; migration is silent except the report line. User content outside the forge section is never modified, and the import line is never duplicated.
77
+ - **yes** invoke `quick-tasking`, handing it the guide path as the task definition. Migrations are never applied inline by upgrading; lossy steps are confirmed inside quick-tasking.
78
+ - **show guide** → display the guide, re-ask.
79
+ - **no** → note in the report (split-brain until migrated — recommend before the next planning/review cycle).
134
80
 
135
- **`.claude/settings.json`:**
136
- 1. Read source and local versions
137
- 2. Compare `forge.*` keys only
138
- 3. If different → update `forge.*` keys in local, preserve user's `hooks` and custom keys
139
- 4. Set `forge.version` to match the source package version
140
- 5. **Additively merge the template's framework hook wiring** (ADR-017, mirrors the bin's `upgradeSettingsHooks`): for each hook group in the template's `hooks`, add it to the local `hooks` unless a deep-equal group already exists. Never remove or rewrite a user's own hook entries. Without this, the scripts synced in Step 4a exist on disk but never fire on projects whose settings.json predates the wiring.
81
+ ### b. Experimental re-sync — `experimental[]`
141
82
 
142
- ## Step 6: Report
83
+ For each entry with `status: stale` (detection is bin-side; the bin never rewrites experimental skills):
143
84
 
144
85
  ```
145
- Forge Local Sync Complete
146
- ─────────────────────────
147
- Source: {path}
148
- Version: {old} → {new}
86
+ {name} is an experimental skill (from experimental/{pkg}/) pinned at install time.
87
+ The release ships newer content. Files that differ: {files}.
88
+ Re-sync from the release package? (yes / show diff / no)
89
+ ```
149
90
 
150
- Updated: {N} files
151
- - .claude/skills/executing/SKILL.md
152
- - ...
91
+ - Source path: `{packageRoot}/experimental/{pkg}/source/skills/{name}/` from the JSON. `packageRoot` is an npx cache path valid at emit time — use it immediately in this session; absent/invalid → fall back to offering `experimental/{pkg}/install.sh` guidance.
92
+ - **yes** → overwrite the installed skill's **markdown** files only (hooks, MCP server, settings/`.mcp.json` wiring stay owned by the package installer). Report *updated (experimental)* and add: *"Hooks / MCP server for {pkg} are not re-synced here — re-run `experimental/{pkg}/install.sh` if the release changed them."*
93
+ - **show diff** → display, re-ask. **no** → stays pinned; note it.
153
94
 
154
- Added: {N} files
155
- - .claude/skills/new-skill/SKILL.md
156
- - ...
95
+ ### c. Codex adapter — `codexAdapterPresent`
157
96
 
158
- Experimental skills: {N}
159
- - orchestrating (from experimental/m10) — updated [or: unchanged / STALE, declined]
160
- - ...
97
+ `false` → skip silently. `true` → the adapter may be stale vs the `.claude/` source the bin just upgraded — offer regeneration (never auto; upgrading never writes `.agents/`):
161
98
 
162
- Codex adapter: regenerated [or: stale, declined / not present]
99
+ ```
100
+ Codex adapter detected (.agents/ / .codex/). It may be stale vs the .claude/ source just upgraded.
101
+ Regenerate it now? (yes / no)
102
+ ```
163
103
 
164
- Removed from template: {N} files
165
- - .claude/agents/old-agent.md (still in your project)
166
- - ...
104
+ - **yes** invoke `quick-tasking`, handing it `.forge/adapters/codex-generation.md` as the task definition (an agent-as-Codex authors the adapter natively; a human reviews the diff before commit).
105
+ - **no** report line `Codex adapter: stale, declined`.
167
106
 
168
- Migrated: {N} files
169
- - CLAUDE.md (forge section extracted to .forge/FORGE.md — @import)
107
+ ### d. Worktree propagation — `worktrees[]`
170
108
 
171
- Restored: {N} files
172
- - CLAUDE.md (@.forge/FORGE.md import line re-appended)
109
+ Non-empty → the upgrade landed in this checkout only; each live worktree keeps its branch-point framework files until it rebases. **Commit the upgrade first** — a worktree can only rebase onto committed history; if the sync is uncommitted, prompt (*"Commit this upgrade before propagating? e.g. `chore(forge): upgrade to v{packageVersion}`"*) and wait. Then offer — never auto (a rebase can conflict and needs human judgment):
173
110
 
174
- Unchanged: {N} files
175
111
  ```
112
+ {N} live worktree(s) lag this upgrade (v{installedVersion} → v{packageVersion}):
113
+ - {path} ({branch})
114
+ Pick up v{packageVersion} in each: git -C {path} rebase main
115
+ Rebase now? (each / list-only / skip)
116
+ ```
117
+
118
+ - **each** → run `git -C {path} rebase main` per worktree; on conflict, stop that one, report it, move on — never force, never auto-resolve.
119
+ - **list-only / skip** → print the commands; the operator runs them at a safe stopping point.
120
+ - Gitignored carve-outs (`.mcp.json`, experimental hooks) are not carried by a rebase — whenever layer (b) re-synced anything, add: each worktree must re-run `experimental/{pkg}/install.sh`.
176
121
 
177
- The `Migrated:` line appears only when a legacy marker/`# Forge` section was extracted this run; `Restored:` only when a missing import line was re-appended. A steady-state upgrade lists `CLAUDE.md` under Unchanged.
122
+ ## Dev Mode (`/upgrading dev` explicit only)
178
123
 
179
- ## Step 7: Post-Upgrade Migration Checks (data-driven)
124
+ Local-clone sync for developing Forge itself: test framework changes in a project without publishing to npm. Presence of `.forge/dev-source` NEVER selects this mode — only the explicit `dev` argument does.
180
125
 
181
- After sync completes, detect legacy file layouts the new framework version no longer writes but may still read in compatibility mode. This is **data-driven** — it loops over the migration guides synced in Step 4 and runs each guide's `## Detection` block. There are **no per-version blocks here**: a new guide that ships in the template (with a conformant Detection block — see `.forge/templates/migration-guide.md`) is auto-covered on the next upgrade with no edit to this skill.
126
+ ### Source resolution
182
127
 
183
- For each guide that signals it applies, surface a migration prompt **never auto-migrate**.
128
+ `.forge/dev-source` exists read the path (first line, trimmed) and confirm it contains `packages/create-forge/template/`. Missing → ask for the local Forge repo path, validate it, save to `.forge/dev-source`.
184
129
 
185
- ### The loop
130
+ ### Harvest upstream desire paths (advisory)
186
131
 
187
- 1. **`installed`** = the project's `.claude/settings.json` `forge.version`, **captured at the START of this upgrade run, BEFORE Step 5 stamps the new version**. If absent/unknown, treat `installed` as `0.0.0` (all guides in range). Do **not** read the *source template's* `settings.json` literal it carries a placeholder the installer stamps at write time, not the real version.
188
- 2. **`source`** = `{source}/packages/create-forge/package.json` `version`. This is the authoritative new version. (Again: never the template `settings.json` literal.)
189
- 3. Glob the just-synced guides: `.forge/migrations/{v}-*.md`. Parse `{v}` (the leading `MAJOR.MINOR.PATCH`) from each filename. Select guides where **`installed < v <= source`** (semver compare on dotted numerics).
190
- 4. For each selected guide, ascending by version: run its `## Detection` fenced bash block from the project root (via the Bash tool, under `bash`).
191
- - stdout contains **`MIGRATE`** → the migration applies; surface the prompt (below). The text after `MIGRATE —` is the reason; show it.
192
- - prints nothing / no `MIGRATE` → **no-op**; report `{v}: no action` and move on.
193
- - the block errors → report `{v}: detection error (skipped)`; do **not** abort the loop.
132
+ Dev mode is the one route that bridges this project and a local Forge clone, so it harvests framework-scope desire paths (ADR-012): glob `.forge/state/desire-paths/upstream/*.md`; none skip silently. Any present → offer to copy each into `{source}/docs/desire-paths-inbox/` (create if missing); on copy, move the project-side block to `.forge/state/desire-paths/upstream/harvested/` so it isn't re-offered. Convenience tier only — the GH issue offered at capture time remains the primary channel. Never blocks the sync.
194
133
 
195
- ### The prompt (uniform across guides)
134
+ ### Downgrade guard
196
135
 
197
- For each applying guide:
136
+ Source version (`{source}/packages/create-forge/package.json`) **<** installed (`.claude/settings.json` `forge.version`) → **STOP**: *"Refusing to downgrade: installed v{installed} is newer than source v{source}. Point dev-source at a newer checkout, or confirm an intentional downgrade."* Proceed only on explicit user override — rolling backward overwrites newer framework files and deletes files newer versions added, and with `.claude/` often gitignored that is unrecoverable.
198
137
 
199
- ```
200
- {v} migration available — {reason from the MIGRATE line, or the guide title}
201
- Guide: .forge/migrations/{v}-{slug}.md
202
- (canonical: https://github.com/Attuned-Media/forge/blob/main/docs/migrations/{v}-{slug}.md)
138
+ ### Local sync
203
139
 
204
- Migrate now? (yes/no/show guide)
205
- ```
140
+ Dev mode syncs from a checkout, so it keeps file-classification semantics — **the npm bin's sync behavior is the spec**; do not re-derive it here. Categories:
206
141
 
207
- - **yes** invoke the `quick-tasking` skill, handing it the guide path as the task definition. `upgrading` never edits user state / project files / `.gitignore` directly — every migration runs via `quick-tasking`, and any lossy step (e.g. narrative extraction, staging files) is confirmed with the user first.
208
- - **show guide** → read and display the guide file, then re-ask.
209
- - **no** note in the upgrade report. The new skills compose new-shape paths on write while legacy files persist on disk (split-brain) until migrated; recommend running it before the next planning/review cycle.
142
+ - **Framework-owned** (`.claude/agents/*.md`, `.claude/skills/*/SKILL.md`, `.forge/FORGE.md`) overwrite when different; a local skill dir absent from the template defers to the experimental pass below (only no-source dirs report *removed from template* never deleted).
143
+ - **Template-only** (`.forge/templates/**`, `.forge/migrations/**`, `.forge/adapters/**`, `.forge/gitignore` `.forge/.gitignore`) overwrite; `.forge/FORGE.md` lands BEFORE the CLAUDE.md pass.
144
+ - **Additive** (`.claude/hooks/**` incl. `tests/`, `.claude/rules/**`, `.forge/checks/**`) add + overwrite template-shipped files, never delete local extras, `chmod +x` synced `*.sh`.
145
+ - **Import-managed** `CLAUDE.md` → the bin's `ensureClaudeMdImport()` cases verbatim; user content outside a forge section is never modified.
146
+ - **Merge-owned** `.claude/settings.json` → `forge.*` keys + additive hook-group wiring only; stamp `forge.version` last.
147
+ - **Never touch** user-generated files: `.forge/project.yml`, `.forge/state/`, `.forge/constitution.md`, `.forge/context.md`, `.forge/requirements/`, `.forge/roadmap.yml`, `.forge/design-system.md`, `.forge/refactor-backlog.yml`.
210
148
 
211
- ### Why there is no bookkeeping
149
+ Render the same report shape as the default path, with `Source: {path}` instead of the registry line.
212
150
 
213
- Recording "crossed" guides is implicit in the range. Once Step 5 stamps `forge.version` to `source`, a later run's range `(new installed, source]` excludes every guide already crossed — so nothing re-detects, with **no ledger or marker file**. The range also closes the install-run blind spot: detection keys off the guides **freshly synced this run** (Step 4) plus the **pre-stamp installed version**, so the run that installs new detection behavior uses the new guides, not this skill's own pre-sync prose.
151
+ ### Post-sync layers
214
152
 
215
- ## Step 8: Propagate to Live Worktrees (offer, never auto)
153
+ Re-use layers (a)–(d) with dev-derived inputs this is the one mode that computes them itself, since there is no engine JSON:
216
154
 
217
- The sync lands framework files (`.claude/skills/`, `.claude/agents/`, `.forge/FORGE.md`, templates) in **this checkout only**. A live Forge worktree keeps running its branch-point copies until it pulls main in and nothing else surfaces this: the integration flag (`.git/forge/integration-pending`) fires only on a verified **work** checkpoint, never on an upgrade, and `forge.worktree_rebase_check` is opt-in. So close the loop here, where the upgrade actually happened.
155
+ - **(a) Migrations** the data-driven Detection loop: select freshly-synced guides `.forge/migrations/{v}-*.md` where `installed < v <= source` (semver on the PRE-stamp installed version), run each guide's `## Detection` bash block from the project root; `MIGRATE` on stdout the layer-(a) prompt with the text after `MIGRATE —` as the reason; no output → `{v}: no action`; block errors `{v}: detection error (skipped)`, never abort the loop. Range bookkeeping is implicit: once `forge.version` is stamped, crossed guides fall out of the next run's range no ledger.
156
+ - **(b) Experimental staleness** — diff installed non-template skill dirs against `{source}/packages/create-forge/experimental/*/source/skills/{name}/`; stale → the layer-(b) offer with `{source}` as the source path.
157
+ - **(c) Codex** — presence glob (`.agents/` / `.codex/`) → the layer-(c) offer.
158
+ - **(d) Worktrees** — `git worktree list --porcelain` filtered to `refs/heads/forge/m-*`, main checkout only (`git rev-parse --git-dir` == `--git-common-dir`; from a worktree, skip and say so) → the layer-(d) offer.
218
159
 
219
- 1. **Main checkout only.** `git rev-parse --git-dir` == `--git-common-dir` → main; differ → this session **is** a worktree, so skip (run `/upgrading` from main to propagate). No remote/not a git repo → skip.
220
- 2. **Commit the sync first** — a worktree can only rebase onto committed history, so uncommitted synced files won't propagate. If the sync is unstaged, prompt: *"Commit this upgrade on main before propagating? (e.g. `chore(forge): upgrade to v{new}`)"* and wait.
221
- 3. **Enumerate live worktrees:** `git worktree list --porcelain`, branches matching `refs/heads/forge/m-*` (same scan as the `forge` boot preflight). None → done, silent.
222
- 4. **Offer — never auto** (a rebase can conflict and needs human judgment):
223
- ```
224
- {N} live worktree(s) lag this upgrade (v{old} → v{new}):
225
- - {path} ({branch})
226
- Pick up v{new} in each: git -C {path} rebase main
227
- Rebase now? (each / list-only / skip)
228
- ```
229
- - **each** → run `git -C {path} rebase main` per worktree; on conflict, **stop that one**, report it, and move on — never force, never auto-resolve.
230
- - **list-only / skip** → print the commands; the operator runs them when each worktree is at a safe stopping point.
231
- 5. **Gitignored carve-outs don't rebase.** `.mcp.json`, experimental hooks (e.g. M10 `forge-claim-check*.sh`), and anything else gitignored is **not** carried by a rebase. If the upgrade changed them (e.g. an experimental skill was re-synced in Step 3b), each worktree must re-run the experimental installer (`experimental/{pkg}/install.sh`) — a rebase alone leaves them stale. Surface this line whenever Step 3b reported an experimental re-sync.
160
+ Same consent gates, same report lines as the registry path.
@@ -104,7 +104,7 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
104
104
  | Security review | `securing` | When auth/data/API |
105
105
  | E2E/integration tests + suite audit (+ M9 author-mode gate) | `testing` | When UI/flows or flaky suite |
106
106
  | Systematic debugging | `debugging` | When stuck |
107
- | Upgrade Forge files | `upgrading` | On-demand |
107
+ | Upgrade Forge files from the npm registry (default; local dev sync only via explicit `/upgrading dev`) | `upgrading` | On-demand |
108
108
  | Cross-session memory | `beads-integration` | When Beads installed |
109
109
  | Project/read Forge work in Notion | `notion-integration` | When Notion configured |
110
110
  | Multi-agent backend (experimental) | `orchestrating` | Optional, usually selected by Chief/Streams |
@@ -239,7 +239,7 @@ git commit -m "chore(forge): sync state after {phase} — m{N} {phase-name}"
239
239
  | **Append-only shared** | `releases.yml`, `reservations.yml`, `state/desire-paths/*`, `deferred-issues/*` | Many writers OK; structure prevents collision (append-only entries / distinct filenames). |
240
240
  | **Machine-local runtime** | `.git/forge/id-reservations`, `.git/forge/integration-pending` | Git common dir — shared across a machine's worktrees, never git-tracked, excluded from framework context. |
241
241
  | **Shared mutable** | `context.md`, `refactor-backlog.yml`, `requirements/m{N}.yml` | Write only your milestone/stream block; within a block, append-only — strike through instead of rewriting. |
242
- | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md`, `.claude/skills/`, `.claude/agents/` | Rarely changes except via `/upgrading`. Canonical on main; worktrees lag until they rebase. An upgrade touches only its own checkout — `upgrading` Step 8 offers live-worktree rebases (never auto); opt-in `forge.worktree_rebase_check` is the boot-time net. Gitignored carve-outs (`.mcp.json`, experimental hooks) propagate via the experimental installer, not git. |
242
+ | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md`, `.claude/skills/`, `.claude/agents/` | Rarely changes except via `/upgrading` (registry default — the npm bin is the sync engine; local dev sync only via explicit `/upgrading dev`). Canonical on main; worktrees lag until they rebase. An upgrade touches only its own checkout — `upgrading` Step 8 offers live-worktree rebases (never auto); opt-in `forge.worktree_rebase_check` is the boot-time net. Gitignored carve-outs (`.mcp.json`, experimental hooks) propagate via the experimental installer, not git. |
243
243
 
244
244
  Ownership rules (quick reference):
245
245
 
@@ -47,6 +47,12 @@ json_escape() {
47
47
  printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/ /\\t/g' | tr '\n' ' '
48
48
  }
49
49
 
50
+ # Milestone-file id → unit id. Numeric files (milestone-8.yml) need the m-
51
+ # prefix added; labeled files (milestone-m-AUTO01.yml) already carry it.
52
+ unit_id() {
53
+ case "$1" in m-*) printf '%s' "$1" ;; *) printf 'm-%s' "$1" ;; esac
54
+ }
55
+
50
56
  # ---------------------------------------------------------------------------
51
57
  # Config: read a scalar under the top-level `board:` block of project.yml.
52
58
  # ---------------------------------------------------------------------------
@@ -135,8 +141,8 @@ emit_deferred() {
135
141
  base=$(basename "$f" .yml); id="${base#milestone-}"
136
142
  nm=$(yget "$f" milestone name); rs=$(yget "$f" lifecycle deferred_reason)
137
143
  [ "$first" = 1 ] && first=0 || printf ',\n'
138
- printf ' {"surface": "milestone", "id": "m-%s", "name": "%s", "deferred_at": "%s", "reason": "%s"}' \
139
- "$id" "$(json_escape "$nm")" "$(json_escape "$da")" "$(json_escape "$rs")"
144
+ printf ' {"surface": "milestone", "id": "%s", "name": "%s", "deferred_at": "%s", "reason": "%s"}' \
145
+ "$(unit_id "$id")" "$(json_escape "$nm")" "$(json_escape "$da")" "$(json_escape "$rs")"
140
146
  done
141
147
  # Surface 2: deferred REFACTOR-backlog items (status: deferred). Block-scan: accumulate
142
148
  # each `- id:` item's fields, emit when its status is deferred.
@@ -174,8 +180,8 @@ emit_deferred() {
174
180
  [ "$hit" = "1" ] || continue
175
181
  base=$(basename "$f" .yml); id="${base#milestone-}"
176
182
  [ "$first" = 1 ] && first=0 || printf ',\n'
177
- printf ' {"surface": "active-milestone", "id": "m-%s", "name": "%s", "deferred_at": "", "reason": "phase/task deferred inside active milestone"}' \
178
- "$id" "$(json_escape "$(yget "$f" milestone name)")"
183
+ printf ' {"surface": "active-milestone", "id": "%s", "name": "%s", "deferred_at": "", "reason": "phase/task deferred inside active milestone"}' \
184
+ "$(unit_id "$id")" "$(json_escape "$(yget "$f" milestone name)")"
179
185
  done
180
186
  }
181
187
 
@@ -200,7 +206,7 @@ emit_projection() {
200
206
  ufirst=1
201
207
  for f in "$FORGE"/state/milestone-*.yml; do
202
208
  [ -f "$f" ] || continue
203
- base=$(basename "$f" .yml); id="${base#milestone-}"; unit="m-$id"
209
+ base=$(basename "$f" .yml); id="${base#milestone-}"; unit=$(unit_id "$id")
204
210
  nm=$(yget "$f" milestone name)
205
211
  status=$(yget "$f" current status)
206
212
  tier=$(yget "$f" current tier)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env sh
2
+ # forge-jarvis-answer.sh — the park → answer → resume round-trip
3
+ # (m-37, Brief-R2 step 4 B4(d); contract handoff-step4-jarvis.md REV2).
4
+ #
5
+ # When a slice PARKS on an ambiguity, the relay (B4) buzzes the operator the question. The
6
+ # operator's reply comes back to Jarvis (typed or from the phone). This helper closes the loop:
7
+ # it lands that reply as the runner's CONVENTIONED answer file, then re-dispatches the slice so
8
+ # it resumes PAST the park — not a cold replay.
9
+ #
10
+ # THE CONVENTION IS FIXED (m-35, landed 0.61.0 — the named Prerequisite): the answer file is
11
+ # <work-dir>/phase-<N>/answer.md
12
+ # beside that phase's phase-report.yml. Do NOT invent a new location. On its next invocation the
13
+ # runner re-assembles that phase's prompt WITH the parked state + this answer and continues; a
14
+ # fresh park after a new answer fires a new ping. All of that is m-35 runner behavior — consumed,
15
+ # never changed (NFR-037: no runner edit).
16
+ #
17
+ # Re-dispatch goes through forge-jarvis-dispatch.sh (the detached-runner seam from B3), so the
18
+ # resume runs headless in the slice worktree exactly like the original dispatch.
19
+ #
20
+ # --work-dir <dir> the runner's --work-dir for this slice (holds phase-<N>/) [required]
21
+ # --phase <N> the parked phase index (→ phase-<N>/answer.md) [required]
22
+ # --slice <worktree> the slice worktree to re-dispatch [required]
23
+ # --answer <text> the operator's reply; or --answer-file <path>; or via stdin
24
+ # --dry-run write nothing, dispatch nothing — print the resolved plan and exit
25
+ # Extra args after `--` are forwarded to forge-jarvis-dispatch.sh verbatim.
26
+ set -u
27
+
28
+ SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
29
+ DISPATCH="${FORGE_JARVIS_DISPATCH:-$SELF_DIR/forge-jarvis-dispatch.sh}"
30
+
31
+ WORK_DIR=""; PHASE=""; SLICE=""; ANSWER=""; ANSWER_FILE=""; DRY=0
32
+ FWD=""
33
+ while [ $# -gt 0 ]; do
34
+ case "$1" in
35
+ --work-dir) WORK_DIR="${2:-}"; shift 2 ;;
36
+ --phase) PHASE="${2:-}"; shift 2 ;;
37
+ --slice) SLICE="${2:-}"; shift 2 ;;
38
+ --answer) ANSWER="${2:-}"; shift 2 ;;
39
+ --answer-file) ANSWER_FILE="${2:-}"; shift 2 ;;
40
+ --dry-run) DRY=1; shift ;;
41
+ --) shift; FWD="$*"; break ;;
42
+ *) shift ;;
43
+ esac
44
+ done
45
+
46
+ [ -n "$WORK_DIR" ] && [ -n "$PHASE" ] && [ -n "$SLICE" ] || {
47
+ printf 'forge-jarvis-answer: --work-dir, --phase and --slice are all required\n' >&2; exit 2; }
48
+
49
+ # Resolve the answer text: --answer, else --answer-file, else stdin.
50
+ if [ -z "$ANSWER" ]; then
51
+ if [ -n "$ANSWER_FILE" ] && [ -f "$ANSWER_FILE" ]; then
52
+ ANSWER="$(cat "$ANSWER_FILE")"
53
+ elif [ ! -t 0 ]; then
54
+ ANSWER="$(cat)"
55
+ fi
56
+ fi
57
+
58
+ # NEG-CONTROL: no answer content → write nothing, re-dispatch nothing (the slice stays parked).
59
+ if [ -z "$ANSWER" ]; then
60
+ printf 'forge-jarvis-answer: no answer content — nothing written, slice stays parked\n' >&2
61
+ exit 3
62
+ fi
63
+
64
+ DEST="$WORK_DIR/phase-$PHASE/answer.md" # the EXACT m-35 convention
65
+
66
+ if [ "$DRY" = 1 ]; then
67
+ printf 'answer plan: write → %s ; then re-dispatch %s --slice %s --work-dir %s %s\n' \
68
+ "$DEST" "$DISPATCH" "$SLICE" "$WORK_DIR" "$FWD"
69
+ exit 0
70
+ fi
71
+
72
+ # 1) land the answer at the conventioned path
73
+ mkdir -p "$WORK_DIR/phase-$PHASE" 2>/dev/null || true
74
+ printf '%s\n' "$ANSWER" > "$DEST" || { printf 'forge-jarvis-answer: could not write %s\n' "$DEST" >&2; exit 1; }
75
+ printf 'forge-jarvis-answer: answer landed at %s\n' "$DEST" >&2
76
+
77
+ # 2) re-dispatch the slice so the runner resumes past the park (m-35 prompt re-assembly)
78
+ # shellcheck disable=SC2086
79
+ "$DISPATCH" --slice "$SLICE" --work-dir "$WORK_DIR" $FWD