forge-orkes 0.77.0 → 0.80.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 (41) hide show
  1. package/bin/create-forge.js +27 -0
  2. package/experimental/m10/README.md +4 -4
  3. package/experimental/m10/install.sh +10 -9
  4. package/experimental/m10/source/hooks/protect-primary-checkout.sh +149 -0
  5. package/package.json +1 -1
  6. package/template/.claude/hooks/forge-context-migrate.sh +297 -0
  7. package/template/.claude/hooks/forge-size-gate.sh +175 -0
  8. package/template/.claude/hooks/forge-state-rollup.sh +135 -2
  9. package/template/.claude/hooks/protect-primary-checkout.sh +149 -0
  10. package/template/.claude/hooks/skill-gate-tiers.txt +6 -0
  11. package/template/.claude/hooks/tests/forge-context-migrate.test.sh +166 -0
  12. package/template/.claude/hooks/tests/forge-size-gate.test.sh +132 -0
  13. package/template/.claude/hooks/tests/protect-primary-checkout.test.sh +147 -0
  14. package/template/.claude/settings.json +14 -1
  15. package/template/.claude/skills/discussing/SKILL.md +30 -31
  16. package/template/.claude/skills/executing/SKILL.md +3 -3
  17. package/template/.claude/skills/forge/SKILL.md +112 -170
  18. package/template/.claude/skills/forge/references/manual-fallback-procedures.md +54 -0
  19. package/template/.claude/skills/initializing/SKILL.md +20 -1
  20. package/template/.claude/skills/planning/SKILL.md +11 -9
  21. package/template/.claude/skills/researching/SKILL.md +2 -0
  22. package/template/.claude/skills/reviewing/SKILL.md +17 -0
  23. package/template/.claude/skills/upgrading/SKILL.md +22 -2
  24. package/template/.claude/skills/verifying/SKILL.md +2 -2
  25. package/template/.forge/FORGE.md +81 -90
  26. package/template/.forge/bin/new-worktree.sh +61 -0
  27. package/template/.forge/bin/tests/new-worktree.test.sh +70 -0
  28. package/template/.forge/checks/forge-jarvis-awareness.sh +46 -2
  29. package/template/.forge/checks/forge-jarvis-relay.sh +64 -16
  30. package/template/.forge/checks/tests/forge-jarvis-awareness.test.sh +58 -0
  31. package/template/.forge/checks/tests/forge-jarvis-relay.test.sh +50 -0
  32. package/template/.forge/git-hooks/pre-commit +57 -0
  33. package/template/.forge/git-hooks/tests/pre-commit.test.sh +97 -0
  34. package/template/.forge/migrations/0.78.0-weld-primary-checkout.md +66 -0
  35. package/template/.forge/migrations/0.80.0-context-md-sharding.md +158 -0
  36. package/template/.forge/migrations/0.80.0-size-gate-wiring.md +145 -0
  37. package/template/.forge/templates/constitution.md +1 -1
  38. package/template/.forge/templates/context-milestone.md +30 -0
  39. package/template/.forge/templates/slice-runner/config.yml +7 -1
  40. package/template/.forge/templates/state/desire-path.yml +3 -0
  41. package/experimental/m10/source/hooks/forge-branch-guard.sh +0 -61
@@ -0,0 +1,145 @@
1
+ # Migration Guide: Size gate wired to boot (Forge 0.80.0)
2
+
3
+ Applies to projects upgrading to 0.80.0 or later. The size-gate check
4
+ (`.claude/hooks/forge-size-gate.sh`, issue #37) — which warns when a
5
+ context-engineering artifact (FORGE.md, context.md, a plan, a skill file, …)
6
+ crosses its declared size gate — is now invoked mechanically by the same
7
+ `SessionStart` hook that runs `forge-state-rollup.sh`. A boot session can no
8
+ longer skip the check and let an artifact silently grow past its gate until
9
+ someone notices by hand.
10
+
11
+ `/upgrading` ships the new hook file (`.claude/hooks/forge-size-gate.sh`) like
12
+ any other framework file. It does **not** wire the `SessionStart` invocation,
13
+ because `.claude/settings.json` is a **user-owned file** — upgrades never
14
+ overwrite it (FORGE.md → State Ownership, "Stable shared" class notes
15
+ settings.json is propagated only via explicit installer steps, never blanket
16
+ overwrite). Without this migration's one manual step, the hook ships but is
17
+ never invoked: the executable exists, nothing mechanically triggers it.
18
+
19
+ Projects that ran `initializing` fresh on 0.80.0+ (or already carry the
20
+ `forge-size-gate` reference in their `SessionStart` block from an earlier
21
+ partial adoption) need nothing further.
22
+
23
+ ## Prerequisites
24
+
25
+ 1. On the new version's framework files (`npx forge-orkes upgrade`, or the
26
+ `upgrading` skill) — so `.claude/hooks/forge-size-gate.sh` is present and
27
+ executable.
28
+ 2. Already migrated to 0.77.0's `forge-state-rollup.sh` SessionStart wiring
29
+ (`.forge/migrations/0.77.0-state-rollup-executable.md`) — this guide
30
+ assumes that hook block already exists in `.claude/settings.json` and
31
+ extends it, rather than creating a `SessionStart` block from scratch. If
32
+ that migration hasn't run yet, do it first.
33
+ 3. Working tree clean or changes committed — this migration edits
34
+ `.claude/settings.json`, a tracked file.
35
+
36
+ ## Detection
37
+
38
+ Keyed on **the executable existing while the SessionStart wiring is
39
+ absent** — the shipped-but-unwired gap. Uses `jq` to inspect
40
+ `.claude/settings.json`'s `hooks.SessionStart` array for an entry referencing
41
+ `forge-size-gate`; degrades to a plain-text grep if `jq` is not installed.
42
+ Silent in a non-Forge / non-git tree, silent once the hook is wired:
43
+
44
+ ```bash
45
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
46
+ [ -x .claude/hooks/forge-size-gate.sh ] || exit 0
47
+
48
+ settings=".claude/settings.json"
49
+ [ -f "$settings" ] || {
50
+ echo "MIGRATE — forge-size-gate.sh is present but $settings does not exist; add it with the SessionStart hook below"
51
+ exit 0
52
+ }
53
+
54
+ if command -v jq >/dev/null 2>&1; then
55
+ wired="$(jq -r '
56
+ (.hooks.SessionStart // []) as $entries
57
+ | [$entries[].hooks[]?.command // "" | select(contains("forge-size-gate"))]
58
+ | length > 0
59
+ ' "$settings" 2>/dev/null)"
60
+ if [ "$wired" != "true" ]; then
61
+ echo "MIGRATE — $settings has no SessionStart hook referencing forge-size-gate.sh; the executable shipped but nothing mechanically triggers it"
62
+ fi
63
+ else
64
+ if ! grep -q "forge-size-gate" "$settings" 2>/dev/null; then
65
+ echo "MIGRATE — $settings has no reference to forge-size-gate.sh (jq unavailable, ran plain-text check); add the SessionStart hook below"
66
+ fi
67
+ fi
68
+ ```
69
+
70
+ ## Migration steps
71
+
72
+ ### 1. Add the size-gate command to the SessionStart hook block
73
+
74
+ Open `.claude/settings.json` and find the `hooks.SessionStart` array entry
75
+ (added by the 0.77.0 migration). Append a second command object to that
76
+ entry's `hooks` array, alongside the existing `forge-state-rollup.sh` command
77
+ — **not** a second `SessionStart` array entry:
78
+
79
+ ```json
80
+ {
81
+ "hooks": {
82
+ "SessionStart": [
83
+ {
84
+ "matcher": "startup|resume|clear",
85
+ "hooks": [
86
+ {
87
+ "type": "command",
88
+ "command": "[ -x \"$CLAUDE_PROJECT_DIR/.claude/hooks/forge-state-rollup.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/forge-state-rollup.sh\" || true"
89
+ },
90
+ {
91
+ "type": "command",
92
+ "command": "[ -x \"$CLAUDE_PROJECT_DIR/.claude/hooks/forge-size-gate.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/forge-size-gate.sh\" || true"
93
+ }
94
+ ]
95
+ }
96
+ ]
97
+ }
98
+ }
99
+ ```
100
+
101
+ Both commands run on the same `startup|resume|clear` matcher, in order —
102
+ `forge-state-rollup.sh` first (so caches are fresh), then
103
+ `forge-size-gate.sh` (which only reads files, order doesn't matter
104
+ functionally, but mirroring this repo's own `.claude/settings.json` keeps the
105
+ two guides consistent).
106
+
107
+ If `hooks.SessionStart` already has other entries (e.g. from a project-local
108
+ customization) beyond the one 0.77.0 added, append the `forge-size-gate.sh`
109
+ command object to whichever entry's `hooks` array already contains
110
+ `forge-state-rollup.sh`, rather than creating a new array entry. If no
111
+ `SessionStart` block exists yet at all (0.77.0's migration was skipped), run
112
+ that migration first — this guide only adds a second command, it does not
113
+ construct the block from scratch.
114
+
115
+ The `[ -x ... ] && ... || true` guard means the hook is a no-op — never a
116
+ boot failure — on any checkout where the script is absent or not executable
117
+ (SessionStart hooks are non-blocking; `forge-size-gate.sh` itself also always
118
+ exits 0 — it's an advisory, not a gate that can fail a boot).
119
+
120
+ ### 2. Commit the settings change
121
+
122
+ ```bash
123
+ git add .claude/settings.json
124
+ git commit -m "chore(forge): wire forge-size-gate SessionStart hook (0.80.0)"
125
+ ```
126
+
127
+ ## Validation
128
+
129
+ - Start a new session, or run `/clear` — the boot summary line should include
130
+ a `[forge-size-gate] N artifact(s) over their declared gate: …` line if any
131
+ tracked artifact is currently over gate, or no `[forge-size-gate]` line at
132
+ all if everything is within gate (the hook is silent when there's nothing
133
+ to report — it does not print an "all clear" line). Either outcome means
134
+ the hook ran; the absence of a `[forge-size-gate]` clause is not the same
135
+ as the hook slot producing no output at all (compare to before this
136
+ migration, where the SessionStart summary had no size-gate clause because
137
+ nothing invoked the script).
138
+ - `jq '.hooks.SessionStart' .claude/settings.json` prints the array with both
139
+ the `forge-state-rollup` and `forge-size-gate` commands present in the same
140
+ entry's `hooks` list.
141
+ - Re-running the Detection block above now prints nothing (exits silently) —
142
+ the gap is closed.
143
+ - Manually run `.claude/hooks/forge-size-gate.sh` from the repo root — it
144
+ should produce the same output the boot's SessionStart line showed,
145
+ confirming the wired invocation matches a direct run.
@@ -130,4 +130,4 @@ Minimize framework token footprint. Compress prose, route models by task complex
130
130
 
131
131
  ## Amending the Constitution
132
132
 
133
- Articles can be amended only by explicit user decision during a discuss phase. Amendments are recorded in `.forge/context.md` with rationale. No agent may unilaterally override a constitutional article.
133
+ Articles can be amended only by explicit user decision during a discuss phase. Amendments are recorded in the active milestone's `.forge/context/{id}.md` with rationale (`.forge/context.md` is a derived index, regenerated every boot — never a write target). No agent may unilaterally override a constitutional article.
@@ -0,0 +1,30 @@
1
+ # Per-Milestone Locked Context — `.forge/context/{id}.md`
2
+
3
+ One file per milestone (`m-{N}.md`), holding exactly that milestone's slice
4
+ of what the old monolithic `.forge/context.md` used to carry inline. Same
5
+ append-only convention as before — strike through instead of rewriting when a
6
+ decision changes; add an Amendment Log entry in `.forge/context-log.md`.
7
+
8
+ The rendered `.forge/context.md` (derived index, `forge-state-rollup.sh`)
9
+ prefers a `<!-- summary: ... -->` marker on the line right after the heading
10
+ over the first bullet, when a hand-written one-liner is worth pinning.
11
+
12
+ ---
13
+
14
+ ### m-{N} — {Topic} (locked {date})
15
+
16
+ <!-- summary: one-line hand-written summary for the derived index (optional) -->
17
+
18
+ Context: {why this milestone exists / what triggered it}.
19
+
20
+ - **[Topic]**: [Decision]. Reason: [Why this was chosen].
21
+
22
+ ## Deferred
23
+
24
+ <!-- Deferred Ideas entries whose text starts with this milestone's id prefix
25
+ (e.g. "m-{N}: ...") land here during migration or ongoing work. -->
26
+
27
+ ## Discretion
28
+
29
+ <!-- Discretion Areas entries whose text starts with this milestone's id
30
+ prefix land here during migration or ongoing work. -->
@@ -15,7 +15,13 @@ per_slice_budget_usd: 25
15
15
 
16
16
  # Max agent turns per headless phase (passed to `claude -p --max-turns`). Bounds a
17
17
  # single phase; cross-platform (unlike a `timeout` wall-clock, which macOS lacks).
18
- max_turns_per_phase: 60
18
+ # Raised 60 -> 120 (m-54): three consecutive error_max_turns halts on large
19
+ # content-editing phases (context.md shard/migration, FORGE.md trim), each attempt
20
+ # landing real incremental progress before hitting the wall — the phases themselves
21
+ # weren't stuck, 60 was just too tight for multi-file, iteratively-tested work.
22
+ # Budget (per_slice_budget_usd) already gates cost independently, so this has
23
+ # headroom: each halted attempt spent $2-6 of the $25 slice cap, nowhere near it.
24
+ max_turns_per_phase: 120
19
25
 
20
26
  # RESERVED — not yet enforced. Intended as an optional per-phase wall-clock cap in
21
27
  # seconds (0 = off); the runner does not read this key yet (macOS has no stock
@@ -7,6 +7,9 @@ type: "" # recurring_friction | agent_struggle | us
7
7
  date: "" # ISO 8601 date the observation was made, e.g. "2026-07-23"
8
8
  milestone: "" # milestone id, or a short label for non-milestone work (e.g. "quick-task (...)")
9
9
  scope: project # project | framework — absent ⇒ project (this repo only; framework ⇒ upstream-eligible per ADR-012)
10
+ # resolves_issue: 26 # OPTIONAL — GitHub issue number this observation's fix resolves. The
11
+ # # close-the-loop check (reviewing) keys off it to surface the issue as
12
+ # # likely-closable once the fix ships. Omit when the observation resolves no issue.
10
13
  suggestion_target: "" # the file/skill this observation would change if acted on
11
14
  note: >
12
15
  What was observed — concrete enough that a future review can judge recurrence
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Forge PreToolUse(Bash) guardrail — protect the shared main checkout's HEAD when
3
- # parallel git worktrees are active.
4
- #
5
- # Blocks the "moved the shared HEAD under other sessions" catastrophe (canvaz, burned
6
- # twice 2026-06-05): switching branches or hard-resetting in the MAIN checkout while
7
- # sibling worktrees are branched off it. Orchestration-agnostic — depends only on git
8
- # worktree state, so it holds whether coordination is M10, Agent Teams, or manual
9
- # worktrees.
10
- #
11
- # Scope is deliberately narrow to avoid false positives:
12
- # - fires ONLY when >1 worktree exists (single-worktree/solo work is never touched)
13
- # - fires ONLY in the main checkout (linked worktrees own their own HEAD)
14
- # - skips any command containing `cd` (can't reliably tell which tree it targets)
15
- # - allows safe forms: file restore (`git checkout -- <path>`), branch create (`-b`)
16
- #
17
- # exit 0 = allow; exit 2 = deny. Override by running the command yourself outside Claude.
18
-
19
- set -uo pipefail
20
-
21
- PAYLOAD=$(cat)
22
- CMD=$(printf '%s' "$PAYLOAD" | jq -r '.tool_input.command // empty' 2>/dev/null) || exit 0
23
- [ -z "$CMD" ] && exit 0
24
-
25
- # Only git commands are interesting; and bail on `cd` (target tree is ambiguous).
26
- case "$CMD" in
27
- *git*) ;;
28
- *) exit 0 ;;
29
- esac
30
- case "$CMD" in
31
- *"cd "*) exit 0 ;;
32
- esac
33
-
34
- git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
35
-
36
- # Single worktree → no shared-HEAD hazard.
37
- wt_count=$(git worktree list 2>/dev/null | wc -l | tr -d ' ')
38
- [ "${wt_count:-1}" -le 1 ] && exit 0
39
-
40
- # Main checkout iff --git-dir == --git-common-dir (linked worktrees differ).
41
- [ "$(git rev-parse --git-dir 2>/dev/null)" = "$(git rev-parse --git-common-dir 2>/dev/null)" ] || exit 0
42
-
43
- deny() { echo "[forge-branch-guard] $*" >&2; exit 2; }
44
-
45
- # Branch switch (checkout/switch to a ref), excluding file-restore (`--`) and create (`-b`).
46
- switches_branch=0
47
- if printf '%s' "$CMD" | grep -qE '\bgit[[:space:]]+(checkout|switch)\b'; then
48
- if ! printf '%s' "$CMD" | grep -qE '([[:space:]]--([[:space:]]|$))|(\bgit[[:space:]]+(checkout|switch)[[:space:]]+-[bB]\b)'; then
49
- switches_branch=1
50
- fi
51
- fi
52
-
53
- # Hard/keep/merge reset rewrites HEAD + working tree.
54
- hard_reset=0
55
- printf '%s' "$CMD" | grep -qE '\bgit[[:space:]]+reset[[:space:]]+(--hard|--keep|--merge)\b' && hard_reset=1
56
-
57
- if [ "$switches_branch" = "1" ] || [ "$hard_reset" = "1" ]; then
58
- deny "Refusing to move the shared main HEAD — $wt_count worktrees are branched off it (the 'burned twice' failure). Switch/reset inside a worktree, or stop the other sessions first. Override: run the command yourself outside Claude."
59
- fi
60
-
61
- exit 0