agentic-team 0.0.0-stage → 6.2.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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +189 -2
  3. package/bin/build-template.js +208 -0
  4. package/bin/index.js +1334 -0
  5. package/bin/platforms.js +510 -0
  6. package/package.json +41 -4
  7. package/template/.claude-plugin/plugin.json +10 -0
  8. package/template/.orchestra/README.md +391 -0
  9. package/template/.orchestra/blueprints/README.md +87 -0
  10. package/template/.orchestra/blueprints/api-only.md +46 -0
  11. package/template/.orchestra/blueprints/component-crud-resource.md +25 -0
  12. package/template/.orchestra/blueprints/saas-starter.md +69 -0
  13. package/template/.orchestra/config.yml +293 -0
  14. package/template/.orchestra/roles/orchestrator.md +105 -0
  15. package/template/.orchestra/roles/product-manager.md +439 -0
  16. package/template/.orchestra/scripts/compact-diff.awk +45 -0
  17. package/template/.orchestra/scripts/context.mjs +373 -0
  18. package/template/.orchestra/stacks/README.md +81 -0
  19. package/template/.orchestra/stacks/_lib/common.sh +3095 -0
  20. package/template/.orchestra/stacks/_repo/setup.sh +1113 -0
  21. package/template/.orchestra/stacks/backend-hono/claude/commands/endpoint.md +13 -0
  22. package/template/.orchestra/stacks/backend-hono/claude/skills/api-endpoint/SKILL.md +111 -0
  23. package/template/.orchestra/stacks/backend-hono/claude/skills/testing/SKILL.md +75 -0
  24. package/template/.orchestra/stacks/backend-hono/setup.sh +5945 -0
  25. package/template/.orchestra/stacks/backend-hono/stack.md +195 -0
  26. package/template/.orchestra/stacks/mobile-android-native/claude/commands/component.md +13 -0
  27. package/template/.orchestra/stacks/mobile-android-native/claude/skills/design-component/SKILL.md +58 -0
  28. package/template/.orchestra/stacks/mobile-android-native/setup.sh +607 -0
  29. package/template/.orchestra/stacks/mobile-android-native/stack.md +138 -0
  30. package/template/.orchestra/stacks/mobile-expo/claude/commands/component.md +12 -0
  31. package/template/.orchestra/stacks/mobile-expo/claude/skills/design-component/SKILL.md +92 -0
  32. package/template/.orchestra/stacks/mobile-expo/setup.sh +470 -0
  33. package/template/.orchestra/stacks/mobile-expo/stack.md +141 -0
  34. package/template/.orchestra/stacks/mobile-ios-native/claude/commands/component.md +12 -0
  35. package/template/.orchestra/stacks/mobile-ios-native/claude/skills/design-component/SKILL.md +58 -0
  36. package/template/.orchestra/stacks/mobile-ios-native/setup.sh +468 -0
  37. package/template/.orchestra/stacks/mobile-ios-native/stack.md +130 -0
  38. package/template/.orchestra/stacks/web-nextjs/claude/commands/component.md +12 -0
  39. package/template/.orchestra/stacks/web-nextjs/claude/skills/design-component/SKILL.md +100 -0
  40. package/template/.orchestra/stacks/web-nextjs/claude/skills/design-tokens/SKILL.md +61 -0
  41. package/template/.orchestra/stacks/web-nextjs/setup.sh +128 -0
  42. package/template/.orchestra/stacks/web-nextjs/stack.md +170 -0
  43. package/template/.orchestra/stacks/web-vite-react/claude/commands/component.md +12 -0
  44. package/template/.orchestra/stacks/web-vite-react/claude/skills/design-component/SKILL.md +100 -0
  45. package/template/.orchestra/stacks/web-vite-react/claude/skills/design-tokens/SKILL.md +61 -0
  46. package/template/.orchestra/stacks/web-vite-react/setup.sh +185 -0
  47. package/template/.orchestra/stacks/web-vite-react/stack.md +177 -0
  48. package/template/.orchestra/trackers/github.sh +467 -0
  49. package/template/CLAUDE.md +54 -0
  50. package/template/agents/architect.md +83 -0
  51. package/template/agents/engineer-high.md +17 -0
  52. package/template/agents/engineer-low.md +17 -0
  53. package/template/agents/engineer-max.md +17 -0
  54. package/template/agents/engineer-medium.md +17 -0
  55. package/template/agents/engineer-xhigh.md +17 -0
  56. package/template/agents/lead.md +925 -0
  57. package/template/agents/milestone-reviewer.md +65 -0
  58. package/template/agents/orchestrator.md +105 -0
  59. package/template/agents/product-manager.md +439 -0
  60. package/template/agents/re-reviewer.md +25 -0
  61. package/template/agents/reviewer.md +303 -0
  62. package/template/agents/scaffolder.md +206 -0
  63. package/template/agents/tracker.md +202 -0
  64. package/template/commands/blueprint.md +23 -0
  65. package/template/commands/create-role.md +64 -0
  66. package/template/commands/export.md +45 -0
  67. package/template/commands/help.md +56 -0
  68. package/template/commands/hotfix.md +14 -0
  69. package/template/commands/orchestrator.md +12 -0
  70. package/template/commands/pm.md +18 -0
  71. package/template/commands/rewind.md +63 -0
  72. package/template/commands/setup.md +15 -0
  73. package/template/commands/start.md +29 -0
  74. package/template/commands/status.md +11 -0
  75. package/template/commands/sync.md +76 -0
  76. package/template/commands/verifier.md +53 -0
  77. package/template/hooks/audit-context.sh +166 -0
  78. package/template/hooks/enforce-role.sh +193 -0
  79. package/template/hooks/protect-system-files.sh +123 -0
  80. package/template/hooks/review-rounds.sh +172 -0
  81. package/template/rules/acceptance-check.orchestra.md +11 -0
  82. package/template/rules/analytics-kpi.orchestra.md +56 -0
  83. package/template/rules/code-standards.orchestra.md +35 -0
  84. package/template/rules/comments.orchestra.md +77 -0
  85. package/template/rules/commit-format.orchestra.md +20 -0
  86. package/template/rules/dependency-upgrade.orchestra.md +112 -0
  87. package/template/rules/design-parity.orchestra.md +181 -0
  88. package/template/rules/observability.orchestra.md +121 -0
  89. package/template/rules/performance.orchestra.md +190 -0
  90. package/template/rules/phase-limits.orchestra.md +3 -0
  91. package/template/rules/pull-request.orchestra.md +218 -0
  92. package/template/rules/role-boundaries.orchestra.md +132 -0
  93. package/template/rules/security.orchestra.md +106 -0
  94. package/template/rules/stuck-detection.orchestra.md +25 -0
  95. package/template/rules/testing-standards.orchestra.md +219 -0
  96. package/template/rules/tracker-authority.orchestra.md +198 -0
  97. package/template/rules/verification-gate.orchestra.md +56 -0
  98. package/template/skills/accessibility/SKILL.md +31 -0
  99. package/template/skills/android-testing/SKILL.md +112 -0
  100. package/template/skills/architecture/SKILL.md +175 -0
  101. package/template/skills/auth-setup/SKILL.md +105 -0
  102. package/template/skills/best-practices/SKILL.md +29 -0
  103. package/template/skills/code-optimizer/SKILL.md +27 -0
  104. package/template/skills/core-web-vitals/SKILL.md +32 -0
  105. package/template/skills/crud-api/SKILL.md +32 -0
  106. package/template/skills/debug/SKILL.md +28 -0
  107. package/template/skills/deployment/SKILL.md +104 -0
  108. package/template/skills/error-handling/SKILL.md +154 -0
  109. package/template/skills/frontend-design/SKILL.md +30 -0
  110. package/template/skills/fullstack-infrastructure/SKILL.md +108 -0
  111. package/template/skills/ios-testing/SKILL.md +100 -0
  112. package/template/skills/mobile-testing/SKILL.md +102 -0
  113. package/template/skills/observability/SKILL.md +91 -0
  114. package/template/skills/project-structure/SKILL.md +114 -0
  115. package/template/skills/push-notifications/SKILL.md +146 -0
  116. package/template/skills/react-best-practices/SKILL.md +31 -0
  117. package/template/skills/realtime/SKILL.md +146 -0
  118. package/template/skills/review/SKILL.md +82 -0
  119. package/template/skills/testing/SKILL.md +45 -0
  120. package/template/skills/web-quality-audit/SKILL.md +47 -0
  121. package/template/skills/web-testing/SKILL.md +120 -0
@@ -0,0 +1,121 @@
1
+ # Observability
2
+
3
+ **Mandatory.** An app without error monitoring is not shippable, and this
4
+ is not a launch-day task — it goes in before the first release, not after
5
+ the first incident.
6
+
7
+ ## Error monitoring
8
+
9
+ Every app reports uncaught errors and crashes to a monitoring service
10
+ before it reaches any real user.
11
+
12
+ - **Release tagging is required.** An error you cannot tie to a build is an
13
+ error you cannot fix. Every report carries the app version and commit.
14
+ - **Upload the symbols** — source maps for web and Node, dSYMs for iOS,
15
+ mapping files for Android. A minified stack trace tells you nothing.
16
+ - **Attach context**: route or screen, and a stable anonymous session id.
17
+ - **Never attach secrets or PII.** No tokens, no emails, no request bodies
18
+ that might contain either. Scrub before send, not after.
19
+
20
+ ## Swallowed errors are bugs
21
+
22
+ ```
23
+ catch (e) { } // defect
24
+ catch (e) { console.log(e) } // defect
25
+ ```
26
+
27
+ Every catch either **handles** the error meaningfully, or **reports** it and
28
+ rethrows. "Handled" means the user gets a correct outcome — not that the
29
+ crash was hidden from them.
30
+
31
+ The one exception is a genuinely optional path, and it carries a comment
32
+ saying why failure is acceptable.
33
+
34
+ Depth on what "handled" means — typed errors, propagation, what the user
35
+ sees, degrading instead of crashing — in
36
+ `.claude/skills/error-handling/SKILL.md`.
37
+
38
+ <!-- platforms: backend -->
39
+ ## Structured logging
40
+
41
+ Logs are data, not prose. JSON, one event per line.
42
+
43
+ - Levels used honestly: `error` means someone should look, `warn` means it
44
+ might become an error, `info` is a business event, `debug` is off in
45
+ production.
46
+ - Every server request carries a correlation id, and it appears in every
47
+ log line for that request. Without it you cannot follow a failure across
48
+ services. Take it from the caller's `x-request-id` when present, so a trace
49
+ that started upstream stays one trace, and echo it back so a caller can
50
+ quote it in a bug report.
51
+ - **Redaction is configured in the logger**, not trusted to every call site.
52
+ "Never log a token" holds until the one time somebody logs the whole
53
+ request object, and by then it is in the log store.
54
+ - No secrets, no PII, no full request bodies.
55
+ - One line per request, at completion, where status and duration are known.
56
+ Logging on the way in as well doubles the volume and answers nothing.
57
+ - Never log inside a hot loop.
58
+ - Every line carries the release and environment, so a log and an error
59
+ report line up against the same deploy.
60
+
61
+ <!-- platforms: backend -->
62
+ ## Health is two endpoints, not one
63
+
64
+ - **`/health`** — liveness. The process is up. Cheap, no dependencies.
65
+ - **`/ready`** — readiness. Every dependency this service cannot serve
66
+ without is reachable. Answers 503, naming the one that failed.
67
+
68
+ The platform's healthcheck points at **readiness**. On Railway the previous
69
+ deployment keeps serving until the new one answers 200, so a readiness probe
70
+ that returns 200 while the database is unreachable does not merely mislead a
71
+ dashboard — it promotes a broken release.
72
+
73
+ That makes readiness load-bearing infrastructure. Anything missing from the
74
+ checks map is a dependency that can be broken while a release is promoted
75
+ anyway.
76
+
77
+ **A readiness check that cannot fail is worse than none.** There is a test
78
+ that makes it fail; if you add a dependency, add its probe and its failing
79
+ test in the same change.
80
+
81
+ <!-- platforms: backend -->
82
+ ## Releasing
83
+
84
+ There is no percentage canary on Railway. What protects a release is:
85
+
86
+ 1. the healthcheck gating promotion,
87
+ 2. a smoke test against the real URL afterwards, and
88
+ 3. putting the last good commit back when that fails.
89
+
90
+ Rollback is the same path as a normal deploy, deliberately — an untested
91
+ recovery path is not a recovery path. The `last-good-deploy` tag moves only
92
+ after the smoke passes, so it always names a release observed working rather
93
+ than one that merely deployed.
94
+
95
+ Confidence comes from being able to undo, not from being sure.
96
+
97
+ ## Feature flags
98
+
99
+ A risky change ships dark and is turned off in seconds without a deploy.
100
+
101
+ The flag exists to protect the service, so a lookup must never be able to
102
+ take it down: bounded by a timeout, failures reported and answered with the
103
+ flag's own fallback rather than thrown into a handler. For a kill switch the
104
+ fallback is **false** — if we cannot ask whether a risky path is allowed, the
105
+ answer is no.
106
+
107
+ Flags live in one typed registry. A string literal at a call site is how they
108
+ rot: the flag is deleted, the call still compiles, and nobody finds out until
109
+ the behaviour quietly changes.
110
+
111
+ ## Defaults
112
+
113
+ Sentry for error monitoring, `pino` for structured logging in Node. Both
114
+ are defaults, not mandates — substitute freely. What is **not** optional is
115
+ that the capability exists and is wired before release.
116
+
117
+ Configure the provider in `.orchestra/config.yml` under `observability`.
118
+
119
+ `observability.require_before_release` (true by default) is checked by lead
120
+ at milestone completion. Setting it false defers the requirement — it does
121
+ not remove it.
@@ -0,0 +1,190 @@
1
+ # Performance
2
+
3
+ Performance is a property of the code as it is written, not a phase that
4
+ happens later. A task whose acceptance criteria say nothing about it still has
5
+ to meet what is below — the same way security and accessibility apply without
6
+ being restated per task.
7
+
8
+ Nothing here names a framework. What is fast on one platform is irrelevant on
9
+ another, and a rule that enumerates today's libraries is wrong within a year.
10
+ **Decide what applies from the code in front of you**, and reach for the
11
+ platform's own skill when you need depth.
12
+
13
+ ## Measure before claiming
14
+
15
+ The rule that outranks the rest of this file: **do not assert a performance
16
+ win you have not measured.**
17
+
18
+ An optimisation with no before-and-after is a guess that made the code harder
19
+ to read. State what was measured, what was assumed, and the numbers. When you
20
+ cannot measure, say *unmeasured* — that is the honest word, and
21
+ `.claude/rules/observability.orchestra.md` asks for it everywhere else too.
22
+
23
+ Two failures, and the second is the common one:
24
+
25
+ - optimising something that was never slow
26
+ - *making it slower* while believing otherwise, because the cheap-looking
27
+ change added work on a path that runs more often than the one it saved
28
+
29
+ ## Scale is the only question worth asking first
30
+
31
+ Before anything else: **how does this behave when the input grows?**
32
+
33
+ Everything is fast at n=10. The defect that matters is the one that is
34
+ invisible in development and fatal in production — and it is almost always
35
+ one of three shapes:
36
+
37
+ | Shape | Looks like |
38
+ |---|---|
39
+ | Work inside a loop that should have happened once | a query, a request, a file read, a compile, a sort |
40
+ | Complexity that is one order worse than it needs to be | a nested scan where a lookup table would do |
41
+ | Unbounded input | no limit, no pagination, no size cap, no timeout |
42
+
43
+ The first is the same defect whether the loop calls a database, an HTTP API,
44
+ the filesystem, or a native bridge. Naming it "N+1" is a database habit; the
45
+ pattern is general.
46
+
47
+ The third is the one that turns into an incident rather than a slow page.
48
+ Anything that accepts input from outside states its bound.
49
+
50
+ ## Do the work once
51
+
52
+ Batch, cache, precompute, reuse. In that order of preference, because each
53
+ step down adds a thing that can be stale.
54
+
55
+ A cache with no stated lifetime and no invalidation path is a correctness bug
56
+ scheduled for later. Say how long it lives and what clears it, next to it.
57
+
58
+ ## Do not pay for what you do not use
59
+
60
+ - Load it when it is needed, not at startup — code, data, connections,
61
+ images, permissions.
62
+ - Send the fields the caller asked for.
63
+ - Release what you hold. A subscription, a listener, a timer or a connection
64
+ that outlives its owner is a leak, and leaks read as "gets slower the longer
65
+ it runs" rather than as an error.
66
+
67
+ ## Memory is a question of lifetime, not of size
68
+
69
+ Every platform this system builds for has a collector — a tracing GC on the
70
+ JVM, V8 and Hermes, reference counting on Apple platforms. A collector frees
71
+ what is unreachable. So on a managed platform **a leak is never lost memory;
72
+ it is a reference that outlived its purpose** — a listener on a global, a
73
+ closure held in a long-lived map, a cache with no bound, a child holding its
74
+ parent, a timer nobody cleared. The collector cannot fix any of those,
75
+ because by its definition they are not garbage.
76
+
77
+ The tell is the axis the growth follows. Memory proportional to **load** is
78
+ a size problem — bound the input, stream, page. Memory proportional to
79
+ **uptime** is a leak, and it arrives as "gets slower the longer it runs,"
80
+ then as an out-of-memory in the one process that stayed up over the weekend.
81
+
82
+ **Whoever starts a lifetime ends it, in the same place.** A subscription, a
83
+ listener, a timer, a connection, a task: the code that creates it is the
84
+ code that names where it stops — the effect's cleanup, the `deinit`, the
85
+ scope's cancellation. A lifetime with no stated end is a leak with a delay.
86
+
87
+ **Allocation is deferred, not free.** A collector turns an allocation into a
88
+ later pause, and where the budget is a frame or a request, the pause lands
89
+ inside it. An object, closure or array created per frame, per row, or per
90
+ request is the shape to look for — it reads as ordinary code and is paid
91
+ for on the path that runs most often. Hoist it, reuse it, or make it a value
92
+ type.
93
+
94
+ **Test for it where the platform lets you.** A `deinit` that must fire, a
95
+ leak detector in the debug build, a heap that is the same size after ten
96
+ thousand iterations as after ten. A leak found in review is luck; one found
97
+ by a test is a rule.
98
+
99
+ ## Data and I/O, wherever the work is served
100
+
101
+ The tier most likely to fail at scale, and the one where the failure arrives
102
+ as an outage rather than as a slow screen.
103
+
104
+ - **One round trip, not one per row.** Fetch the set, join it, or batch it. A
105
+ loop that awaits is the shape to look for — it reads as ordinary code and
106
+ costs a full round trip per iteration.
107
+ - **Index what is filtered, joined and sorted on.** An index nobody measured
108
+ is a write cost with no read benefit, so check the query plan rather than
109
+ adding one by reflex.
110
+ - **Ask for the columns you use.** Selecting everything moves data you throw
111
+ away, through the driver, the network and the serialiser.
112
+ - **Every list endpoint has a hard maximum**, not just a default. A caller
113
+ passing a large enough page size is the same outage as no limit at all.
114
+ - **Every outbound call has a timeout**, and every pool has a ceiling. A
115
+ dependency that hangs should fail your request, not exhaust your process.
116
+ - **Stream what is large.** Buffering a response into memory to send it makes
117
+ the peak proportional to the biggest caller.
118
+ - **Keep slow work off the path that answers.** Hashing, image work, report
119
+ building and anything unbounded belong in a job, not in a request.
120
+
121
+ Where the runtime is single-threaded, a synchronous call on a request path
122
+ stops every other request, not just this one. Blocking work there is a defect
123
+ even when it is fast in isolation.
124
+
125
+ <!-- platforms: web, expo, ios, android -->
126
+ ## Rendering and interaction, wherever the UI runs
127
+
128
+ The words differ per platform; the constraints do not.
129
+
130
+ - **Re-render only what changed.** Anything drawn inside a list or under a
131
+ frequently-changing parent is the thing to look at, and allocating a new
132
+ object or closure per frame defeats whatever memoization sits above it.
133
+ - **Identity, not position.** A list keyed by index reuses the wrong element
134
+ with the wrong state when the list reorders. That is a correctness bug that
135
+ merely looks like a performance one.
136
+ - **Long lists are windowed.** Rendering ten thousand rows to show twelve is
137
+ the same waste on every platform.
138
+ - **Never block the thread that draws.** Move parsing, crypto, image work and
139
+ large loops off it.
140
+
141
+ <!-- platforms: web -->
142
+ ## The shipped size is a budget
143
+
144
+ Whatever the user downloads or installs has a number, and the number only goes
145
+ down. `.claude/rules/testing-standards.orchestra.md` holds the ratchet: it may
146
+ fall and may not rise, and deliberate growth is re-recorded in its own commit
147
+ so it is a decision rather than a drift.
148
+
149
+ <!-- platforms: web, expo, backend -->
150
+ ## What is enforced
151
+
152
+ Only what can be decided precisely. Complexity, cache lifetime, index choice
153
+ and unbounded input all matter more than the four below and none of them is
154
+ mechanically decidable — a gate that cries wolf is a gate people learn to
155
+ override, so they stay review items above.
156
+
157
+ | Check | Fails on |
158
+ |---|---|
159
+ | `await-in-loop` | a round trip per iteration |
160
+ | `array-index-key` | a list keyed by position |
161
+ | `inline-style-object` | an object allocated in a component kit's render |
162
+ | `unmemoized-kit-component` | a kit component memoizing nothing, where the toolchain is not doing it for you |
163
+ | `effect-without-cleanup` | a React effect that subscribes, listens or schedules and returns no cleanup |
164
+
165
+ These five are concrete because the stacks this system scaffolds are concrete.
166
+ They are the floor, not the definition of performance work.
167
+
168
+ `effect-without-cleanup` sees a lifetime start with no stated end — the
169
+ memory rule above, in the one place a script can see it. An effect that
170
+ genuinely needs none says so in a comment, and the check reads it.
171
+
172
+ `await-in-loop` has an escape hatch, because sequential really is the answer
173
+ sometimes — an ordered write, a rate limit, a cursor that depends on the
174
+ previous page. A comment above the loop saying which one it is clears the
175
+ check. Nothing else does: `Promise.all` over the batch is the fix in every
176
+ other case.
177
+
178
+ **Read the toolchain before applying the third.** React 19's compiler
179
+ memoizes automatically, so on a project with `reactCompiler` enabled,
180
+ hand-written `memo` is redundant noise and the check stays silent. Whether it
181
+ is on is a fact to read from the project, never recalled — the same discipline
182
+ as a library version.
183
+
184
+ ## Depth
185
+
186
+ `.claude/skills/code-optimizer/SKILL.md` is for optimising code that already
187
+ exists. `.claude/skills/core-web-vitals/SKILL.md` is web-specific, and installed
188
+ only for web. Neither
189
+ covers writing something performant the first time, which is what this rule is
190
+ for.
@@ -0,0 +1,3 @@
1
+ # Phase Limits
2
+
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`.
@@ -0,0 +1,218 @@
1
+ ---
2
+ paths:
3
+ - ".github/**"
4
+ ---
5
+
6
+ # Pull Requests
7
+
8
+ **One task, one PR.** Always — there is no mode in which work reaches the
9
+ user any other way.
10
+
11
+ One per milestone is the failure this rule exists to prevent, and it happened
12
+ anyway: the diff spans every phase, no reviewer can hold it, and the design
13
+ table has one row while the milestone has ten screens — so the screenshots get
14
+ dropped and nobody notices. A PR is one task, one commit, one screen.
15
+
16
+ ## Body
17
+
18
+ Use the repo's PR template. Keep it short — the reader wants to know what
19
+ changed and whether it is safe, not how you got there.
20
+
21
+ - **What** — one or two sentences
22
+ - **Why** — link the milestone or task
23
+ - **Design** — required for any UI change: expected and implemented, side by
24
+ side, in the template's table. Any deliberate difference is stated with
25
+ its reason. "Close enough" is not a reason.
26
+
27
+ One PR is one task is one screen, so the table has one row and an empty
28
+ cell has no excuse. The implemented image is the task's `design_capture`,
29
+ committed under `designs/.captures/` **in this branch** — `gh` cannot
30
+ upload an image, so a screenshot outside the branch cannot appear here. If
31
+ it could not be captured, the cell carries the reason. An empty cell reads
32
+ as a pass nobody observed.
33
+ - **Verification** — the checklist, actually checked
34
+
35
+ Do not paste the diff into the description. Do not narrate your process.
36
+ Do not paste the acceptance criteria. Read the body back as the colleague
37
+ who will review it: a table of files, a `path:line`, a list of what was
38
+ checked — those came from a tool, and the reader wanted a person.
39
+
40
+ ## Mechanism
41
+
42
+ The `gh` CLI. Check it before relying on it:
43
+
44
+ ```bash
45
+ gh auth status # not authenticated → stop and tell the user, do not work around it
46
+ ```
47
+
48
+ Open with a body file, never an inline string — the body contains newlines,
49
+ a table, and backticks:
50
+
51
+ ```bash
52
+ gh pr create --base <delivery.base_branch> --title "#<task-id> - <subject>" --body-file .git/PR_BODY.md
53
+ ```
54
+
55
+ ## Naming
56
+
57
+ | | Shape | Example |
58
+ |---|---|---|
59
+ | Branch | `<git-user>/<task-id>-<slug>` | `sulhadin/123-add-login` |
60
+ | PR title | `#<task-id> - <subject>` | `#123 - Add login` |
61
+
62
+ The id goes in both so the board finds the work without anyone pasting a link,
63
+ and it is the tracker's own id — a GitHub issue number or a ClickUp task id. With `tracker.provider: none` there is no id, so the branch drops
64
+ that segment and the title is the subject alone.
65
+
66
+ **The title is not conventional-commit form**, and that is deliberate. A task
67
+ PR accumulates commits — the implementation, then whatever review asked for —
68
+ and each of those *is* conventional. The squash collapses them, and its
69
+ subject is the one place the board's id has to appear.
70
+
71
+ That squashed subject still passes the scaffolded `commit-msg` hook, which
72
+ accepts `#<task-id> - <subject>` alongside the conventional form for exactly
73
+ this case. Without that, the delivery flow would produce the one commit the
74
+ repo's own gate rejects.
75
+
76
+ Record the returned URL. Lead reports it and stores it in the phase's note (`context.mjs phase … "pr: <url>"`), so a
77
+ later session can find the PR without searching.
78
+
79
+ Reading the change is `gh pr diff <n>`, and a diff read straight into
80
+ context spends four lines per file on the `diff --git`/`index`/`---`/`+++`
81
+ header. Pipe it — or any `git diff` / `git show` — through
82
+ `.orchestra/scripts/compact-diff.awk`; it replaces those four with one and
83
+ touches nothing else, so every hunk line comes through as git wrote it.
84
+
85
+ Reading review feedback is also `gh`, and it is two different calls — a
86
+ review body and inline line comments are separate objects, and reading only
87
+ one loses half the feedback:
88
+
89
+ ```bash
90
+ gh pr view <n> --json reviews,comments
91
+ gh api repos/{owner}/{repo}/pulls/<n>/comments
92
+ ```
93
+
94
+ Never poll in a loop. Fetch when the user says the review is ready.
95
+
96
+ ## Review cycle
97
+
98
+ The review happens **on the pull request**, not in a file beside it. Findings
99
+ land inline, on the line they are about, and the engineer answers them on the
100
+ thread. Nothing about the conversation lives only on somebody's disk.
101
+
102
+ The loop is: reviewer comments → a fresh engineer with the phase's identity
103
+ fixes, replies and resolves → re-review. Max rounds is `thresholds.stuck_retry_limit`,
104
+ counted by `.claude/hooks/review-rounds.sh`. On exhaustion the user decides
105
+ whether another round is worth it — say what is still open rather than going
106
+ round again.
107
+
108
+ **An engineer answers, not lead.** It reads the code, the diff and the thread
109
+ before it replies. A thread answered from a summary is a thread answered
110
+ wrong. It is a new engineer each round rather than the one that wrote the
111
+ code, because a resumed agent re-reads its whole history on every call.
112
+
113
+ ### Posting the review — one call, every finding
114
+
115
+ ```bash
116
+ cat > .git/REVIEW.json <<'JSON'
117
+ {
118
+ "event": "COMMENT",
119
+ "body": "changes-requested — two or three sentences",
120
+ "comments": [
121
+ { "path": "src/auth/session.ts", "line": 42, "side": "RIGHT",
122
+ "body": "[blocking] The session id is compared with ===, which is not constant time.\n\nUse `crypto.timingSafeEqual` over the two buffers." }
123
+ ]
124
+ }
125
+ JSON
126
+ gh api repos/{owner}/{repo}/pulls/<n>/reviews --input .git/REVIEW.json
127
+ ```
128
+
129
+ One review, not one call per finding — each separate call is a separate
130
+ notification.
131
+
132
+ **`COMMENT`, never `APPROVE` or `REQUEST_CHANGES`.** The same account authored
133
+ the pull request and GitHub refuses to let an author approve their own.
134
+
135
+ So the verdict travels as a **label**, which is the part GitHub will let the
136
+ author set and the part everything else can read — the board, a filter, a
137
+ human scanning the list, and the next agent.
138
+
139
+ | Label | Means |
140
+ |---|---|
141
+ | `review:changes-requested` | unresolved findings; the engineer is up |
142
+ | `review:approved` | nothing blocking; the task can move on |
143
+
144
+ ```bash
145
+ # once per repo, idempotent — the scaffold does this
146
+ gh label create review:changes-requested -c D93F0B -d "Reviewer found blocking issues" -f
147
+ gh label create review:approved -c 0E8A16 -d "Reviewer found nothing blocking" -f
148
+
149
+ # one state at a time, so the two can never both be on
150
+ gh pr edit <n> --add-label review:changes-requested --remove-label review:approved
151
+ ```
152
+
153
+ The label is the handshake between reviewer and engineer, and it is what lead
154
+ reads to decide the next move. The review body still carries the summary, but
155
+ a body has to be parsed and a label does not — and a run that dies between
156
+ rounds can pick up exactly where it left off by asking the pull request what
157
+ state it is in.
158
+
159
+ Every inline comment names the problem in a sentence and says what to do
160
+ instead. A comment that only objects makes the next agent guess.
161
+
162
+ ### Reading the threads
163
+
164
+ REST returns comments; only GraphQL carries the thread id and whether it is
165
+ resolved, and both are needed to answer one.
166
+
167
+ ```bash
168
+ gh api graphql -f query='
169
+ query($o:String!,$r:String!,$n:Int!) {
170
+ repository(owner:$o, name:$r) { pullRequest(number:$n) {
171
+ reviewThreads(first:50) { nodes {
172
+ id isResolved path line
173
+ comments(first:20) { nodes { body author { login } } }
174
+ } } } } }' -f o={owner} -f r={repo} -F n=<n>
175
+ ```
176
+
177
+ ### Answering and resolving
178
+
179
+ Reply on the thread, then resolve it. A few words on what was done and the
180
+ commit id — the diff says the rest.
181
+
182
+ ```bash
183
+ gh api graphql -f query='
184
+ mutation($t:ID!, $b:String!) {
185
+ addPullRequestReviewThreadReply(input:{pullRequestReviewThreadId:$t, body:$b}) {
186
+ comment { id } } }' -f t=<thread-id> -f b="Fixed with timingSafeEqual — a1b2c3d"
187
+
188
+ gh api graphql -f query='
189
+ mutation($t:ID!) {
190
+ resolveReviewThread(input:{threadId:$t}) { thread { isResolved } } }' -f t=<thread-id>
191
+ ```
192
+
193
+ Resolve only what you actually addressed. A thread resolved without a fix is
194
+ worse than one left open, because the next reader believes it.
195
+
196
+ If you did something other than what was asked, say why in the reply. If you
197
+ disagree, say so and leave the thread open — resolving a disagreement is not
198
+ answering it.
199
+
200
+ Never poll in a loop. A human review is fetched when the user says it is
201
+ ready.
202
+
203
+ ## Who merges
204
+
205
+ Decided by the `--auto` flag on `/orchestra:start`, and by nothing else.
206
+
207
+ | | Who merges | What follows |
208
+ |---|---|---|
209
+ | normal | **the human**, squashing | lead waits; a task whose `depends_on` is unmerged does not start |
210
+ | `--auto` | lead, squash-merging | commit link posted on the tracker task, status moved, branch deleted |
211
+
212
+ Outside `--auto`, **never merge your own PR unless explicitly told to.**
213
+
214
+ **Never stack a branch on an unmerged branch** to avoid waiting. The human
215
+ squashes, so the parent's commits reappear in the child's diff until it is
216
+ rebased, and an agent doing that on every task will get it wrong. Waiting is
217
+ the honest cost of reviewing per task — say what is open and what is blocked
218
+ rather than stalling silently.
@@ -0,0 +1,132 @@
1
+ ---
2
+ paths:
3
+ - ".orchestra/**"
4
+ - ".claude/**"
5
+ - "CLAUDE.md"
6
+ ---
7
+
8
+ # Role Boundary Enforcement
9
+
10
+ Before writing ANY file, check your role's ownership scope.
11
+
12
+ ## Ownership Map
13
+
14
+ | Role | Can Write | Everything Else |
15
+ |------|-----------|----------------|
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 |
17
+ | PM | `.orchestra/milestones/*` (prd, milestone, grooming, phases) | 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 |
20
+ | Tracker | Tracker id frontmatter in `.orchestra/milestones/*` | Refuse |
21
+ | Sub-agents | Only what phase `## Scope` defines | Refuse |
22
+ | Reviewer | Review verdict in phase file only | Refuse |
23
+
24
+ Some entries on the Orchestrator's list are **not** blocked by the hook below,
25
+ each for its own reason. `.orchestra/blueprints/` is where `/orchestra
26
+ blueprint` writes, `.orchestra/config.yml` is merged rather than overwritten,
27
+ and `CLAUDE.md` keeps everything outside the orchestra section for the user —
28
+ the installer preserves all three on upgrade, so an edit there is not lost
29
+ work. `docs/` is not installed into a project at all; it is this repo's own
30
+ documentation, so in an installed copy there is nothing under that name to
31
+ protect and blocking it would only block the user's own docs.
32
+
33
+ They stay on the ownership map because a role should still not reach into
34
+ them. They are simply not things a hook can decide.
35
+
36
+ ## Enforced, not asked for
37
+
38
+ Three `PreToolUse` hooks. The first blocks Write and Edit against installed
39
+ system files. The second holds a session inside the role it declared. The
40
+ third caps review rounds per phase.
41
+
42
+ ### The system-file hook
43
+
44
+ Two questions decide it, and neither is "which role is running": a slash
45
+ command is a markdown prompt and cannot export anything a hook could read on
46
+ its own, so a guard built only on the role would never fire.
47
+
48
+ **Which repo is this?** In the agentic-team repo these files are source, and
49
+ the hook allows everything. Elsewhere they are build output.
50
+
51
+ **Did Orchestra put this file here?** `.orchestra/.installed.json` records
52
+ every file the installer wrote, and the hook blocks exactly that list.
53
+
54
+ The manifest exists because the answer stopped being guessable. Rules carry a
55
+ `.orchestra.md` suffix, so a system rule can be told from one you wrote.
56
+ Skills lost that marker when they became directories, and a list of globs
57
+ cannot tell a shipped skill from yours — so it blocked both, including the
58
+ extension point `docs/skills.md` tells you to use, and including a stack's
59
+ own `apps/<name>/.claude/` pack. The upgrade had the same blindness from the
60
+ other side: a skill this project stops shipping looked like one you wrote, and
61
+ survived forever. The manifest answers both, and retires what a new version no
62
+ longer ships.
63
+
64
+ **`.orchestra/config.yml` is never blocked.** It is merged on upgrade rather
65
+ than overwritten, so an edit survives — and `/orchestra:setup` writes its
66
+ verification keys, which `.orchestra/README.md` has always documented as an
67
+ exception the hook did not implement.
68
+
69
+ Neither are `.orchestra/milestones/`, `blueprints/`, roles you create, or
70
+ skills and rules you write: none of them is in the manifest, because the
71
+ installer preserves them.
72
+
73
+ When it blocks: make the change in the agentic-team repo and reinstall, or —
74
+ if it is genuinely project-specific — put it in the app's own `CLAUDE.md`, a
75
+ project skill of your own, or `config.yml`, none of which is overwritten.
76
+
77
+ ### The role hook
78
+
79
+ The ownership map above was prose, and a PM opened a pull request. So the
80
+ role is now something a hook can read: each role command has the agent run
81
+ one Bash line — `true # orchestra-role=product-manager` — and the hook, which
82
+ sees every Bash call together with the session id Claude Code passes it,
83
+ records the pair under `.orchestra/.sessions/`. Every later call in that
84
+ session is judged against it. Two terminals are two sessions; the PM's marker
85
+ never touches lead.
86
+
87
+ What it refuses for the PM: a Write or Edit outside `.orchestra/milestones/`;
88
+ `gh pr` in any form; a new branch; merge, rebase, cherry-pick, a hard reset,
89
+ a force push; `git commit -a`; `git add` of anything but milestone files;
90
+ any `Agent` launch other than `milestone-reviewer` and `tracker`; any
91
+ `Workflow`; and a ClickUp task write carrying a status other than the
92
+ backlog one. The last three came from one afternoon: the word "ultracode" in
93
+ a message told the platform to use the Workflow tool, and a PM whose role
94
+ said "cannot review code" ran a multi-agent code review, recommended itself
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.
107
+
108
+ Two things are structural rather than hook-judged, because they do not
109
+ depend on the role. `Workflow` is denied in the project's `settings.json`;
110
+ no role runs one. The reviewer, the re-reviewer and the milestone reviewer carry
111
+ `disallowedTools: [Write, Edit, NotebookEdit]` in their frontmatter; they
112
+ review, and Claude Code removes the tools rather than asking them not to.
113
+
114
+ No marker means no role, and nothing is refused. That is discussion mode.
115
+
116
+ When it blocks, the message says why. Say so to the user and stop; the
117
+ answer is never to find a command the pattern does not match.
118
+
119
+ ### The review-round hook
120
+
121
+ `.claude/hooks/review-rounds.sh` counts reviewer launches per phase, from the
122
+ `Phase:` line lead puts in every reviewer prompt, in
123
+ `.orchestra/.sessions/review-rounds.json`. Past `thresholds.stuck_retry_limit`
124
+ it refuses the launch until the prompt quotes the user's approval. The cap
125
+ was prose, and a run read "loop until clean" as overriding it.
126
+
127
+ ## Rules
128
+
129
+ - Check what file you'd write to, not what the user said
130
+ - If user insists → still refuse: "Outside my scope as {role}. Use /orchestra:{command}."
131
+ - Discussion mode (no role) → no restrictions
132
+ - Reading → always allowed