agentic-team 6.1.0 → 6.3.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 (45) hide show
  1. package/README.md +8 -3
  2. package/bin/build-template.js +2 -2
  3. package/bin/index.js +234 -39
  4. package/bin/platforms.js +510 -0
  5. package/package.json +4 -4
  6. package/template/.claude-plugin/plugin.json +2 -2
  7. package/template/.orchestra/README.md +41 -51
  8. package/template/.orchestra/config.yml +16 -0
  9. package/template/.orchestra/roles/product-manager.md +3 -2
  10. package/template/.orchestra/scripts/compact-diff.awk +1 -1
  11. package/template/.orchestra/scripts/context.mjs +373 -0
  12. package/template/.orchestra/stacks/_repo/setup.sh +7 -3
  13. package/template/.orchestra/stacks/mobile-expo/setup.sh +1 -1
  14. package/template/CLAUDE.md +1 -1
  15. package/template/agents/lead.md +171 -116
  16. package/template/agents/milestone-reviewer.md +5 -3
  17. package/template/agents/product-manager.md +3 -2
  18. package/template/agents/re-reviewer.md +25 -0
  19. package/template/agents/reviewer.md +7 -5
  20. package/template/agents/scaffolder.md +26 -0
  21. package/template/commands/help.md +1 -0
  22. package/template/commands/hotfix.md +3 -1
  23. package/template/commands/rewind.md +10 -8
  24. package/template/commands/setup.md +2 -1
  25. package/template/commands/start.md +2 -2
  26. package/template/commands/status.md +1 -1
  27. package/template/commands/sync.md +5 -2
  28. package/template/commands/verifier.md +2 -2
  29. package/template/hooks/audit-context.sh +15 -2
  30. package/template/hooks/enforce-role.sh +21 -3
  31. package/template/hooks/protect-system-files.sh +2 -2
  32. package/template/hooks/review-rounds.sh +1 -1
  33. package/template/rules/code-standards.orchestra.md +3 -2
  34. package/template/rules/design-parity.orchestra.md +12 -1
  35. package/template/rules/observability.orchestra.md +3 -0
  36. package/template/rules/performance.orchestra.md +5 -1
  37. package/template/rules/phase-limits.orchestra.md +1 -1
  38. package/template/rules/pull-request.orchestra.md +7 -2
  39. package/template/rules/role-boundaries.orchestra.md +24 -9
  40. package/template/rules/security.orchestra.md +1 -1
  41. package/template/rules/stuck-detection.orchestra.md +1 -1
  42. package/template/rules/testing-standards.orchestra.md +10 -5
  43. package/template/rules/tracker-authority.orchestra.md +9 -1
  44. package/template/skills/push-notifications/SKILL.md +1 -1
  45. package/template/skills/testing/SKILL.md +2 -1
@@ -2,7 +2,7 @@
2
2
  name: reviewer
3
3
  description: "Independent code reviewer. Reviews a task's pull request — findings inline on the lines they are about, the verdict as a label. No memory of writing the code. Called by lead once the PR exists."
4
4
  model: opus
5
- effort: xhigh
5
+ effort: high
6
6
  disallowedTools: [Write, Edit, NotebookEdit]
7
7
  ---
8
8
 
@@ -127,8 +127,10 @@ and neither is "looks fine" — the app has a token module precisely so this is
127
127
  a number rather than an opinion.
128
128
 
129
129
  If the design and the token module genuinely disagree — a value the tokens
130
- cannot express — that is a blocker to report, not a difference to split. See
131
- `.claude/rules/design-parity.orchestra.md`.
130
+ cannot express — that is a blocker to report, not a difference to split. When
131
+ the pull request has a design source, read
132
+ `.claude/rules/design-parity.orchestra.md` before you start; it is not loaded
133
+ for you.
132
134
 
133
135
  **And look for what no number catches:**
134
136
 
@@ -266,8 +268,8 @@ determine a base, say so and review the full working tree instead.
266
268
  Severity is the impact on the product, not how interesting the finding is.
267
269
  Blocking and Important are fixed in this pull request. A Suggestion does not
268
270
  hold it: it is an inline comment tagged `[suggestion]` like any other finding,
269
- and lead records each one — a `tech_debt` task with a provider, a line in
270
- `context.md` with `tracker.provider: none`. One run left the
271
+ and lead records each one — a `tech_debt` task with a provider,
272
+ `context.mjs decide <m> tech_debt` with `tracker.provider: none`. One run left the
271
273
  scale undefined, and a reviewer probing a measuring tool's maths found a new
272
274
  "medium" corner every round, nine rounds running.
273
275
 
@@ -71,6 +71,23 @@ convention is worse than an absent one, because it looks decided.
71
71
  |---|---|
72
72
  | Task tracker | none · GitHub Projects · ClickUp |
73
73
  | Shared UI kit? | yes · no |
74
+ | Base branch — where tasks branch off and squash into | `main` · another (name it) |
75
+
76
+ Write the answer to `delivery.base_branch` in `.orchestra/config.yml`. Do not
77
+ read it from the repository's default branch: that is often the release
78
+ branch, not the one work lands on.
79
+
80
+ Write the platforms from Round 1 and 2 to `project.platforms`: Web → `web`,
81
+ Expo → `expo`, Native mobile → `ios, android`, Backend → `backend`. Desktop
82
+ and Game have no pack and add nothing. Step 5 narrows the install to this
83
+ list.
84
+
85
+ Add to the list, never replace it. With `--force` on a project that already
86
+ has apps, its platforms stay listed — Step 5 would otherwise retire the
87
+ existing app's skills and rule sections. If the stack you need is not under
88
+ `.orchestra/stacks/`, the install was narrowed before this app was asked
89
+ for: write the list now and run `npx -y agentic-team@<version>` (the version
90
+ in `.orchestra/.installed.json`) to bring the stack back, then scaffold.
74
91
 
75
92
  If the user wants a shared UI kit **and** picked `standalone`, stop and
76
93
  resolve the conflict now: standalone apps cannot import a workspace package.
@@ -168,6 +185,15 @@ command. Report that to the user; do not try to install it yourself.
168
185
  Fix failures before reporting. If a fix fails 3 times, stop and report —
169
186
  follow `.claude/rules/stuck-detection.orchestra.md`.
170
187
 
188
+ Then narrow the install to the platforms you wrote: run
189
+ `npx -y agentic-team@<version>`, where `<version>` is the `version` in
190
+ `.orchestra/.installed.json`. Same version, so nothing changes except what is
191
+ installed: the other platforms' stacks, skills and rule sections are retired.
192
+ Run it only now — Step 4 still needed the stacks. If that version cannot be
193
+ fetched — staged on npm and not yet approved, or installed from a checkout —
194
+ say so in the report and give the user the command; never substitute
195
+ `@latest`, which could install a different version over this one.
196
+
171
197
  Commit once: `chore(setup): scaffold {platforms}`.
172
198
 
173
199
  ## Step 6 — Report
@@ -30,6 +30,7 @@ AGENTS:
30
30
  scaffolder Interactive project setup (/orchestra:setup)
31
31
  lead Team assembler + milestone executor (/orchestra:start)
32
32
  reviewer Independent code review (called by lead)
33
+ re-reviewer Review rounds 2+ at lower effort (called by lead)
33
34
  tracker Milestone export to an external tracker (/orchestra:export)
34
35
 
35
36
  PIPELINE (set by PM via milestone Complexity field):
@@ -4,7 +4,9 @@ Usage: `/orchestra:hotfix {description}`
4
4
 
5
5
  The lead will:
6
6
  1. Auto-create a hotfix milestone + single phase
7
- 2. Implement the fix (minimal, focused change)
7
+ 2. Implement the fix (minimal, focused change) — through an engineer whose
8
+ prompt starts with a line `Hotfix: {description}`, the one engineer the role
9
+ hook lets lead start from its own session
8
10
  3. Run verification gate (test + lint MUST pass)
9
11
  4. Commit with `fix({scope}): {description}`
10
12
  5. Push immediately — no RFC, no review, no approval gates
@@ -9,13 +9,16 @@ Review milestone execution history for actionable insights. PM role only.
9
9
  - No arguments: all milestones with `status: done`
10
10
  - With numbers: only milestones matching those numbers (e.g., `1` matches `M1-*`)
11
11
  3. For each milestone, read execution artifacts:
12
- - `context.md` — structured sections:
13
- - `## Decisions` — key choices made during implementation
14
- - `## Journal` — append-only: what happened, and which alternatives were rejected and why
15
- - `## Metrics` — phase duration and verification retries
16
- - `## Phases` — status, commits, errors per phase
12
+ - `context.md` — `## Phases`: status, commits, errors per phase
13
+ - `decisions.md` — choices made during implementation; a line naming a
14
+ decision as `closed` says how it ended
15
+ - `journal.md` — append-only: what happened, and which alternatives were
16
+ rejected and why
17
+ - `node .orchestra/scripts/context.mjs metrics <m>` — review rounds and the
18
+ last commit per phase, computed now. Durations and retries are not
19
+ stored; say so rather than estimating them
17
20
  - `grooming.md` — original scope vs what actually happened
18
- - Review verdict and comments (from context.md or git log)
21
+ - Review verdict and comments (from the pull requests or git log)
19
22
  4. Extract and present — focus on **what the user needs to know**, not execution mechanics:
20
23
 
21
24
  ```
@@ -29,8 +32,7 @@ Review milestone execution history for actionable insights. PM role only.
29
32
 
30
33
  ### Performance
31
34
  - Total phases: 5 | Completed: 5 | Failed: 0
32
- - Longest phase: phase-3 (~12min) — complex UI with form validation
33
- - Verification retries: 3 total (phase-2: 2, phase-4: 1)
35
+ - Review rounds: phase-2: 3, phase-4: 1 (from `context.mjs metrics`)
34
36
  - Stuck: No
35
37
 
36
38
  ### Review Findings
@@ -8,7 +8,8 @@ The scaffolder will:
8
8
  3. Scaffold each app with its official create tool
9
9
  4. Install the shared baseline (TypeScript, Biome, Vitest, CI, Node pin)
10
10
  5. Write per-app `CLAUDE.md` and `.claude/` from `.orchestra/stacks/`
11
- 6. Run the verification gate and report what was created
11
+ 6. Run the verification gate, narrow the install to the chosen platforms,
12
+ and report what was created
12
13
 
13
14
  Refuses to run if the working tree already contains a scaffolded app,
14
15
  unless you pass `--force`.
@@ -19,8 +19,8 @@ sub-agents are unaffected either way; their effort comes from the phase.
19
19
  The lead will:
20
20
  1. Scan milestones for pending work
21
21
  2. Assemble the right team for each phase (identity derived from phase content)
22
- 3. Delegate phases to sub-agents, load skills, implement code
23
- 4. Trigger code review via reviewer agent
22
+ 3. Run each milestone in a milestone agent, which delegates phases to sub-agents
23
+ 4. Have each task's pull request reviewed there, by the reviewer agent
24
24
  5. Push automatically after review passes
25
25
  6. After milestone: `--auto` continues to next automatically, normal mode asks user
26
26
 
@@ -7,5 +7,5 @@ Show full milestone status report. PM role only.
7
7
  - All milestones with status, current phase, next action
8
8
  - Phase details for active milestone (status, effort, metrics)
9
9
  - Git status (branch, unpushed commits)
10
- - Metrics summary from context.md `## Metrics` section (duration + retries per phase)
10
+ - Metrics from `node .orchestra/scripts/context.mjs metrics <m>` — review rounds and last commit per phase, computed now; nothing else is stored
11
11
  - Actions needed (specific next steps)
@@ -40,8 +40,11 @@ other's and stops. See `.claude/rules/tracker-authority.orchestra.md`.
40
40
  6. **Plan change, as the PM:** revise the phase file, run
41
41
  `/orchestra:export {id}` so the board carries the new text, reply under
42
42
  the comment saying what changed. **As lead:** surface it and stop.
43
- 7. **Anything else, as lead:** build it, run the full verification gate, open
44
- a pull request. Reply under the triggering comment with the pull request
43
+ 7. **Anything else, as lead:** launch a single-phase agent
44
+ (`.claude/agents/lead.md`, Single-Phase Agent) — fix mode for the task's open
45
+ PR, build mode for new work — with the comment quoted. It builds, runs the
46
+ full verification gate and opens or updates the pull request; lead itself
47
+ starts no engineer. Reply under the triggering comment with the pull request
45
48
  link. Move the task to `in_review`. **As the PM:** surface it and stop.
46
49
 
47
50
  ## What it refuses
@@ -15,8 +15,8 @@ Verify that implemented milestones match their requirements. PM role only.
15
15
  - `grooming.md` — scope decisions and phase breakdown
16
16
  - All `phases/*.md` — phase acceptance criteria
17
17
  4. For each milestone, read execution context:
18
- - `context.md` — `## Decisions` section (why specific approaches were chosen)
19
- - `context.md` — `## Journal` section (rejected alternatives, useful when a requirement shows as "deviated")
18
+ - `decisions.md` — why specific approaches were chosen, and which were later closed
19
+ - `journal.md` — rejected alternatives, useful when a requirement shows as "deviated"
20
20
  - `context.md` — `## Phases` section (which phases completed, which failed)
21
21
  5. For each milestone, read the actual implementation:
22
22
  - Run `git log --oneline` filtered to commits from that milestone's phases
@@ -79,7 +79,7 @@ printf '%s' "$input" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}" node -e
79
79
  const section = /(?:^|\r?\n)## Phases\r?\n([\s\S]*?)(?=\r?\n## |$)/.exec(text);
80
80
  const body = section ? section[1] : "";
81
81
  for (const line of body.split(/\r?\n/)) {
82
- const m = /^-\s*(\S+):\s*(done|in-progress|failed|pending)\b/i.exec(line.trim());
82
+ const m = /^-\s*(\S+):\s*(done|in-progress|failed|pending|blocked)\b/i.exec(line.trim());
83
83
  if (!m) continue;
84
84
  const [, phase, statusRaw] = m;
85
85
  const status = statusRaw.toLowerCase();
@@ -90,6 +90,19 @@ printf '%s' "$input" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}" node -e
90
90
  }
91
91
  }
92
92
  }
93
+ // decisions.md: one source for what a valid line is — the script that
94
+ // writes it — rather than a second parser here that drifts from it.
95
+ const script = path.join(root, ".orchestra", "scripts", "context.mjs");
96
+ if (fs.existsSync(script)) {
97
+ try {
98
+ require("node:child_process").execFileSync(process.execPath, [script, "check"], {
99
+ cwd: root,
100
+ stdio: ["ignore", "pipe", "ignore"],
101
+ });
102
+ } catch (e) {
103
+ for (const f of String(e.stdout || "").split("\n").filter(Boolean)) findings.push(f);
104
+ }
105
+ }
93
106
  return { findings, open };
94
107
  }
95
108
 
@@ -132,7 +145,7 @@ printf '%s' "$input" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}" node -e
132
145
 
133
146
  const lines = [];
134
147
  if (result.findings && result.findings.length) {
135
- lines.push("Context audit after compaction — context.md has unresolved defects:");
148
+ lines.push("Context audit after compaction — the milestone record has unresolved defects:");
136
149
  for (const f of result.findings) lines.push(` - ${f}`);
137
150
  }
138
151
  if (result.open && result.open.length) {
@@ -14,6 +14,8 @@
14
14
  # judged against the recorded role. Two terminals are two sessions, so the
15
15
  # PM's marker never applies to lead.
16
16
  #
17
+ # Lead is held to one rule: phase work starts inside a milestone agent.
18
+ #
17
19
  # No marker means no role — discussion mode — and nothing is blocked.
18
20
  #
19
21
  # Reads the tool call on stdin. Exit 0 allows, exit 2 blocks.
@@ -89,10 +91,26 @@ verdict="$(printf '%s' "$input" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD
89
91
  } catch {
90
92
  return;
91
93
  }
94
+ // ── Lead ─────────────────────────────────────────────────────────────
95
+ // Phase work runs inside a milestone agent, so each milestone'"'"'s context is
96
+ // dropped when it ends. One run kept two milestones in the lead session
97
+ // instead: 738 calls, context up to 925k tokens. A call from a sub-agent
98
+ // carries agent_id; one from the lead session itself does not.
99
+ if (role === "lead") {
100
+ if (tool !== "Agent" || call.agent_id) return;
101
+ const type = input.subagent_type || "";
102
+ if (!/^(engineer-\w+|reviewer|re-reviewer)$/.test(type)) return;
103
+ if (/^engineer-/.test(type) && /^\s*Hotfix:/.test(String(input.prompt || ""))) return;
104
+ process.stdout.write(
105
+ `Lead runs phase work inside a milestone agent, not in its own session — launch a general-purpose agent with the Milestone Agent or Single-Phase Agent prompt in .claude/agents/lead.md, and let it start ${type}. A hotfix engineer'"'"'s prompt starts with a line "Hotfix: <description>".\n`,
106
+ );
107
+ return;
108
+ }
109
+
92
110
  if (role !== "product-manager") return;
93
111
 
94
112
  // ── The PM ───────────────────────────────────────────────────────────
95
- // Writes milestones. Commits them to main and pushes. Nothing else.
113
+ // Writes milestones. Commits them to delivery.base_branch and pushes. Nothing else.
96
114
  const deny = (why) => {
97
115
  process.stdout.write(`Outside the PM'"'"'s scope: ${why}\n`);
98
116
  };
@@ -140,10 +158,10 @@ verdict="$(printf '%s' "$input" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD
140
158
  const cmd = input.command || "";
141
159
 
142
160
  if (/\bgh\s+pr\s+(create|merge|edit|close|ready|checkout)\b/.test(cmd)) {
143
- return deny("the PM never opens, edits or merges a pull request. Milestone files go to main directly; code is lead'"'"'s.");
161
+ return deny("the PM never opens, edits or merges a pull request. Milestone files go to delivery.base_branch directly; code is lead'"'"'s.");
144
162
  }
145
163
  if (/\bgit\s+(checkout|switch)\s+(-b|-c|--create|-B)\b/.test(cmd)) {
146
- return deny("the PM never creates a branch. Milestone files are committed to main.");
164
+ return deny("the PM never creates a branch. Milestone files are committed to delivery.base_branch.");
147
165
  }
148
166
  if (/\bgit\s+(merge|rebase|cherry-pick|reset\s+--hard)\b/.test(cmd)) {
149
167
  return deny("the PM never rewrites history or merges.");
@@ -56,7 +56,7 @@ blocked="$(printf '%s' "$input" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD
56
56
  }
57
57
  if (!root) return;
58
58
 
59
- // The orchestrator repo is where these files are source. That is a fact
59
+ // The agentic-team repo is where these files are source. That is a fact
60
60
  // about the tree, not about who is asking.
61
61
  if (fs.existsSync(path.join(root, "bin/build-template.js"))) return;
62
62
 
@@ -114,7 +114,7 @@ Orchestra put it there. Editing it here is lost work: the next upgrade
114
114
  overwrites it, and the next project meets the same problem you were about to
115
115
  fix.
116
116
 
117
- Change it in the orchestrator repo and reinstall. If it is genuinely
117
+ Change it in the agentic-team repo and reinstall. If it is genuinely
118
118
  project-specific, it belongs in the app's own CLAUDE.md, a project skill of
119
119
  your own, or .orchestra/config.yml — none of which is overwritten.
120
120
 
@@ -44,7 +44,7 @@ verdict="$(printf '%s' "$input" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD
44
44
  }
45
45
  if (call.tool_name !== "Agent") return;
46
46
  const input = call.tool_input || {};
47
- if (input.subagent_type !== "reviewer") return;
47
+ if (input.subagent_type !== "reviewer" && input.subagent_type !== "re-reviewer") return;
48
48
  const prompt = String(input.prompt || "");
49
49
 
50
50
  // The installer writes into the directory it ran in, which can be an app
@@ -20,7 +20,8 @@
20
20
  This governs volume, never correctness. Validation, error handling, security
21
21
  and accessibility are never on this ladder — they apply at every rung,
22
22
  covered by their own rules (`security.orchestra.md`,
23
- `observability.orchestra.md`, the `accessibility` skill), regardless of how
23
+ `observability.orchestra.md`, and the `accessibility` skill where the
24
+ platform has one), regardless of how
24
25
  small the change looks. Rung 5 does not skip the wrapper
25
26
  `testing-standards.orchestra.md` requires for anything calling a third-party
26
27
  library (`src/integrations/<provider>/`) — that wrapper exists for the seam
@@ -28,7 +29,7 @@ test, not for swapability, and the ladder governs what sits on top of it,
28
29
  never whether it exists.
29
30
 
30
31
  **Zero-tolerance rules:**
31
- - No workarounds — if the right solution is hard, do it right. Flag effort in report or context.md.
32
+ - No workarounds — if the right solution is hard, do it right. Flag the effort in your report.
32
33
  - No unused code — delete dead imports, functions, files. Don't comment out.
33
34
  - No breaking changes without migration — update every consumer.
34
35
  - Current library versions only — verify before using, don't assume from memory.
@@ -1,3 +1,8 @@
1
+ ---
2
+ paths:
3
+ - "designs/**"
4
+ ---
5
+
1
6
  # Design Parity
2
7
 
3
8
  ## What a design source is
@@ -49,9 +54,15 @@ skipped. That is an honest unverified, not a pass.
49
54
  designs/
50
55
  ├── <screen>.png the design, committed, mirroring the screen structure
51
56
  ├── .fetched/ a URL design, fetched once at milestone start
52
- └── .captures/ what was built, committed so the PR can show it
57
+ ├── .captures/ what was built, committed so the PR can show it
58
+ └── .reports/ machine output of a comparison, gitignored
53
59
  ```
54
60
 
61
+ Only the images a pull request shows are committed. A comparison tool's
62
+ output — JSON reports, logs, diffs — goes to `designs/.reports/`, which is
63
+ never committed: it is regenerated from the images, and one project committed
64
+ ~100k lines of it, which agents then read back as context.
65
+
55
66
  A URL is fetched **once** into `designs/.fetched/`, when lead composes the
56
67
  phase, not at each comparison. The capture goes to `designs/.captures/` and is
57
68
  committed with the change, because `gh` cannot upload an image and a
@@ -35,6 +35,7 @@ Depth on what "handled" means — typed errors, propagation, what the user
35
35
  sees, degrading instead of crashing — in
36
36
  `.claude/skills/error-handling/SKILL.md`.
37
37
 
38
+ <!-- platforms: backend -->
38
39
  ## Structured logging
39
40
 
40
41
  Logs are data, not prose. JSON, one event per line.
@@ -57,6 +58,7 @@ Logs are data, not prose. JSON, one event per line.
57
58
  - Every line carries the release and environment, so a log and an error
58
59
  report line up against the same deploy.
59
60
 
61
+ <!-- platforms: backend -->
60
62
  ## Health is two endpoints, not one
61
63
 
62
64
  - **`/health`** — liveness. The process is up. Cheap, no dependencies.
@@ -76,6 +78,7 @@ anyway.
76
78
  that makes it fail; if you add a dependency, add its probe and its failing
77
79
  test in the same change.
78
80
 
81
+ <!-- platforms: backend -->
79
82
  ## Releasing
80
83
 
81
84
  There is no percentage canary on Railway. What protects a release is:
@@ -122,6 +122,7 @@ Where the runtime is single-threaded, a synchronous call on a request path
122
122
  stops every other request, not just this one. Blocking work there is a defect
123
123
  even when it is fast in isolation.
124
124
 
125
+ <!-- platforms: web, expo, ios, android -->
125
126
  ## Rendering and interaction, wherever the UI runs
126
127
 
127
128
  The words differ per platform; the constraints do not.
@@ -137,6 +138,7 @@ The words differ per platform; the constraints do not.
137
138
  - **Never block the thread that draws.** Move parsing, crypto, image work and
138
139
  large loops off it.
139
140
 
141
+ <!-- platforms: web -->
140
142
  ## The shipped size is a budget
141
143
 
142
144
  Whatever the user downloads or installs has a number, and the number only goes
@@ -144,6 +146,7 @@ down. `.claude/rules/testing-standards.orchestra.md` holds the ratchet: it may
144
146
  fall and may not rise, and deliberate growth is re-recorded in its own commit
145
147
  so it is a decision rather than a drift.
146
148
 
149
+ <!-- platforms: web, expo, backend -->
147
150
  ## What is enforced
148
151
 
149
152
  Only what can be decided precisely. Complexity, cache lifetime, index choice
@@ -181,6 +184,7 @@ as a library version.
181
184
  ## Depth
182
185
 
183
186
  `.claude/skills/code-optimizer/SKILL.md` is for optimising code that already
184
- exists. `.claude/skills/core-web-vitals/SKILL.md` is web-specific. Neither
187
+ exists. `.claude/skills/core-web-vitals/SKILL.md` is web-specific, and installed
188
+ only for web. Neither
185
189
  covers writing something performant the first time, which is what this rule is
186
190
  for.
@@ -1,3 +1,3 @@
1
1
  # Phase Limits
2
2
 
3
- **Scope guard:** Working on something NOT in phase acceptance criteria → STOP. Note in context.md, don't implement.
3
+ **Scope guard:** Working on something NOT in phase acceptance criteria → STOP. Name it in your result's notes, don't implement; lead records it with `.orchestra/scripts/context.mjs journal`.
@@ -1,3 +1,8 @@
1
+ ---
2
+ paths:
3
+ - ".github/**"
4
+ ---
5
+
1
6
  # Pull Requests
2
7
 
3
8
  **One task, one PR.** Always — there is no mode in which work reaches the
@@ -44,7 +49,7 @@ Open with a body file, never an inline string — the body contains newlines,
44
49
  a table, and backticks:
45
50
 
46
51
  ```bash
47
- gh pr create --title "#<task-id> - <subject>" --body-file .git/PR_BODY.md
52
+ gh pr create --base <delivery.base_branch> --title "#<task-id> - <subject>" --body-file .git/PR_BODY.md
48
53
  ```
49
54
 
50
55
  ## Naming
@@ -68,7 +73,7 @@ accepts `#<task-id> - <subject>` alongside the conventional form for exactly
68
73
  this case. Without that, the delivery flow would produce the one commit the
69
74
  repo's own gate rejects.
70
75
 
71
- Record the returned URL. Lead reports it and stores it in `context.md`, so a
76
+ Record the returned URL. Lead reports it and stores it in the phase's note (`context.mjs phase … "pr: <url>"`), so a
72
77
  later session can find the PR without searching.
73
78
 
74
79
  Reading the change is `gh pr diff <n>`, and a diff read straight into
@@ -1,3 +1,10 @@
1
+ ---
2
+ paths:
3
+ - ".orchestra/**"
4
+ - ".claude/**"
5
+ - "CLAUDE.md"
6
+ ---
7
+
1
8
  # Role Boundary Enforcement
2
9
 
3
10
  Before writing ANY file, check your role's ownership scope.
@@ -8,8 +15,8 @@ Before writing ANY file, check your role's ownership scope.
8
15
  |------|-----------|----------------|
9
16
  | Orchestrator | `.orchestra/roles/`, `.orchestra/config.yml`, `.orchestra/README.md`, `.orchestra/blueprints/`, `.orchestra/stacks/`, `.orchestra/trackers/`, `.orchestra/scripts/`, `.claude/agents/`, `.claude/hooks/`, `.claude/rules/*.orchestra.md`, `.claude/skills/*/SKILL.md`, `.claude/commands/orchestra/`, `CLAUDE.md`, `docs/` | Refuse |
10
17
  | PM | `.orchestra/milestones/*` (prd, milestone, grooming, phases) | Refuse |
11
- | Lead | `.orchestra/milestones/*/context.md` | Refuse |
12
- | Scaffolder | `.orchestra/config.yml` (verification, tracker, observability keys only), scaffolded app files, repo config | Refuse |
18
+ | Lead | `.orchestra/milestones/*/{context,decisions,journal}.md`, through `.orchestra/scripts/context.mjs` | Refuse |
19
+ | Scaffolder | `.orchestra/config.yml` (verification, tracker, observability, `delivery.base_branch`, `project.platforms` keys only), scaffolded app files, repo config | Refuse |
13
20
  | Tracker | Tracker id frontmatter in `.orchestra/milestones/*` | Refuse |
14
21
  | Sub-agents | Only what phase `## Scope` defines | Refuse |
15
22
  | Reviewer | Review verdict in phase file only | Refuse |
@@ -38,7 +45,7 @@ Two questions decide it, and neither is "which role is running": a slash
38
45
  command is a markdown prompt and cannot export anything a hook could read on
39
46
  its own, so a guard built only on the role would never fire.
40
47
 
41
- **Which repo is this?** In the orchestrator repo these files are source, and
48
+ **Which repo is this?** In the agentic-team repo these files are source, and
42
49
  the hook allows everything. Elsewhere they are build output.
43
50
 
44
51
  **Did Orchestra put this file here?** `.orchestra/.installed.json` records
@@ -63,7 +70,7 @@ Neither are `.orchestra/milestones/`, `blueprints/`, roles you create, or
63
70
  skills and rules you write: none of them is in the manifest, because the
64
71
  installer preserves them.
65
72
 
66
- When it blocks: make the change in the orchestrator repo and reinstall, or —
73
+ When it blocks: make the change in the agentic-team repo and reinstall, or —
67
74
  if it is genuinely project-specific — put it in the app's own `CLAUDE.md`, a
68
75
  project skill of your own, or `config.yml`, none of which is overwritten.
69
76
 
@@ -85,14 +92,22 @@ any `Agent` launch other than `milestone-reviewer` and `tracker`; any
85
92
  backlog one. The last three came from one afternoon: the word "ultracode" in
86
93
  a message told the platform to use the Workflow tool, and a PM whose role
87
94
  said "cannot review code" ran a multi-agent code review, recommended itself
88
- for it, and opened the task straight into `in progress`. Lead and the
89
- Orchestrator are recorded and, for now, not restricted — lead's sub-agents
90
- write code in the same session, and a restriction there would block the
91
- work it exists to do.
95
+ for it, and opened the task straight into `in progress`.
96
+
97
+ Lead is held to one rule: phase work starts inside a milestone agent. An
98
+ `engineer-*`, `reviewer` or `re-reviewer` launched from the lead session
99
+ itself is refused; one launched by a milestone agent passes, because a call
100
+ from a sub-agent carries `agent_id` and one from the session does not —
101
+ verified against a live payload. A hotfix engineer whose prompt starts with
102
+ `Hotfix:` passes. One run kept two milestones in the lead session and grew
103
+ to 738 calls and a 925k-token context. Sub-agents share the session id, so
104
+ the role marker reaches them; lead's own sub-agents still write code, which
105
+ is why nothing else is restricted. The Orchestrator is recorded and not
106
+ restricted.
92
107
 
93
108
  Two things are structural rather than hook-judged, because they do not
94
109
  depend on the role. `Workflow` is denied in the project's `settings.json`;
95
- no role runs one. The reviewer and the milestone reviewer carry
110
+ no role runs one. The reviewer, the re-reviewer and the milestone reviewer carry
96
111
  `disallowedTools: [Write, Edit, NotebookEdit]` in their frontmatter; they
97
112
  review, and Claude Code removes the tools rather than asking them not to.
98
113
 
@@ -52,7 +52,7 @@ staged changes, where it is still cheap to fix.
52
52
  `scripts/policy-check.mjs` runs in the gate. These are the checks it makes
53
53
  that belong to this rule — it decides others too;
54
54
  `.claude/rules/performance.orchestra.md` and
55
- `.claude/rules/comments.orchestra.md` each list their own:
55
+ `.claude/rules/comments.orchestra.md` each list their own where they apply:
56
56
 
57
57
  | Check | What fails |
58
58
  |---|---|
@@ -9,7 +9,7 @@ You are **stuck** when any of these happen:
9
9
  ## Recovery Protocol
10
10
 
11
11
  1. **STOP immediately.** Do not attempt another fix.
12
- 2. **Log** in context.md: phase name, symptom, attempts, root cause hypothesis.
12
+ 2. **Log** it: `node .orchestra/scripts/context.mjs journal <m> <phase> "stuck: <symptom>; tried <attempts>; likely <cause>"` — or, as a sub-agent, put it in your result's notes for lead to record.
13
13
  3. **Try a different approach** (ONE attempt):
14
14
  - Same code fix failed? → entirely different implementation strategy
15
15
  - Dependency broken? → alternative library
@@ -15,6 +15,7 @@ if the suite covers the seams where our code meets someone else's.
15
15
  Snapshot tests do not satisfy this. A snapshot passes while a handler
16
16
  silently stops firing — assert behaviour, not markup.
17
17
 
18
+ <!-- platforms: backend -->
18
19
  ## Backend — the shape
19
20
 
20
21
  The test pyramid is wrong for a backend. It comes from an era when starting
@@ -52,7 +53,7 @@ forbids this.
52
53
  - Each test creates the data it needs. A shared seed fixture is what makes
53
54
  a suite unmaintainable somewhere around test three hundred.
54
55
 
55
- ### Fake external services at the network boundary
56
+ ## Fake external services at the network boundary
56
57
 
57
58
  Intercept the HTTP call — MSW or a stub server. Do **not** mock your own
58
59
  `sendEmail()`.
@@ -60,7 +61,7 @@ Intercept the HTTP call — MSW or a stub server. Do **not** mock your own
60
61
  Mocking your own wrapper means the wrapper is never tested, and the wrapper
61
62
  is exactly where a provider's API change bites you.
62
63
 
63
- ### Test data
64
+ ## Test data
64
65
 
65
66
  Synthetic, built by factories, created per test. Never a copy of production
66
67
  data — it carries PII and it drifts.
@@ -94,6 +95,7 @@ Do not chase the global number with unit tests. If the integration tests
94
95
  are real, coverage arrives on its own. If tests are being written to move a
95
96
  percentage, the gate has started doing harm — say so rather than complying.
96
97
 
98
+ <!-- platforms: web, expo, backend -->
97
99
  ## Mutation testing
98
100
 
99
101
  Coverage says a line ran. Mutation says the line was **checked**.
@@ -113,6 +115,7 @@ On an app using TS 7 the scaffolder skips the install and says so — do not
113
115
  work around it by pinning TypeScript backwards. The gate turns on by itself
114
116
  once Stryker supports the new compiler.
115
117
 
118
+ <!-- platforms: web -->
116
119
  ## Web — jsdom is not a browser
117
120
 
118
121
  It has no layout engine. `getBoundingClientRect` returns zeros, nothing
@@ -137,6 +140,7 @@ item here.
137
140
 
138
141
  Depth in `.claude/skills/web-testing/SKILL.md`.
139
142
 
143
+ <!-- platforms: expo -->
140
144
  ## Mobile is a different shape
141
145
 
142
146
  Jest does not run React Native — `jest-expo` runs components in Node against
@@ -151,10 +155,9 @@ expo-doctor
151
155
  expo install --check
152
156
  ```
153
157
 
154
- Depth in `.claude/skills/mobile-testing/SKILL.md`. Native iOS and Android
155
- have their own shapes again — `.claude/skills/ios-testing/SKILL.md` and
156
- `.claude/skills/android-testing/SKILL.md`.
158
+ Depth in `.claude/skills/mobile-testing/SKILL.md`.
157
159
 
160
+ <!-- platforms: ios, android -->
158
161
  ## Native has two more shapes
159
162
 
160
163
  **iOS:** a green unit suite does not prove the app launches. A missing
@@ -175,6 +178,7 @@ accessibility instead of review noticing it.
175
178
  Depth in `.claude/skills/ios-testing/SKILL.md` and
176
179
  `.claude/skills/android-testing/SKILL.md`.
177
180
 
181
+ <!-- platforms: web -->
178
182
  ## The bundle has a budget, and it is a ratchet
179
183
 
180
184
  Same shape as the coverage ratchet: the committed baseline is whatever the
@@ -187,6 +191,7 @@ browser downloads.
187
191
  Deliberate growth is re-recorded on purpose, in its own commit, so it is a
188
192
  decision somebody made rather than a number that drifted.
189
193
 
194
+ <!-- platforms: web, expo, backend -->
190
195
  ## A build is part of the suite
191
196
 
192
197
  `tsc --noEmit` accepts an app that fails to bundle, and neither tests nor
@@ -1,3 +1,9 @@
1
+ ---
2
+ paths:
3
+ - ".orchestra/milestones/**"
4
+ - ".orchestra/trackers/**"
5
+ ---
6
+
1
7
  # Tracker Authority
2
8
 
3
9
  `tracker.provider` in `.orchestra/config.yml` decides who owns requirements.
@@ -24,7 +30,9 @@ The milestone directory holds pointers, and only pointers:
24
30
  tracker.yml committed provider, epic id, task ids, urls
25
31
  phases/*.md committed the PM's draft — what export pushes; lead never reads it
26
32
  .cache/ gitignored requirement text, fetched once per run
27
- context.md committed lead's execution log — local by nature
33
+ context.md committed lead's state: status, phases, codebase map
34
+ decisions.md committed lead's decisions, open until closed
35
+ journal.md committed what was tried and why it lost
28
36
  ```
29
37
 
30
38
  The draft is not a second source of truth, because nothing reads it but
@@ -17,7 +17,7 @@ arrives.
17
17
  Push needs a build with your own credentials, on a real device, with the app
18
18
  in each of three states — foreground, background, terminated. Anything less
19
19
  tests the mock. This is the same reason the device layer is not optional in
20
- `.claude/skills/mobile-testing/SKILL.md`.
20
+ `.claude/skills/mobile-testing/SKILL.md`, on Expo.
21
21
 
22
22
  Resolve the SDK's current API and constraints from its own docs at
23
23
  implementation time. This area changes across SDK releases, and a handler