@terreno/agent-plugin-loop 0.0.0-stage → 57.22.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.
@@ -0,0 +1,195 @@
1
+ # Scan contract
2
+
3
+ Aim, Sweep, Sift, Plot, and Track are **transitions**, not the orchestration loop.
4
+ `terreno-loop-campaign` is the invocable outer loop; it must never be recorded as a
5
+ `stage` value.
6
+
7
+ A scan is not a code change. Scan stages discover, prioritize, and shape work; the
8
+ Terreno lifecycle (`terreno-1-grow`, `terreno-2-pick`, `terreno-3-roast`,
9
+ `terreno-4-brew`, `terreno-5-taste`) implements and ships it. No scan stage edits
10
+ product code, commits, pushes, or opens a PR.
11
+
12
+ Two outer loops drive the stages. `terreno-loop-campaign` runs bounded rounds and stops
13
+ at each gate. `terreno-loop-run` stays resident: it keeps the campaign moving, routes
14
+ every PR to a named human per [`pr-routing.md`](pr-routing.md), and heartbeats over open
15
+ PRs per [`heartbeat.md`](heartbeat.md) so review comments, red CI, and merges are handled
16
+ without a person restarting anything.
17
+
18
+ | Owner | Responsibility |
19
+ | --- | --- |
20
+ | Campaign loop | when to invoke, which agent, persistence, retry, stop, escalation, lifecycle handoff |
21
+ | Resident loop | the same, plus PR routing, the heartbeat over open PRs, and scheduling |
22
+ | Scan stage | one bounded transition plus its evidence |
23
+ | Lifecycle stages | implementing, proving, submitting, and landing one plotted slice |
24
+ | Repository skills | repository commands, architecture, safety rules, and domain conventions |
25
+ | Campaign state | what a fresh invocation needs from previous rounds |
26
+
27
+ Every stage reads durable inputs, performs one bounded transition, writes campaign
28
+ state, emits one result document, and exits. Never rely on conversational memory.
29
+
30
+ ## Stages
31
+
32
+ | Stage | Transition | PASS next |
33
+ | --- | --- | --- |
34
+ | `aim` | Goal, metric, baseline, detection rules, exclusions, validity rule, severity rubric, slice policy; human approval | `sweep` |
35
+ | `sweep` | Shard the rule hits and map parallel workers over the shards into candidate findings | `sift` |
36
+ | `sift` | Reduce: dedupe, verify, drop unfounded, rank, write the findings report | `plot`, or `track` when the round is dry |
37
+ | `plot` | Turn ranked findings into PR-sized slices and hand the top slice to the lifecycle | `track` with `handoff: grow` |
38
+ | `track` | Re-measure the goal metric, close or reopen findings, record the round, choose the next move | `plot`, `sweep`, or `null` |
39
+
40
+ ## Goal before findings
41
+
42
+ A campaign exists to move one measurable goal, not to collect observations. Aim must
43
+ produce a metric with a command that prints it, a baseline captured by running that
44
+ command, and a target. Every later stage justifies its output by that metric:
45
+
46
+ - A detection rule that cannot plausibly move the metric does not belong in the charter.
47
+ - A finding that cannot be tied to the metric is dismissed in Sift, not deferred.
48
+ - A slice that lands without a metric re-measurement in Track is unproven work.
49
+
50
+ See [`goal-tracking.md`](goal-tracking.md).
51
+
52
+ ## Evidence
53
+
54
+ Every candidate and every finding carries reproducible evidence: a `path:line`
55
+ anchor plus the command, query, probe output, or artifact that produced it. Findings
56
+ without evidence are dropped in Sift, never carried forward with a caveat. Never persist
57
+ chain-of-thought, worker transcripts, or speculative narration.
58
+
59
+ ## Discover supporting skills
60
+
61
+ At the start of every stage:
62
+
63
+ 1. Inspect skills exposed by the harness and repository.
64
+ 2. Match their descriptions to **this round's rules, files, and findings**, not the
65
+ whole catalog.
66
+ 3. Load applicable `SKILL.md` files before acting and record their names in `skills`.
67
+ 4. If repository instructions require a capability and it is unavailable, return
68
+ `BLOCKED`; never silently skip it.
69
+ 5. If no skill applies, infer conventions from repository instructions, existing code,
70
+ tests, package scripts, and recent analogous changes.
71
+
72
+ The lifecycle plugin's stages perform their own discovery once a slice is handed off.
73
+ Scan stages do not pre-resolve implementation skills for them.
74
+
75
+ ## Map workers
76
+
77
+ Sweep spawns fresh workers with no parent memory. Each worker receives a
78
+ **shard briefing** and returns schema-valid findings only. Workers never reconstruct the
79
+ repository, never run a full-branch diff, never spawn nested workers, and never edit
80
+ files. See [`mapreduce.md`](mapreduce.md).
81
+
82
+ ## Documentation
83
+
84
+ Scan stages write scan artifacts (charter, findings report, round records). They do not
85
+ write product architecture docs. The plotted slices carry documentation work: every task
86
+ Plot writes names the docs to create or update, and the lifecycle's documentation
87
+ contract applies when that slice runs.
88
+
89
+ ## Stage result
90
+
91
+ The machine-readable result is for the next skill and the campaign loop. It is **not**
92
+ the human-facing answer.
93
+
94
+ Lead the chat with `status`, `next`, and `action` in one or two lines, plus the current
95
+ metric when the stage measured one. Put the YAML only in a collapsed details block.
96
+
97
+ The schema is [`scan-result.schema.json`](scan-result.schema.json) (`v: 2`). Required
98
+ keys are `v`, `stage`, `status`, `next`, and `action`. Omit nulls and empty arrays.
99
+ Status values are exactly `PASS`, `FAIL`, `BLOCKED`, or `PENDING`.
100
+
101
+ Wrap the YAML:
102
+
103
+ ```html
104
+ <details>
105
+ <summary>Scan result</summary>
106
+ </details>
107
+ ```
108
+
109
+ Example payload:
110
+
111
+ ```yaml
112
+ v: 2
113
+ stage: sift
114
+ status: PASS
115
+ goal: reduce-cold-build-time
116
+ round: 2
117
+ found: 31
118
+ kept: 12
119
+ metric:
120
+ value: 48.2
121
+ unit: s
122
+ ev: "bun run compile (cold): 48.2s"
123
+ artifacts:
124
+ - docs/scans/reduce-cold-build-time/round-2-findings.md
125
+ next: plot
126
+ action: Slice the four high-severity findings into the first PR-sized unit of work
127
+ ```
128
+
129
+ | Key | Meaning |
130
+ | --- | --- |
131
+ | `goal` | campaign goal slug |
132
+ | `round` | current sweep round |
133
+ | `metric` | measured metric value, unit, delta, and the command that produced it |
134
+ | `found` / `kept` / `closed` | candidate, surviving, and resolved finding counts |
135
+ | `skills` | supporting skills loaded |
136
+ | `artifacts` | charter, candidate file, findings report, slice briefs |
137
+ | `handoff` | lifecycle stage the campaign must invoke next (`grow` or `pick`) |
138
+ | `fail` | `need` / `want` / `got` / `ev` |
139
+ | `block` | `kind` / `why` / optional `ev` |
140
+ | `ask` | human questions: `q` / `rec` / optional `opts` |
141
+ | `next` | recommended next scan stage or `null` |
142
+ | `action` | concrete next action |
143
+ | `wait` | seconds until the campaign should check a handed-off slice again |
144
+
145
+ ## Campaign state
146
+
147
+ The charter and the findings report remain authoritative for scope and evidence.
148
+ Campaign state is a small loop-owned handoff, not a third plan.
149
+
150
+ Use an existing repository convention when present. Otherwise use
151
+ `.terreno/scan/<goal-slug>.json`, conforming to
152
+ [`scan-state.schema.json`](scan-state.schema.json). The campaign loop must preserve or
153
+ transport this file between fresh invocations. Do not commit it unless the repository
154
+ explicitly tracks scan state; lifecycle Brew must exclude loop-owned state from PR
155
+ commits.
156
+
157
+ Each invocation:
158
+
159
+ 1. Read state and verify its goal, round, branch, and findings against reality.
160
+ 2. Increment `attempt` for the invoked stage.
161
+ 3. Consume `last` and prior `tried` approaches; do not repeat a failed approach without
162
+ new evidence.
163
+ 4. Perform the stage.
164
+ 5. Replace `last`, merge artifact references, and set `next`.
165
+ 6. Emit the same result to the caller so the loop can persist it elsewhere.
166
+
167
+ These six operations are mandatory whenever a stage says "update campaign state." Every
168
+ result also includes a concrete `action`, even when `next` is `null`.
169
+
170
+ If no writable artifact exists, still emit the result. If the campaign loop cannot
171
+ preserve that result for the next fresh invocation, return `BLOCKED` and name the
172
+ missing state transport.
173
+
174
+ ## Failures, blockers, and pending
175
+
176
+ - `FAIL`: objective failure of this stage — an unproven detection rule, a malformed
177
+ candidate set, a findings report that contradicts its evidence. Preserve exact
178
+ evidence and recommend the smallest retry.
179
+ - `BLOCKED`: no safe action exists now. Classify `human`, `environment`, `access`, or
180
+ `external`; include the exact action or decision required.
181
+ - `PENDING`: a handed-off slice is still moving through the lifecycle, a PR is waiting on
182
+ review or CI, or a long measurement has not finished. Include `wait`; the **campaign or
183
+ resident loop** waits and invokes again. Prefer the harness's native completion hook or the provider's watch hook over
184
+ sleep polling; use a timer only when no hook applies.
185
+ - `PASS`: this stage's success conditions are proven for the recorded round.
186
+
187
+ Human gates include the goal statement and target, destructive or irreversible
188
+ remediation, public API or data-format changes surfaced by findings, scope growth beyond
189
+ the approved charter, and policy-required approval. Include options, tradeoffs,
190
+ evidence, and a recommended default.
191
+
192
+ A campaign requesting human input must first summarize goal state, metric movement,
193
+ completed rounds, decisive evidence, options and impact, and a recommended default. It
194
+ ends with one exact question, then the Demo section when a handed-off PR has deployment
195
+ URLs. Objective failures are not human gates while a concrete safe action remains.
@@ -0,0 +1,121 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://terreno.dev/schemas/scan-result.schema.json",
4
+ "title": "Terreno scan stage result",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["v", "stage", "status", "next", "action"],
8
+ "properties": {
9
+ "v": {"const": 2},
10
+ "stage": {"enum": ["aim", "sweep", "sift", "plot", "track"]},
11
+ "status": {"enum": ["PASS", "FAIL", "BLOCKED", "PENDING"]},
12
+ "goal": {"type": "string"},
13
+ "round": {"type": "integer", "minimum": 1},
14
+ "attempt": {"type": "integer", "minimum": 1},
15
+ "branch": {"type": "string"},
16
+ "sha": {"type": "string"},
17
+ "pr": {"type": "string"},
18
+ "skills": {"type": "array", "items": {"type": "string"}},
19
+ "rules": {"type": "array", "items": {"type": "string"}},
20
+ "shards": {"type": "integer", "minimum": 0},
21
+ "found": {"type": "integer", "minimum": 0},
22
+ "kept": {"type": "integer", "minimum": 0},
23
+ "closed": {"type": "integer", "minimum": 0},
24
+ "metric": {
25
+ "type": "object",
26
+ "additionalProperties": false,
27
+ "required": ["value", "ev"],
28
+ "properties": {
29
+ "value": {"type": "number"},
30
+ "unit": {"type": "string"},
31
+ "delta": {"type": "number"},
32
+ "ev": {"type": "string"}
33
+ }
34
+ },
35
+ "done": {"type": "array", "items": {"type": "string"}},
36
+ "artifacts": {"type": "array", "items": {"type": "string"}},
37
+ "slices": {
38
+ "type": "array",
39
+ "items": {
40
+ "type": "object",
41
+ "additionalProperties": false,
42
+ "required": ["slug", "findings", "status"],
43
+ "properties": {
44
+ "slug": {"type": "string"},
45
+ "ip": {"type": "string"},
46
+ "tasks": {"type": "string"},
47
+ "findings": {"type": "array", "items": {"type": "string"}},
48
+ "status": {"enum": ["planned", "running", "merged", "abandoned"]},
49
+ "pr": {"type": "string"}
50
+ }
51
+ }
52
+ },
53
+ "prs": {
54
+ "type": "array",
55
+ "items": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": ["pr", "bucket"],
59
+ "properties": {
60
+ "pr": {"type": "string"},
61
+ "bucket": {
62
+ "enum": ["unrouted", "answered", "broken", "bot-pending", "merged", "closed", "waiting"]
63
+ },
64
+ "who": {"type": "string"},
65
+ "slice": {"type": "string"}
66
+ }
67
+ }
68
+ },
69
+ "fail": {
70
+ "type": "array",
71
+ "items": {
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "required": ["need", "want", "got", "ev"],
75
+ "properties": {
76
+ "need": {"type": "string"},
77
+ "sev": {"enum": ["blocking", "non_blocking"]},
78
+ "want": {"type": "string"},
79
+ "got": {"type": "string"},
80
+ "ev": {"type": "string"}
81
+ }
82
+ }
83
+ },
84
+ "block": {
85
+ "type": "array",
86
+ "items": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "required": ["kind", "why"],
90
+ "properties": {
91
+ "kind": {"enum": ["human", "environment", "access", "external"]},
92
+ "why": {"type": "string"},
93
+ "ev": {"type": "string"}
94
+ }
95
+ }
96
+ },
97
+ "ask": {
98
+ "type": "array",
99
+ "items": {
100
+ "type": "object",
101
+ "additionalProperties": false,
102
+ "required": ["q", "rec"],
103
+ "properties": {
104
+ "q": {"type": "string"},
105
+ "opts": {"type": "array", "items": {"type": "string"}},
106
+ "rec": {"type": "string"}
107
+ }
108
+ }
109
+ },
110
+ "handoff": {
111
+ "type": ["string", "null"],
112
+ "enum": ["grow", "pick", null]
113
+ },
114
+ "next": {
115
+ "type": ["string", "null"],
116
+ "enum": ["aim", "sweep", "sift", "plot", "track", null]
117
+ },
118
+ "action": {"type": "string"},
119
+ "wait": {"type": "integer", "minimum": 1}
120
+ }
121
+ }
@@ -0,0 +1,199 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://terreno.dev/schemas/scan-state.schema.json",
4
+ "title": "Terreno scan campaign state",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["v", "goal", "stage", "round", "attempt", "next"],
8
+ "properties": {
9
+ "v": {"const": 2},
10
+ "goal": {
11
+ "type": "object",
12
+ "additionalProperties": false,
13
+ "required": ["slug", "statement", "metric", "baseline", "target"],
14
+ "properties": {
15
+ "slug": {"type": "string"},
16
+ "statement": {"type": "string"},
17
+ "charter": {"type": "string"},
18
+ "metric": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "required": ["name", "command", "unit", "direction"],
22
+ "properties": {
23
+ "name": {"type": "string"},
24
+ "command": {"type": "string"},
25
+ "unit": {"type": "string"},
26
+ "direction": {"enum": ["up", "down"]},
27
+ "protocol": {"type": "string"}
28
+ }
29
+ },
30
+ "baseline": {"type": "number"},
31
+ "target": {"type": "number"},
32
+ "current": {"type": "number"},
33
+ "horizon": {"type": "string"},
34
+ "review": {
35
+ "type": "object",
36
+ "additionalProperties": false,
37
+ "required": ["mode"],
38
+ "properties": {
39
+ "mode": {"enum": ["fixed", "codeowners", "blame", "round-robin", "none"]},
40
+ "reviewers": {"type": "array", "items": {"type": "string"}},
41
+ "assignees": {"type": "array", "items": {"type": "string"}},
42
+ "teams": {"type": "array", "items": {"type": "string"}},
43
+ "draft": {"type": "boolean"},
44
+ "labels": {"type": "array", "items": {"type": "string"}},
45
+ "wipLimit": {"type": "integer", "minimum": 1},
46
+ "mergePolicy": {"enum": ["human", "agent-after-approval"]},
47
+ "notify": {"type": "string"},
48
+ "rotation": {"type": "integer", "minimum": 0}
49
+ }
50
+ },
51
+ "budget": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "properties": {
55
+ "rounds": {"type": "integer", "minimum": 1},
56
+ "slicesPerRound": {"type": "integer", "minimum": 1},
57
+ "openPrs": {"type": "integer", "minimum": 1},
58
+ "minStep": {"type": "number"}
59
+ }
60
+ }
61
+ }
62
+ },
63
+ "stage": {"enum": ["aim", "sweep", "sift", "plot", "track"]},
64
+ "round": {"type": "integer", "minimum": 1},
65
+ "attempt": {"type": "integer", "minimum": 1},
66
+ "branch": {"type": "string"},
67
+ "skills": {"type": "array", "items": {"type": "string"}},
68
+ "rules": {
69
+ "type": "array",
70
+ "items": {
71
+ "type": "object",
72
+ "additionalProperties": false,
73
+ "required": ["name", "query", "matches", "why"],
74
+ "properties": {
75
+ "name": {"type": "string"},
76
+ "query": {"type": "string"},
77
+ "matches": {"type": "string"},
78
+ "why": {"type": "string"},
79
+ "exclude": {"type": "array", "items": {"type": "string"}},
80
+ "hits": {"type": "integer", "minimum": 0}
81
+ }
82
+ }
83
+ },
84
+ "exclude": {"type": "array", "items": {"type": "string"}},
85
+ "findings": {
86
+ "type": "array",
87
+ "items": {"$ref": "finding.schema.json"}
88
+ },
89
+ "slices": {
90
+ "type": "array",
91
+ "items": {
92
+ "type": "object",
93
+ "additionalProperties": false,
94
+ "required": ["slug", "findings", "status"],
95
+ "properties": {
96
+ "slug": {"type": "string"},
97
+ "ip": {"type": "string"},
98
+ "tasks": {"type": "string"},
99
+ "findings": {"type": "array", "items": {"type": "string"}},
100
+ "status": {"enum": ["planned", "running", "merged", "abandoned"]},
101
+ "branch": {"type": "string"},
102
+ "pr": {"type": "string"},
103
+ "reviewer": {"type": "string"},
104
+ "assignee": {"type": "string"},
105
+ "routedBy": {"enum": ["fixed", "codeowners", "blame", "round-robin", "none"]},
106
+ "bucket": {
107
+ "enum": ["unrouted", "answered", "broken", "bot-pending", "merged", "closed", "waiting"]
108
+ },
109
+ "answered": {"type": "array", "items": {"type": "string"}},
110
+ "closedReason": {"type": "string"},
111
+ "round": {"type": "integer", "minimum": 1}
112
+ }
113
+ }
114
+ },
115
+ "rounds": {
116
+ "type": "array",
117
+ "items": {
118
+ "type": "object",
119
+ "additionalProperties": false,
120
+ "required": ["round", "metricStart", "ev"],
121
+ "properties": {
122
+ "round": {"type": "integer", "minimum": 1},
123
+ "metricStart": {"type": "number"},
124
+ "metricEnd": {"type": "number"},
125
+ "found": {"type": "integer", "minimum": 0},
126
+ "kept": {"type": "integer", "minimum": 0},
127
+ "planned": {"type": "integer", "minimum": 0},
128
+ "fixed": {"type": "integer", "minimum": 0},
129
+ "dismissed": {"type": "integer", "minimum": 0},
130
+ "reopened": {"type": "integer", "minimum": 0},
131
+ "slices": {"type": "array", "items": {"type": "string"}},
132
+ "risks": {"type": "array", "items": {"type": "string"}},
133
+ "ev": {"type": "string"}
134
+ }
135
+ }
136
+ },
137
+ "artifacts": {"type": "array", "items": {"type": "string"}},
138
+ "heartbeat": {
139
+ "type": "object",
140
+ "additionalProperties": false,
141
+ "properties": {
142
+ "lastTick": {"type": "string"},
143
+ "interval": {"type": "integer", "minimum": 1},
144
+ "ticks": {"type": "integer", "minimum": 0},
145
+ "hostFailures": {"type": "integer", "minimum": 0},
146
+ "overrides": {
147
+ "type": "object",
148
+ "additionalProperties": false,
149
+ "properties": {
150
+ "reviewers": {"type": "array", "items": {"type": "string"}},
151
+ "assignee": {"type": "string"},
152
+ "wipLimit": {"type": "integer", "minimum": 1},
153
+ "rounds": {"type": "integer", "minimum": 1},
154
+ "until": {"type": "string"},
155
+ "tick": {"type": "integer", "minimum": 1}
156
+ }
157
+ }
158
+ }
159
+ },
160
+ "last": {
161
+ "anyOf": [
162
+ {"type": "null"},
163
+ {"$ref": "scan-result.schema.json"}
164
+ ]
165
+ },
166
+ "tried": {
167
+ "type": "array",
168
+ "items": {
169
+ "type": "object",
170
+ "additionalProperties": false,
171
+ "required": ["stage", "attempt", "why", "status", "ev"],
172
+ "properties": {
173
+ "stage": {"enum": ["aim", "sweep", "sift", "plot", "track"]},
174
+ "attempt": {"type": "integer", "minimum": 1},
175
+ "why": {"type": "string"},
176
+ "status": {"enum": ["PASS", "FAIL", "BLOCKED", "PENDING"]},
177
+ "ev": {"type": "string"}
178
+ }
179
+ }
180
+ },
181
+ "next": {
182
+ "type": "object",
183
+ "additionalProperties": false,
184
+ "required": ["stage", "why", "human"],
185
+ "properties": {
186
+ "stage": {
187
+ "type": ["string", "null"],
188
+ "enum": ["aim", "sweep", "sift", "plot", "track", null]
189
+ },
190
+ "handoff": {
191
+ "type": ["string", "null"],
192
+ "enum": ["grow", "pick", null]
193
+ },
194
+ "why": {"type": "string"},
195
+ "human": {"type": "boolean"}
196
+ }
197
+ }
198
+ }
199
+ }
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: 1-aim
3
+ description: Turn a long-term codebase goal into an approved scan charter — measurable metric, captured baseline, dry-run detection rules, exclusions, validity rule, severity rubric, and slice policy. Use when asked to scan a codebase for an outcome ("improve build time", "find N+1 queries", "reduce maintenance overhead", "make us WCAG-compliant"), to set a long-term engineering goal, or to start or reshape a scan campaign; not for implementation, PR operations, or one-off searches.
4
+ ---
5
+
6
+ # Aim — charter the goal
7
+
8
+ Turn "make the codebase better at X" into a charter a fresh Sweep agent can execute
9
+ without conversation history, and a metric that proves whether the campaign worked.
10
+
11
+ Read the shared [`scan contract`](../../references/scan-contract.md),
12
+ [`agentic map-reduce`](../../references/mapreduce.md),
13
+ [`goal tracking`](../../references/goal-tracking.md),
14
+ [`PR routing`](../../references/pr-routing.md), and the
15
+ [`charter template`](references/charter.md) before acting.
16
+
17
+ ## Preconditions
18
+
19
+ - A goal, outcome, or complaint plus a repository are available.
20
+ - This invocation owns charter shaping only. It never edits product code. After
21
+ `PASS`, Sweep runs the scan.
22
+
23
+ ## Inputs
24
+
25
+ - The requested outcome and any linked context
26
+ - Existing charter, campaign state, and prior findings when the goal already exists
27
+ - Repository instructions, code, tests, build scripts, CI config, and available skills
28
+
29
+ ## Procedure
30
+
31
+ 1. **Reconstruct.** Resolve the repository's scan conventions from its instructions and
32
+ nearby artifacts. If none exists, use `docs/scans/<goal-slug>/charter.md` for the
33
+ charter and `.terreno/scan/<goal-slug>.json` for campaign state, and record the new
34
+ convention. Read prior state and verify it against repository reality.
35
+ 2. **Read architecture docs.** Load the architecture, domain, and operator docs for the
36
+ areas the goal touches. They define what "correct" means before any rule is written.
37
+ 3. **Discover supporting skills.** Load repository/project skills matching the goal's
38
+ domains. Record them for downstream stages.
39
+ 4. **Make the goal measurable.** Derive the metric, its command, unit, direction,
40
+ target, horizon, and budget per `goal-tracking.md`. Run the metric command now and
41
+ quote its output as the baseline. If nothing measurable exists, grill for a proxy or
42
+ return `BLOCKED` naming the missing instrumentation — never a judgment-only metric.
43
+ 5. **Grill the human frontier.** Work one decision frontier at a time using selectable
44
+ options with a recommended default and the harness's structured question tool when it
45
+ has one. Settle: the outcome statement, the target and horizon, what is explicitly out
46
+ of scope, which trade-offs are unacceptable (for example "no public API breaks"), and
47
+ the budget. Vague or conflicting answers stay on the frontier.
48
+ 6. **Settle review routing.** Ask who reviews the campaign's PRs and record the `review`
49
+ block from `pr-routing.md`: mode, reviewer and assignee handles, teams, draft policy,
50
+ labels, the open-PR limit, the merge policy, and how to notify. Verify every handle is
51
+ requestable before writing it. "Whoever is free" is not an answer — resolve it to a
52
+ mode and a list, or to an explicit `mode: none`. This is the decision that makes an
53
+ unattended run safe to leave running.
54
+ 7. **Write detection rules.** For each rule record `name`, `query`, `matches`, `why`
55
+ (its link to the metric), and `exclude`. Dry-run every rule, record its hit count, and
56
+ remove or narrow rules that return nothing or flood. A rule that cannot plausibly move
57
+ the metric does not ship.
58
+ 8. **Define validity and severity.** State in one sentence what makes a finding valid,
59
+ and give the severity rubric concrete thresholds (`high` / `medium` / `low`) plus the
60
+ effort scale (`S` / `M` / `L`). Workers and Sift use these words verbatim.
61
+ 9. **Set the slice policy.** How findings become PR-sized units: maximum files and
62
+ findings per slice, what must never be mixed in one slice, and which findings require
63
+ their own slice because they are risky or reviewer-heavy.
64
+ 10. **Write and approve.** Produce the charter from the template, capture the baseline in
65
+ campaign state, and post the standalone approval brief: where the repository is today,
66
+ the goal and metric, the rules with their hit counts, the exclusions, the severity
67
+ rubric, the slice policy, and the decisions table pairing each settled decision with
68
+ the question that prompted it. Mark the charter approved only after human
69
+ confirmation. Update campaign state and emit the result collapsed per the scan
70
+ contract.
71
+
72
+ ## Supporting skills
73
+
74
+ Follow the shared discovery procedure. Useful skills typically cover build/test tooling,
75
+ static analysis, performance measurement, accessibility, data access, or security,
76
+ depending on the goal. Names are repository-defined; none is universally required.
77
+
78
+ ## Evidence produced
79
+
80
+ - Charter path and campaign-state path
81
+ - Metric definition plus the baseline command output, quoted
82
+ - Detection rules with dry-run hit counts
83
+ - Exclusions, validity rule, severity rubric, effort scale, slice policy
84
+ - Review block: mode, verified reviewer/assignee handles, draft and label policy,
85
+ open-PR limit, merge policy
86
+ - Decisions table with the question that prompted each decision
87
+ - Selected supporting skills and the approved `PASS` result
88
+
89
+ ## Success conditions
90
+
91
+ - The metric has a command that prints a number, and the baseline was measured here.
92
+ - Every rule is dry-run-proven with a recorded hit count and a stated link to the metric.
93
+ - A fresh Sweep invocation can shard and brief workers from the charter alone.
94
+ - Every campaign PR has a named, verified destination, or the charter states `mode: none`.
95
+ - A reviewer with no prior context can approve or push back from the brief alone.
96
+ - Emit `PASS` with `next: sweep`.
97
+
98
+ ## Failure conditions
99
+
100
+ An unmeasured baseline, an unproven rule, a missing validity rule, or a charter that
101
+ contradicts itself emits `FAIL` with exact defects, `next: aim`, and a focused retry.
102
+ Do not pass an unproven charter to Sweep.
103
+
104
+ ## Blocked conditions
105
+
106
+ Unresolved goal or target decisions, a metric the repository cannot produce, a reviewer
107
+ handle that cannot be verified, or missing access to build/measurement tooling emit
108
+ `BLOCKED` with `next: null`, options, tradeoffs, and a recommended default.
109
+
110
+ ## Recommended next stage
111
+
112
+ - `PASS` → Sweep
113
+ - `FAIL` → Aim with defect evidence
114
+ - `BLOCKED` → campaign loop routes the named human/external gate