bigpowers 2.77.1 → 2.78.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.
package/.pi/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.77.1",
4
- "description": "79 skills — 73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
3
+ "version": "2.78.0",
4
+ "description": "80 skills — 73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
5
5
  "keywords": [
6
6
  "pi-package"
7
7
  ],
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: find-way
3
+ description: "Plan a large effort as a shared map of decision tickets on an issue tracker, resolving them one at a time until the way is clear. Use when an idea is too big for one session, needs structured exploration before implementation, or requires mapping decisions before building."
4
+ ---
5
+
6
+ # Find Way
7
+
8
+ ## Concept
9
+
10
+ A large effort arrives — too big for one session, wrapped in fog. **Find-way** charts the way as a **shared map** on the repo's issue tracker, then works **decision tickets** — questions whose resolution is a decision, not build slices — one at a time until the route is clear.
11
+
12
+ **Plan, don't do.** Each ticket resolves a decision. The map is done when the way is clear — nothing left to decide before someone executes.
13
+
14
+ **Refer by name.** Every map and ticket is an issue with a title. Always use the name, never bare ids or numbers.
15
+
16
+ ## The Map
17
+
18
+ A single issue labelled `wayfinder:map` — the canonical artifact. Child issues are its tickets.
19
+
20
+ ```markdown
21
+ ## Destination
22
+ <what reaching the end looks like — one or two lines>
23
+
24
+ ## Notes
25
+ <domain, skills to consult, standing preferences>
26
+
27
+ ## Decisions so far
28
+ - [closed ticket title](link) — one-line gist
29
+
30
+ ## Not yet specified
31
+ <in-scope fog you can't ticket yet>
32
+
33
+ ## Out of scope
34
+ <work ruled beyond the destination>
35
+ ```
36
+
37
+ ## Ticket Types
38
+
39
+ | Type | Label | Mode | Purpose |
40
+ |------|-------|------|---------|
41
+ | Research | `wayfinder:research` | AFK | Surface facts from docs/APIs |
42
+ | Prototype | `wayfinder:prototype` | HITL | Cheap artifact to react to |
43
+ | Grilling | `wayfinder:grilling` | HITL | One-question-at-a-time dialogue |
44
+ | Task | `wayfinder:task` | Either | Manual work unblocking a decision |
45
+
46
+ **HITL** = human in the loop. **AFK** = agent alone.
47
+
48
+ ## Two Modes
49
+
50
+ ### Chart the Map
51
+
52
+ User invokes with a loose idea.
53
+
54
+ 1. **Name the destination** — grill to pin down what this map finds its way to
55
+ 2. **Map the frontier** — breadth-first: surface open decisions and first steps. If no fog emerges, the effort fits one session — skip the map
56
+ 3. **Create the map** issue (label `wayfinder:map`)
57
+ 4. **Create tickets** as child issues, then wire blocking edges in a second pass
58
+ 5. **Fire research subagents** for each `research` ticket in parallel
59
+ 6. Stop — charting resolves nothing
60
+
61
+ ### Work Through the Map
62
+
63
+ User invokes with a map (URL/number). Ticket optional — without one, pick the next frontier ticket.
64
+
65
+ 1. Load the map (low-res view)
66
+ 2. **Choose & claim** — assign ticket before any work
67
+ 3. **Resolve** — zoom into related/closed tickets as needed
68
+ 4. **Record** — post resolution comment, close issue, append to Decisions-so-far
69
+ 5. **Graduate fog** — create new tickets from newly-specifiable fog, clear from Not-yet-specified
70
+ 6. **Rule out of scope** if resolution reveals something past the destination
71
+
72
+ **Never resolve more than one ticket per session** (except research).
73
+
74
+ ## Fog of War
75
+
76
+ Beyond live tickets lies the **fog** — decisions you can tell are coming but can't pin down yet. The **Not yet specified** section holds this dim view.
77
+
78
+ **Fog or ticket?** Test: can you state the question precisely now?
79
+
80
+ - **Ticket** when the question is sharp (even if blocked)
81
+ - **Not yet specified** when you can't phrase it sharply yet
82
+
83
+ ## Out of Scope
84
+
85
+ Work beyond the destination. Ruling something out is a scoping act, not a route step. Never graduates — returns only if destination is redrawn.
86
+
87
+ If an existing ticket sits past the destination, close it and note in Out of scope.
88
+
89
+ ## Blocking & Frontier
90
+
91
+ - **Blocking**: tracker's native dependency relationship
92
+ - **Unblocked**: every blocker is closed
93
+ - **Frontier**: open, unblocked, unclaimed children — the edge of the known
94
+
95
+ ## Detailed Reference
96
+
97
+ See [REFERENCE.md](REFERENCE.md) for ticket anatomy, resolution protocol, and examples.
98
+
99
+ ---
100
+
101
+ # Find-Way Reference
102
+
103
+ ## Ticket Anatomy
104
+
105
+ Each ticket holds one question, sized to one 100K token agent session:
106
+
107
+ ```markdown
108
+ ## Question
109
+
110
+ <the decision or investigation this ticket resolves>
111
+ ```
112
+
113
+ **Labels:** `wayfinder:research`, `wayfinder:prototype`, `wayfinder:grilling`, or `wayfinder:task`
114
+
115
+ **Claim:** Assign to yourself before any work — claim blocks concurrent sessions.
116
+
117
+ **Blocking:** Use tracker's native dependency relationship so the frontier renders visually.
118
+
119
+ **Resolution:** Post answer as a comment, close the issue, append to map's Decisions-so-far.
120
+
121
+ ## Resolution Protocol
122
+
123
+ 1. **Load the map** — read low-res view, not every ticket body
124
+ 2. **Choose & claim** — assign ticket to yourself (or take first frontier ticket if not named)
125
+ 3. **Resolve** — zoom into related/closed tickets as needed; invoke skills the Notes block names
126
+ 4. **Record** — post resolution comment with the answer, close the issue
127
+ 5. **Update map** — append to Decisions-so-far with one-line gist + link
128
+ 6. **Graduate fog** — create new tickets from newly-specifiable fog; clear from Not-yet-specified
129
+ 7. **Rule out of scope** if resolution reveals something past the destination
130
+
131
+ ## Ticket Types in Detail
132
+
133
+ ### Research (AFK)
134
+ Surfacing facts from docs, APIs, or knowledge bases that a decision waits on.
135
+ - Resolved by a `/research` subagent
136
+ - Use when knowledge outside the working directory is required
137
+ - **Label:** `wayfinder:research`
138
+
139
+ ### Prototype (HITL)
140
+ Raise fidelity by building cheap, rough, concrete artifact to react to — outline, stub, UI/logic code.
141
+ - Use the `/prototype` skill
142
+ - Links the prototype as an asset
143
+ - Use when "how should it look" or "how should it behave" is key
144
+ - **Label:** `wayfinder:prototype`
145
+
146
+ ### Grilling (HITL)
147
+ Conversation via `/grilling` and `/domain-modeling` skills, one question at a time.
148
+ - The default case for clarifying decisions
149
+ - **Label:** `wayfinder:grilling`
150
+
151
+ ### Task (HITL or AFK)
152
+ Manual work unblocking a decision — signing up for service, provisioning access, moving data.
153
+ - This is the **only** type that does rather than decides
154
+ - Earns its place by unblocking a decision, not by delivering the destination
155
+ - Agent drives AFK tasks alone; HITL tasks get precise checklist
156
+ - **Label:** `wayfinder:task`
157
+
158
+ ## Fog-to-Ticket Graduation
159
+
160
+ When resolving a ticket clears fog, graduate the newly-specifiable bits into fresh tickets:
161
+
162
+ 1. **Identify the fog** in Not-yet-specified that the resolution illuminates
163
+ 2. **Create new tickets** with crisp question statements
164
+ 3. **Wire blocking edges** if the new tickets depend on each other or existing ones
165
+ 4. **Clear from fog** — remove the graduated patch from Not-yet-specified so it lives only as its ticket
166
+
167
+ ## Out-of-Scope Close Protocol
168
+
169
+ If a ticket turns out to sit past the destination:
170
+
171
+ 1. **Close the ticket** — mark it resolved or won't-fix
172
+ 2. **Add one line to Out of scope** — gist + why it's out of scope, linking the closed ticket
173
+ 3. **Never append to Decisions so far** — scope boundaries aren't steps on the route
174
+
175
+ ## Map State Transitions
176
+
177
+ | State | Meaning | Action |
178
+ |-------|---------|--------|
179
+ | **Open, unblocked, unclaimed** | Frontier — ready to take | Claim and resolve |
180
+ | **Open, blocked** | Waiting on something | Wait for blockers to close |
181
+ | **Open, claimed** | Concurrent session working | Skip for now |
182
+ | **Closed** | Decided — lives in Decisions so far | Reference, don't re-open |
183
+
184
+ ## Concurrency
185
+
186
+ Users may run unblocked tickets in parallel, so expect the tracker to be edited concurrently. Always:
187
+
188
+ - Refresh the map before picking a ticket
189
+ - Claim immediately before starting work
190
+ - Never assume state hasn't changed since last read
@@ -69,7 +69,9 @@ gate_trace:
69
69
  ## Integration Points
70
70
 
71
71
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
72
- - **sync-skills.yml** — CI gate (FAIL blocks pipeline).
72
+ - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
73
+ blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
74
+ does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
73
75
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
74
76
 
75
77
  ## References
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: find-way
3
+ model: sonnet
4
+ description: "Plan a large effort as a shared map of decision tickets on an issue tracker, resolving them one at a time until the way is clear. Use when an idea is too big for one session, needs structured exploration before implementation, or requires mapping decisions before building."
5
+ ---
6
+
7
+ # Find Way
8
+
9
+ ## Concept
10
+
11
+ A large effort arrives — too big for one session, wrapped in fog. **Find-way** charts the way as a **shared map** on the repo's issue tracker, then works **decision tickets** — questions whose resolution is a decision, not build slices — one at a time until the route is clear.
12
+
13
+ **Plan, don't do.** Each ticket resolves a decision. The map is done when the way is clear — nothing left to decide before someone executes.
14
+
15
+ **Refer by name.** Every map and ticket is an issue with a title. Always use the name, never bare ids or numbers.
16
+
17
+ ## The Map
18
+
19
+ A single issue labelled `wayfinder:map` — the canonical artifact. Child issues are its tickets.
20
+
21
+ ```markdown
22
+ ## Destination
23
+ <what reaching the end looks like — one or two lines>
24
+
25
+ ## Notes
26
+ <domain, skills to consult, standing preferences>
27
+
28
+ ## Decisions so far
29
+ - [closed ticket title](link) — one-line gist
30
+
31
+ ## Not yet specified
32
+ <in-scope fog you can't ticket yet>
33
+
34
+ ## Out of scope
35
+ <work ruled beyond the destination>
36
+ ```
37
+
38
+ ## Ticket Types
39
+
40
+ | Type | Label | Mode | Purpose |
41
+ |------|-------|------|---------|
42
+ | Research | `wayfinder:research` | AFK | Surface facts from docs/APIs |
43
+ | Prototype | `wayfinder:prototype` | HITL | Cheap artifact to react to |
44
+ | Grilling | `wayfinder:grilling` | HITL | One-question-at-a-time dialogue |
45
+ | Task | `wayfinder:task` | Either | Manual work unblocking a decision |
46
+
47
+ **HITL** = human in the loop. **AFK** = agent alone.
48
+
49
+ ## Two Modes
50
+
51
+ ### Chart the Map
52
+
53
+ User invokes with a loose idea.
54
+
55
+ 1. **Name the destination** — grill to pin down what this map finds its way to
56
+ 2. **Map the frontier** — breadth-first: surface open decisions and first steps. If no fog emerges, the effort fits one session — skip the map
57
+ 3. **Create the map** issue (label `wayfinder:map`)
58
+ 4. **Create tickets** as child issues, then wire blocking edges in a second pass
59
+ 5. **Fire research subagents** for each `research` ticket in parallel
60
+ 6. Stop — charting resolves nothing
61
+
62
+ ### Work Through the Map
63
+
64
+ User invokes with a map (URL/number). Ticket optional — without one, pick the next frontier ticket.
65
+
66
+ 1. Load the map (low-res view)
67
+ 2. **Choose & claim** — assign ticket before any work
68
+ 3. **Resolve** — zoom into related/closed tickets as needed
69
+ 4. **Record** — post resolution comment, close issue, append to Decisions-so-far
70
+ 5. **Graduate fog** — create new tickets from newly-specifiable fog, clear from Not-yet-specified
71
+ 6. **Rule out of scope** if resolution reveals something past the destination
72
+
73
+ **Never resolve more than one ticket per session** (except research).
74
+
75
+ ## Fog of War
76
+
77
+ Beyond live tickets lies the **fog** — decisions you can tell are coming but can't pin down yet. The **Not yet specified** section holds this dim view.
78
+
79
+ **Fog or ticket?** Test: can you state the question precisely now?
80
+
81
+ - **Ticket** when the question is sharp (even if blocked)
82
+ - **Not yet specified** when you can't phrase it sharply yet
83
+
84
+ ## Out of Scope
85
+
86
+ Work beyond the destination. Ruling something out is a scoping act, not a route step. Never graduates — returns only if destination is redrawn.
87
+
88
+ If an existing ticket sits past the destination, close it and note in Out of scope.
89
+
90
+ ## Blocking & Frontier
91
+
92
+ - **Blocking**: tracker's native dependency relationship
93
+ - **Unblocked**: every blocker is closed
94
+ - **Frontier**: open, unblocked, unclaimed children — the edge of the known
95
+
96
+ ## Detailed Reference
97
+
98
+ See [REFERENCE.md](../../../skills/find-way/REFERENCE.md) for ticket anatomy, resolution protocol, and examples.
99
+
100
+ ---
101
+
102
+ # Find-Way Reference
103
+
104
+ ## Ticket Anatomy
105
+
106
+ Each ticket holds one question, sized to one 100K token agent session:
107
+
108
+ ```markdown
109
+ ## Question
110
+
111
+ <the decision or investigation this ticket resolves>
112
+ ```
113
+
114
+ **Labels:** `wayfinder:research`, `wayfinder:prototype`, `wayfinder:grilling`, or `wayfinder:task`
115
+
116
+ **Claim:** Assign to yourself before any work — claim blocks concurrent sessions.
117
+
118
+ **Blocking:** Use tracker's native dependency relationship so the frontier renders visually.
119
+
120
+ **Resolution:** Post answer as a comment, close the issue, append to map's Decisions-so-far.
121
+
122
+ ## Resolution Protocol
123
+
124
+ 1. **Load the map** — read low-res view, not every ticket body
125
+ 2. **Choose & claim** — assign ticket to yourself (or take first frontier ticket if not named)
126
+ 3. **Resolve** — zoom into related/closed tickets as needed; invoke skills the Notes block names
127
+ 4. **Record** — post resolution comment with the answer, close the issue
128
+ 5. **Update map** — append to Decisions-so-far with one-line gist + link
129
+ 6. **Graduate fog** — create new tickets from newly-specifiable fog; clear from Not-yet-specified
130
+ 7. **Rule out of scope** if resolution reveals something past the destination
131
+
132
+ ## Ticket Types in Detail
133
+
134
+ ### Research (AFK)
135
+ Surfacing facts from docs, APIs, or knowledge bases that a decision waits on.
136
+ - Resolved by a `/research` subagent
137
+ - Use when knowledge outside the working directory is required
138
+ - **Label:** `wayfinder:research`
139
+
140
+ ### Prototype (HITL)
141
+ Raise fidelity by building cheap, rough, concrete artifact to react to — outline, stub, UI/logic code.
142
+ - Use the `/prototype` skill
143
+ - Links the prototype as an asset
144
+ - Use when "how should it look" or "how should it behave" is key
145
+ - **Label:** `wayfinder:prototype`
146
+
147
+ ### Grilling (HITL)
148
+ Conversation via `/grilling` and `/domain-modeling` skills, one question at a time.
149
+ - The default case for clarifying decisions
150
+ - **Label:** `wayfinder:grilling`
151
+
152
+ ### Task (HITL or AFK)
153
+ Manual work unblocking a decision — signing up for service, provisioning access, moving data.
154
+ - This is the **only** type that does rather than decides
155
+ - Earns its place by unblocking a decision, not by delivering the destination
156
+ - Agent drives AFK tasks alone; HITL tasks get precise checklist
157
+ - **Label:** `wayfinder:task`
158
+
159
+ ## Fog-to-Ticket Graduation
160
+
161
+ When resolving a ticket clears fog, graduate the newly-specifiable bits into fresh tickets:
162
+
163
+ 1. **Identify the fog** in Not-yet-specified that the resolution illuminates
164
+ 2. **Create new tickets** with crisp question statements
165
+ 3. **Wire blocking edges** if the new tickets depend on each other or existing ones
166
+ 4. **Clear from fog** — remove the graduated patch from Not-yet-specified so it lives only as its ticket
167
+
168
+ ## Out-of-Scope Close Protocol
169
+
170
+ If a ticket turns out to sit past the destination:
171
+
172
+ 1. **Close the ticket** — mark it resolved or won't-fix
173
+ 2. **Add one line to Out of scope** — gist + why it's out of scope, linking the closed ticket
174
+ 3. **Never append to Decisions so far** — scope boundaries aren't steps on the route
175
+
176
+ ## Map State Transitions
177
+
178
+ | State | Meaning | Action |
179
+ |-------|---------|--------|
180
+ | **Open, unblocked, unclaimed** | Frontier — ready to take | Claim and resolve |
181
+ | **Open, blocked** | Waiting on something | Wait for blockers to close |
182
+ | **Open, claimed** | Concurrent session working | Skip for now |
183
+ | **Closed** | Decided — lives in Decisions so far | Reference, don't re-open |
184
+
185
+ ## Concurrency
186
+
187
+ Users may run unblocked tickets in parallel, so expect the tracker to be edited concurrently. Always:
188
+
189
+ - Refresh the map before picking a ticket
190
+ - Claim immediately before starting work
191
+ - Never assume state hasn't changed since last read
@@ -70,7 +70,9 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — CI gate (FAIL blocks pipeline).
73
+ - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
+ blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
75
+ does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
74
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
75
77
 
76
78
  ## References
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ # [2.78.0](https://github.com/danielvm-git/bigpowers/compare/v2.77.2...v2.78.0) (2026-07-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **ci:** make G-11 venv-ignore check independent of .venv's on-disk existence ([91f550d](https://github.com/danielvm-git/bigpowers/commit/91f550d79e408e5734820fb896e8c942fabbf41d))
7
+ * **scripts:** defer type-hint evaluation in blind-spots.py for Python 3.9 ([6356b87](https://github.com/danielvm-git/bigpowers/commit/6356b87167d59422ed75eea0e933229a13b78c12))
8
+ * **scripts:** repair completeness-critic's story-tag check ([5e336ea](https://github.com/danielvm-git/bigpowers/commit/5e336eadc260cb6752cc260f58946b1eddf2c1ee))
9
+ * **scripts:** stop sync-status-from-epics.sh corrupting status on multi-line epic entries ([e23a3be](https://github.com/danielvm-git/bigpowers/commit/e23a3be1a1338ebb2f4393801fb3c74fbdae49ca))
10
+ * **scripts:** unblock compliance gate and repair silently-passing capsule validator ([608d76a](https://github.com/danielvm-git/bigpowers/commit/608d76a428dc3d1d9015236577905a446d8155b8))
11
+ * **skills:** add missing model: frontmatter to find-way, resolving skill-count divergence ([ddb7655](https://github.com/danielvm-git/bigpowers/commit/ddb76556e56c269fca6380e0b94d7d53d90c1ccf))
12
+
13
+
14
+ ### Features
15
+
16
+ * **e53:** kick off establish-migration-baseline epic ([e74aeca](https://github.com/danielvm-git/bigpowers/commit/e74aecad771284841a59f226fd6c2eb827922553))
17
+ * **planning:** elaborate e55-e59 migration epics into scoped planning-context ([25fb6ff](https://github.com/danielvm-git/bigpowers/commit/25fb6ff5e5cbb1d83acc0341be679666fd57adbd))
18
+ * **release-plan:** sequence e54-e59, the remaining bigspec migration epics ([b6af03d](https://github.com/danielvm-git/bigpowers/commit/b6af03dbc7d19f9ac56b0abf2fa9997c83bacddc))
19
+ * **skills:** add find-way skill for large effort planning ([6b7d27e](https://github.com/danielvm-git/bigpowers/commit/6b7d27e9d007b45b5dba4db688cfd3b135ca72d5))
20
+ * **skills:** add find-way skill with complete reference documentation ([7aa9e0d](https://github.com/danielvm-git/bigpowers/commit/7aa9e0d769aca621071e2a719a0dc12db5a9c28f))
21
+
22
+ ## [2.77.2](https://github.com/danielvm-git/bigpowers/compare/v2.77.1...v2.77.2) (2026-07-19)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **scripts:** fall back to simple_yaml when PyYAML is absent during setup ([#81](https://github.com/danielvm-git/bigpowers/issues/81)) ([2608c64](https://github.com/danielvm-git/bigpowers/commit/2608c645e1143f1bbf96c3174341b107c672ae8e))
28
+
1
29
  ## [2.77.1](https://github.com/danielvm-git/bigpowers/compare/v2.77.0...v2.77.1) (2026-07-19)
2
30
 
3
31
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
4
4
  ![npm version](https://img.shields.io/npm/v/bigpowers.svg)
5
- ![Skills](https://img.shields.io/badge/skills-79-brightgreen.svg)
5
+ ![Skills](https://img.shields.io/badge/skills-80-brightgreen.svg)
6
6
 
7
7
  **Agent skills synthesizing 17 years of software engineering discipline — from Clean Code to AI-native architecture — into a single, prescriptive methodology for solo developers.**
8
8
 
package/SKILL-INDEX.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > **DO NOT EDIT** — This file is auto-generated by `scripts/generate-skill-index.sh`.
4
4
  > Edit `SKILL.md` source files or `skills-lock.json` instead. Run `bash scripts/sync-skills.sh` to regenerate.
5
5
 
6
- **Skills:** 79
6
+ **Skills:** 80
7
7
 
8
8
  ---
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.77.1",
3
+ "version": "2.78.0",
4
4
  "description": "73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
+ # story: e53s01
2
3
  # golden-g11-gitignore-venv.sh — Python venv gitignore gate (G-11)
3
4
  #
4
5
  # Prevents recurrence of BUG-2026-07-03-venv-not-gitignored: a root .venv/
@@ -38,12 +39,25 @@ for pattern in "${required_patterns[@]}"; do
38
39
  fi
39
40
  done
40
41
 
42
+ # git check-ignore on a directory-only pattern (.venv/) only matches when the
43
+ # path is confirmed to be a directory — a nonexistent .venv (e.g. a fresh
44
+ # kickoff-branch worktree, before setup-environment runs) reports as "not
45
+ # ignored" even when the pattern is correct. Create it if absent so the check
46
+ # is independent of prior local setup; remove it again only if we created it.
47
+ CREATED_VENV=0
48
+ if [ ! -e .venv ]; then
49
+ mkdir .venv
50
+ CREATED_VENV=1
51
+ fi
52
+
41
53
  if git check-ignore -q .venv 2>/dev/null; then
42
54
  g11_pass "git check-ignore .venv"
43
55
  else
44
56
  g11_fail "git check-ignore .venv (path not ignored)"
45
57
  fi
46
58
 
59
+ [ "$CREATED_VENV" -eq 1 ] && rmdir .venv 2>/dev/null || true
60
+
47
61
  echo ""
48
62
  echo "G-11 summary: $PASS passed, $FAIL failed"
49
63
  if [[ "$FAIL" -gt 0 ]]; then
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env python3
2
+ # story: e53s01
2
3
  """story: e38s04 — TEA-inspired heuristic blind-spot detector (Python engine).
3
4
 
4
5
  Reads execution-status.yaml + traceability-matrix.json, runs 7 structural
@@ -9,6 +10,8 @@ Usage: called by scripts/check-blind-spots.sh with positional args:
9
10
  <exec_status_yaml> <matrix_json> <verifications_dir> <epics_dir>
10
11
  """
11
12
 
13
+ from __future__ import annotations
14
+
12
15
  import json, os, re, sys
13
16
  from pathlib import Path
14
17
  from datetime import datetime, timezone
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # story: e45s05
2
+ # story: e45s05 e53s01
3
3
  # Adversarial gap-finding completeness critic — post gate-trace.
4
4
  # Classifications: BLOCKER | WARNING | FILLED
5
5
  set -euo pipefail
@@ -28,8 +28,18 @@ classify() {
28
28
  || classify BLOCKER "Missing specs/blind-spots.json — run check-blind-spots.sh"
29
29
 
30
30
  if [[ -f specs/traceability-matrix.json ]]; then
31
- UNDONE="$(jq '[.stories[]? | select(.status != "done" and (.code_tags // 0) == 0)] | length' specs/traceability-matrix.json 2>/dev/null || echo 0)"
32
- (( UNDONE > 0 )) && classify BLOCKER "$UNDONE active story(ies) with zero code tags"
31
+ # verify-work runs per-story (build-epic builds one story at a time), so this
32
+ # check must scope to the story currently being verified — not its
33
+ # not-yet-built siblings (which have no code tags by definition until their
34
+ # own turn) and not unrelated epics (which may be plan-worked-to-build-ready
35
+ # but not yet kicked off).
36
+ ACTIVE_STORY="$(grep '^active_story:' specs/state.yaml 2>/dev/null | awk '{print $2}')"
37
+ if [[ -n "$ACTIVE_STORY" ]]; then
38
+ UNDONE="$(jq --arg s "$ACTIVE_STORY" \
39
+ '[.stories[]? | select(.id == $s and .status != "done" and ((.links // []) | map(select(.method == "explicit_tag")) | length) == 0)] | length' \
40
+ specs/traceability-matrix.json 2>/dev/null || echo 0)"
41
+ (( UNDONE > 0 )) && classify BLOCKER "active story $ACTIVE_STORY has zero explicit story-tag links"
42
+ fi
33
43
  COVERAGE="$(jq -r '.summary.coverage_percent // .coverage_percent // empty' specs/traceability-matrix.json 2>/dev/null)"
34
44
  if [[ -n "$COVERAGE" && "$COVERAGE" != "null" ]]; then
35
45
  awk -v c="$COVERAGE" 'BEGIN{if(c+0 < 60) exit 1}' || classify WARNING "Trace coverage ${COVERAGE}% below 60% target"
@@ -25,13 +25,21 @@ report() {
25
25
  esac
26
26
  }
27
27
 
28
+ # bash-3.2-portable membership test (no associative arrays — macOS ships bash 3.2)
29
+ in_array() {
30
+ local needle="$1"; shift
31
+ local x
32
+ for x in "$@"; do [[ "$x" == "$needle" ]] && return 0; done
33
+ return 1
34
+ }
35
+
28
36
  [[ -f "$EPIC_YAML" ]] || report CRITICAL "Missing epic.yaml in $CAPSULE"
29
37
 
30
38
  # Story IDs declared in epic.yaml
31
- declare -A EPIC_STORIES=()
39
+ EPIC_STORIES=()
32
40
  if [[ -f "$EPIC_YAML" ]]; then
33
41
  while IFS= read -r sid; do
34
- [[ -n "$sid" ]] && EPIC_STORIES["$sid"]=1
42
+ [[ -n "$sid" ]] && EPIC_STORIES+=("$sid")
35
43
  done < <(grep -E '^[[:space:]]*- id: e[0-9]+s[0-9]+' "$EPIC_YAML" | awk '{print $3}')
36
44
  fi
37
45
 
@@ -43,32 +51,35 @@ shopt -u nullglob
43
51
  (( ${#SPECS[@]} > 0 )) || report CRITICAL "No story spec .md files in capsule (run slice-tasks first)"
44
52
  (( ${#TASKS[@]} > 0 )) || report CRITICAL "No *-tasks.yaml files in capsule (run plan-work first)"
45
53
 
46
- declare -A SPEC_IDS=()
47
- for spec in "${SPECS[@]}"; do
54
+ SPEC_IDS=()
55
+ for spec in "${SPECS[@]+"${SPECS[@]}"}"; do
48
56
  base="$(basename "$spec" .md)"
49
57
  sid="$(echo "$base" | grep -oE 'e[0-9]+s[0-9]+' | head -1)"
50
58
  [[ -n "$sid" ]] || continue
51
- SPEC_IDS["$sid"]=1
52
- [[ -n "${EPIC_STORIES[$sid]+x}" ]] || report HIGH "Story $sid in spec file but missing from epic.yaml manifest"
53
- grep -qE '^## (17\.|Acceptance|Verification Script)' "$spec" \
59
+ SPEC_IDS+=("$sid")
60
+ in_array "$sid" "${EPIC_STORIES[@]+"${EPIC_STORIES[@]}"}" \
61
+ || report HIGH "Story $sid in spec file but missing from epic.yaml manifest"
62
+ grep -qE '^#{2,3} (17\.|Acceptance|Verification Script)' "$spec" \
54
63
  || report HIGH "Story $sid spec missing §17 acceptance criteria or Verification Script"
55
64
  grep -qiE 'ambiguous|TBD|TODO|FIXME' "$spec" \
56
65
  && report MED "Story $sid spec contains ambiguous/TBD markers"
57
66
  done
58
67
 
59
- for tasks in "${TASKS[@]}"; do
68
+ for tasks in "${TASKS[@]+"${TASKS[@]}"}"; do
60
69
  base="$(basename "$tasks" -tasks.yaml)"
61
70
  sid="$(echo "$base" | grep -oE 'e[0-9]+s[0-9]+' | head -1)"
62
71
  [[ -n "$sid" ]] || continue
63
- [[ -n "${SPEC_IDS[$sid]+x}" ]] || report CRITICAL "tasks.yaml for $sid without matching story spec .md"
72
+ in_array "$sid" "${SPEC_IDS[@]+"${SPEC_IDS[@]}"}" \
73
+ || report CRITICAL "tasks.yaml for $sid without matching story spec .md"
64
74
  grep -qE '^[[:space:]]*verify:' "$tasks" \
65
75
  || report CRITICAL "Story $sid tasks.yaml missing runnable verify: commands"
66
76
  grep -qE '^status:[[:space:]]*(failing|todo|passing)' "$tasks" \
67
77
  || report MED "Story $sid tasks.yaml should use status: failing|passing ledger (e45s06)"
68
78
  done
69
79
 
70
- for sid in "${!EPIC_STORIES[@]}"; do
71
- [[ -n "${SPEC_IDS[$sid]+x}" ]] || report HIGH "epic.yaml lists $sid but no story spec .md exists"
80
+ for sid in "${EPIC_STORIES[@]+"${EPIC_STORIES[@]}"}"; do
81
+ in_array "$sid" "${SPEC_IDS[@]+"${SPEC_IDS[@]}"}" \
82
+ || report HIGH "epic.yaml lists $sid but no story spec .md exists"
72
83
  done
73
84
 
74
85
  echo "---"
@@ -7,20 +7,30 @@ def parse_simple_yaml(text: str) -> dict:
7
7
  root: dict = {}
8
8
  # Each stack entry: (indent, container, parent_dict, key_in_parent)
9
9
  stack: list = [(0, root, None, None)]
10
- skip_until_indent = None # skip block scalar continuation lines (| and >)
10
+ skip_until_indent = None # indent of an active block scalar's key line
11
+ block: dict = {} # active block scalar: {'lines': [...], 'style': str, 'assign': fn}
12
+
13
+ def flush_block():
14
+ if block:
15
+ block['assign'](_fold_block(block['lines'], block['style']))
16
+ block.clear()
17
+
11
18
  for i, raw in enumerate(text.splitlines()):
12
19
  stripped = raw.strip()
13
- if not stripped or stripped.startswith("#"):
14
- # Reset block-scalar skip on blank lines
15
- continue
16
20
  indent = len(raw) - len(raw.lstrip())
17
-
18
- # Skip block scalar continuation (| and > in YAML)
21
+
22
+ # Inside a block scalar: capture continuation lines verbatim. Blank and
23
+ # '#'-prefixed lines are literal content here, not comments.
19
24
  if skip_until_indent is not None:
20
- if indent > skip_until_indent:
25
+ if not stripped or indent > skip_until_indent:
26
+ if block:
27
+ block['lines'].append(raw)
21
28
  continue
22
- else:
23
- skip_until_indent = None
29
+ flush_block()
30
+ skip_until_indent = None
31
+
32
+ if not stripped or stripped.startswith("#"):
33
+ continue
24
34
 
25
35
  # Pop stack until we're at the right nesting level
26
36
  # Don't pop list containers at same indent — sibling items stay in same list
@@ -68,8 +78,11 @@ def parse_simple_yaml(text: str) -> dict:
68
78
  v = v.strip()
69
79
  # Detect block scalars in list items too
70
80
  if v in ("|", ">", "|-", ">-", "|+", ">+"):
71
- item = {k: v}
81
+ item = {k: None}
72
82
  container.append(item)
83
+ block.clear()
84
+ block.update(lines=[], style=v,
85
+ assign=lambda s, _i=item, _k=k: _i.__setitem__(_k, s))
73
86
  skip_until_indent = indent
74
87
  continue
75
88
  item = {k: _yaml_scalar(v) if v else None}
@@ -87,12 +100,18 @@ def parse_simple_yaml(text: str) -> dict:
87
100
  key = key.strip()
88
101
  val = val.strip()
89
102
 
90
- # Detect block scalars (|, >, |- etc.)
103
+ # Detect block scalars (|, >, |- etc.) and capture their folded content.
91
104
  if val in ("|", ">", "|-", ">-", "|+", ">+"):
105
+ tgt = None
92
106
  if isinstance(container, list) and container:
93
- container[-1][key] = val # store the marker
107
+ tgt = container[-1]
94
108
  elif isinstance(container, dict):
95
- container[key] = val
109
+ tgt = container
110
+ if tgt is not None:
111
+ tgt[key] = None
112
+ block.clear()
113
+ block.update(lines=[], style=val,
114
+ assign=lambda s, _t=tgt, _k=key: _t.__setitem__(_k, s))
96
115
  skip_until_indent = indent
97
116
  continue
98
117
 
@@ -111,11 +130,43 @@ def parse_simple_yaml(text: str) -> dict:
111
130
  stack.append((indent, nxt, container, key))
112
131
  else:
113
132
  container[key] = _yaml_scalar(val)
133
+ flush_block()
114
134
  return root
115
135
 
116
136
 
137
+ def _fold_block(raw_lines, style):
138
+ """Fold a YAML block scalar (| literal / > folded) to match PyYAML."""
139
+ # Strip the common leading indentation of the block's content lines.
140
+ indents = [len(l) - len(l.lstrip()) for l in raw_lines if l.strip()]
141
+ ci = min(indents) if indents else 0
142
+ lines = [l[ci:] if len(l) >= ci else l.strip() for l in raw_lines]
143
+ if style[0] == "|": # literal: keep line breaks
144
+ text = "\n".join(lines)
145
+ else: # folded: single break -> space, blank line -> newline
146
+ parts, prev_blank = [], True
147
+ for l in lines:
148
+ if l == "":
149
+ parts.append("\n")
150
+ prev_blank = True
151
+ else:
152
+ if parts and not prev_blank:
153
+ parts.append(" ")
154
+ parts.append(l)
155
+ prev_blank = False
156
+ text = "".join(parts)
157
+ text = text.rstrip("\n")
158
+ # Chomping indicator: '-' strip, default clip (single trailing newline).
159
+ return text if style.endswith("-") else text + "\n"
160
+
161
+
117
162
  def _yaml_scalar(val: str):
118
163
  """Convert a YAML scalar string to Python type."""
164
+ stripped = val.strip()
165
+ # Double-quoted scalar: unescape YAML escapes (\" and \\) like PyYAML, and
166
+ # keep it a string (no bool/int coercion). Keeps the PyYAML-free fallback
167
+ # byte-identical to PyYAML for quoted descriptions.
168
+ if len(stripped) >= 2 and stripped[0] == '"' and stripped[-1] == '"':
169
+ return stripped[1:-1].replace('\\"', '"').replace("\\\\", "\\")
119
170
  val = val.strip('"').strip("'")
120
171
  if val in ("true", "false"):
121
172
  return val == "true"
@@ -1,19 +1,30 @@
1
1
  #!/usr/bin/env python3
2
2
  # story: e48s15
3
+ # story: BUG-2026-07-18-setup-pyyaml-missing
3
4
  import os
4
5
  import sys
5
6
  import json
6
7
  import glob
7
8
  import subprocess
8
- import yaml
9
9
 
10
- # Ensure scripts/lib is on the path so link_utils resolves regardless of cwd.
10
+ # Ensure scripts/lib is on the path so sibling modules (link_utils, and the
11
+ # simple_yaml fallback below) resolve regardless of cwd.
11
12
  _LIB_DIR = os.path.dirname(os.path.abspath(__file__))
12
13
  if _LIB_DIR not in sys.path:
13
14
  sys.path.insert(0, _LIB_DIR)
14
15
 
15
16
  from link_utils import LINK_RE, EXTERNAL_RE, strip_code_spans # noqa: E402
16
17
 
18
+ # End-user setup (bigpowers setup) may run on a python3 without PyYAML.
19
+ # Prefer PyYAML for exact parsing; fall back to the dependency-free
20
+ # simple_yaml.py (story e38s01) so setup never hard-crashes.
21
+ try:
22
+ import yaml
23
+ _load_yaml = yaml.safe_load
24
+ except ModuleNotFoundError:
25
+ from simple_yaml import parse_simple_yaml
26
+ _load_yaml = parse_simple_yaml
27
+
17
28
  def rewrite_links_for_pi(body, name):
18
29
  """Repoint relative links so they resolve from .pi/skills/<name>/.
19
30
 
@@ -114,7 +125,7 @@ def parse_skill(skill_md_path):
114
125
  skill_body = '---'.join(parts[2:])
115
126
 
116
127
  try:
117
- frontmatter = yaml.safe_load(frontmatter_raw)
128
+ frontmatter = _load_yaml(frontmatter_raw)
118
129
  except Exception as e:
119
130
  print(f"Error parsing YAML frontmatter in {skill_md_path}: {e}", file=sys.stderr)
120
131
  sys.exit(1)
@@ -160,7 +171,7 @@ def get_active_adapters(repo_root):
160
171
  return ["cursor", "gemini", "pi"]
161
172
  try:
162
173
  with open(targets_file, 'r', encoding='utf-8') as f:
163
- data = yaml.safe_load(f)
174
+ data = _load_yaml(f.read())
164
175
  adapters = []
165
176
  for target in data.get('targets', []):
166
177
  if target.get('skill') is not None:
@@ -74,7 +74,7 @@ if [[ -f "$TARGETS_FILE" ]] && command -v yq >/dev/null 2>&1; then
74
74
  [[ -n "$adapter" && "$adapter" != "null" ]] && SKILL_ADAPTERS+=("$adapter")
75
75
  done < <(yq -r '.targets[] | select(.skill != null) | .skill.adapter' "$TARGETS_FILE" | sort -u)
76
76
  else
77
- echo "sync-skills: WARN — targets.yaml missing; using legacy render list" >&2
77
+ echo "sync-skills: WARN — yq unavailable or targets.yaml missing; using legacy render list (cursor gemini pi)" >&2
78
78
  SKILL_ADAPTERS=(cursor gemini pi)
79
79
  fi
80
80
 
@@ -46,8 +46,9 @@ keys: dict[str, str] = {}
46
46
  existing_path = epics_dir.parent / "execution-status.yaml"
47
47
  if existing_path.exists():
48
48
  existing = existing_path.read_text(encoding="utf-8")
49
- for m in re.finditer(r"^ ([a-z0-9._-]+):\s*(\S+)", existing, re.M):
50
- keys[m.group(1)] = m.group(2)
49
+ for m in re.finditer(r"^ ([a-z0-9._-]+):[ \t]*(\S+)?[ \t]*$", existing, re.M):
50
+ if m.group(2):
51
+ keys[m.group(1)] = m.group(2)
51
52
 
52
53
  # --- Parse release-plan.yaml for wsjf/tier per epic ---
53
54
  rp_epics: dict[str, dict[str, object]] = {}
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env bash
2
+ # test-setup-no-pyyaml.sh — recurrence guard for BUG-2026-07-18-setup-pyyaml-missing
3
+ #
4
+ # `bigpowers setup` must not crash when the resolved python3 lacks PyYAML.
5
+ # Two setup-path scripts hard-depended on PyYAML: srp-engine.py (import yaml)
6
+ # and validate-skill-yaml.py (sys.exit(2) on ImportError). Both now fall back
7
+ # to scripts/lib/simple_yaml.py. This guard asserts:
8
+ # 1. srp-engine.py parses a skill under a PyYAML-free interpreter.
9
+ # 2. validate-skill-yaml.py runs (does not exit 2) under the same.
10
+ # 3. simple_yaml's output equals PyYAML's for every skill's frontmatter
11
+ # (covers escaped quotes and folded/literal block scalars).
12
+ #
13
+ # Usage: bash scripts/test-setup-no-pyyaml.sh
14
+ set -euo pipefail
15
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
16
+ resolve_repo_root
17
+ cd "$REPO_ROOT"
18
+
19
+ GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m'
20
+ FAILURES=0
21
+ pass() { echo -e "${GREEN}PASS${NC} $1"; }
22
+ fail() { echo -e "${RED}FAIL${NC} $1"; FAILURES=$((FAILURES + 1)); }
23
+
24
+ # Build a throwaway PyYAML-free interpreter — a fresh venv has no PyYAML,
25
+ # reproducing a stock macOS python3 without touching the caller's environment.
26
+ NOYAML_DIR="$(mktemp -d)"
27
+ trap 'rm -rf "$NOYAML_DIR"' EXIT
28
+ if ! python3 -m venv "$NOYAML_DIR/venv" >/dev/null 2>&1; then
29
+ echo "test-setup-no-pyyaml: SKIP — cannot create venv"; exit 0
30
+ fi
31
+ NOPY="$NOYAML_DIR/venv/bin/python"
32
+ if "$NOPY" -c "import yaml" >/dev/null 2>&1; then
33
+ echo "test-setup-no-pyyaml: SKIP — fresh venv unexpectedly has PyYAML"; exit 0
34
+ fi
35
+
36
+ # 1. srp-engine.py must parse a skill's frontmatter without crashing.
37
+ if "$NOPY" scripts/lib/srp-engine.py skills/fix-bug/SKILL.md >/dev/null 2>&1; then
38
+ pass "srp-engine.py parses frontmatter without PyYAML"
39
+ else
40
+ fail "srp-engine.py crashed without PyYAML"
41
+ fi
42
+
43
+ # 2. validate-skill-yaml.py must not bail with exit 2 (the old PyYAML-required path).
44
+ rc=0
45
+ "$NOPY" scripts/validate-skill-yaml.py >/dev/null 2>&1 || rc=$?
46
+ if [[ "$rc" -ne 2 ]]; then
47
+ pass "validate-skill-yaml.py runs without PyYAML (exit $rc)"
48
+ else
49
+ fail "validate-skill-yaml.py bailed with exit 2 (PyYAML required)"
50
+ fi
51
+
52
+ # 3. Fallback fidelity: simple_yaml must equal PyYAML for every skill's
53
+ # frontmatter. Runs under python3 (CI has PyYAML); skips if PyYAML absent.
54
+ if python3 - <<'PY'; then
55
+ import glob, sys
56
+ sys.path.insert(0, "scripts/lib")
57
+ from simple_yaml import parse_simple_yaml
58
+ try:
59
+ import yaml
60
+ except ImportError:
61
+ print("skip: PyYAML not available for comparison"); sys.exit(0)
62
+ bad = []
63
+ for p in sorted(glob.glob("skills/*/SKILL.md")):
64
+ fm = open(p, encoding="utf-8").read().split("---")[1]
65
+ a = parse_simple_yaml(fm)
66
+ b = yaml.safe_load(fm)
67
+ for k in ("name", "description", "effort", "model"):
68
+ if a.get(k) != b.get(k):
69
+ bad.append(f"{p}:{k}")
70
+ if bad:
71
+ print("mismatch: " + ", ".join(bad)); sys.exit(1)
72
+ sys.exit(0)
73
+ PY
74
+ pass "simple_yaml matches PyYAML for all skill frontmatter"
75
+ else
76
+ fail "simple_yaml diverged from PyYAML for some skill frontmatter"
77
+ fi
78
+
79
+ if [[ "$FAILURES" -gt 0 ]]; then
80
+ echo "test-setup-no-pyyaml: $FAILURES failure(s)" >&2
81
+ exit 1
82
+ fi
83
+ echo "test-setup-no-pyyaml: PASS"
@@ -4,11 +4,18 @@ import glob
4
4
  import sys
5
5
  import os
6
6
 
7
+ # Prefer PyYAML; fall back to the dependency-free scripts/lib/simple_yaml.py
8
+ # (story e38s01) so `bigpowers setup` works on a python3 without PyYAML.
9
+ # story: BUG-2026-07-18-setup-pyyaml-missing
10
+ _LIB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib")
7
11
  try:
8
12
  import yaml
9
- except ImportError:
10
- print("ERROR: PyYAML required — pip install pyyaml")
11
- sys.exit(2)
13
+ _load_yaml = yaml.safe_load
14
+ except ModuleNotFoundError:
15
+ if _LIB not in sys.path:
16
+ sys.path.insert(0, _LIB)
17
+ from simple_yaml import parse_simple_yaml
18
+ _load_yaml = parse_simple_yaml
12
19
 
13
20
  SKILLS_DIR = ".pi/skills"
14
21
  files = sorted(glob.glob(f"{SKILLS_DIR}/*/SKILL.md"))
@@ -30,8 +37,8 @@ for path in files:
30
37
  frontmatter = parts[1]
31
38
 
32
39
  try:
33
- data = yaml.safe_load(frontmatter)
34
- except yaml.YAMLError as e:
40
+ data = _load_yaml(frontmatter)
41
+ except Exception as e:
35
42
  parse_failures.append((skill_name, str(e).split("\n")[0]))
36
43
  continue
37
44
 
@@ -0,0 +1,90 @@
1
+ # Find-Way Reference
2
+
3
+ ## Ticket Anatomy
4
+
5
+ Each ticket holds one question, sized to one 100K token agent session:
6
+
7
+ ```markdown
8
+ ## Question
9
+
10
+ <the decision or investigation this ticket resolves>
11
+ ```
12
+
13
+ **Labels:** `wayfinder:research`, `wayfinder:prototype`, `wayfinder:grilling`, or `wayfinder:task`
14
+
15
+ **Claim:** Assign to yourself before any work — claim blocks concurrent sessions.
16
+
17
+ **Blocking:** Use tracker's native dependency relationship so the frontier renders visually.
18
+
19
+ **Resolution:** Post answer as a comment, close the issue, append to map's Decisions-so-far.
20
+
21
+ ## Resolution Protocol
22
+
23
+ 1. **Load the map** — read low-res view, not every ticket body
24
+ 2. **Choose & claim** — assign ticket to yourself (or take first frontier ticket if not named)
25
+ 3. **Resolve** — zoom into related/closed tickets as needed; invoke skills the Notes block names
26
+ 4. **Record** — post resolution comment with the answer, close the issue
27
+ 5. **Update map** — append to Decisions-so-far with one-line gist + link
28
+ 6. **Graduate fog** — create new tickets from newly-specifiable fog; clear from Not-yet-specified
29
+ 7. **Rule out of scope** if resolution reveals something past the destination
30
+
31
+ ## Ticket Types in Detail
32
+
33
+ ### Research (AFK)
34
+ Surfacing facts from docs, APIs, or knowledge bases that a decision waits on.
35
+ - Resolved by a `/research` subagent
36
+ - Use when knowledge outside the working directory is required
37
+ - **Label:** `wayfinder:research`
38
+
39
+ ### Prototype (HITL)
40
+ Raise fidelity by building cheap, rough, concrete artifact to react to — outline, stub, UI/logic code.
41
+ - Use the `/prototype` skill
42
+ - Links the prototype as an asset
43
+ - Use when "how should it look" or "how should it behave" is key
44
+ - **Label:** `wayfinder:prototype`
45
+
46
+ ### Grilling (HITL)
47
+ Conversation via `/grilling` and `/domain-modeling` skills, one question at a time.
48
+ - The default case for clarifying decisions
49
+ - **Label:** `wayfinder:grilling`
50
+
51
+ ### Task (HITL or AFK)
52
+ Manual work unblocking a decision — signing up for service, provisioning access, moving data.
53
+ - This is the **only** type that does rather than decides
54
+ - Earns its place by unblocking a decision, not by delivering the destination
55
+ - Agent drives AFK tasks alone; HITL tasks get precise checklist
56
+ - **Label:** `wayfinder:task`
57
+
58
+ ## Fog-to-Ticket Graduation
59
+
60
+ When resolving a ticket clears fog, graduate the newly-specifiable bits into fresh tickets:
61
+
62
+ 1. **Identify the fog** in Not-yet-specified that the resolution illuminates
63
+ 2. **Create new tickets** with crisp question statements
64
+ 3. **Wire blocking edges** if the new tickets depend on each other or existing ones
65
+ 4. **Clear from fog** — remove the graduated patch from Not-yet-specified so it lives only as its ticket
66
+
67
+ ## Out-of-Scope Close Protocol
68
+
69
+ If a ticket turns out to sit past the destination:
70
+
71
+ 1. **Close the ticket** — mark it resolved or won't-fix
72
+ 2. **Add one line to Out of scope** — gist + why it's out of scope, linking the closed ticket
73
+ 3. **Never append to Decisions so far** — scope boundaries aren't steps on the route
74
+
75
+ ## Map State Transitions
76
+
77
+ | State | Meaning | Action |
78
+ |-------|---------|--------|
79
+ | **Open, unblocked, unclaimed** | Frontier — ready to take | Claim and resolve |
80
+ | **Open, blocked** | Waiting on something | Wait for blockers to close |
81
+ | **Open, claimed** | Concurrent session working | Skip for now |
82
+ | **Closed** | Decided — lives in Decisions so far | Reference, don't re-open |
83
+
84
+ ## Concurrency
85
+
86
+ Users may run unblocked tickets in parallel, so expect the tracker to be edited concurrently. Always:
87
+
88
+ - Refresh the map before picking a ticket
89
+ - Claim immediately before starting work
90
+ - Never assume state hasn't changed since last read
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: find-way
3
+ phase: discover
4
+ effort: standard
5
+ model: sonnet
6
+ description: Plan a large effort as a shared map of decision tickets on an issue tracker, resolving them one at a time until the way is clear. Use when an idea is too big for one session, needs structured exploration before implementation, or requires mapping decisions before building.
7
+ ---
8
+
9
+ # Find Way
10
+
11
+ ## Concept
12
+
13
+ A large effort arrives — too big for one session, wrapped in fog. **Find-way** charts the way as a **shared map** on the repo's issue tracker, then works **decision tickets** — questions whose resolution is a decision, not build slices — one at a time until the route is clear.
14
+
15
+ **Plan, don't do.** Each ticket resolves a decision. The map is done when the way is clear — nothing left to decide before someone executes.
16
+
17
+ **Refer by name.** Every map and ticket is an issue with a title. Always use the name, never bare ids or numbers.
18
+
19
+ ## The Map
20
+
21
+ A single issue labelled `wayfinder:map` — the canonical artifact. Child issues are its tickets.
22
+
23
+ ```markdown
24
+ ## Destination
25
+ <what reaching the end looks like — one or two lines>
26
+
27
+ ## Notes
28
+ <domain, skills to consult, standing preferences>
29
+
30
+ ## Decisions so far
31
+ - [closed ticket title](link) — one-line gist
32
+
33
+ ## Not yet specified
34
+ <in-scope fog you can't ticket yet>
35
+
36
+ ## Out of scope
37
+ <work ruled beyond the destination>
38
+ ```
39
+
40
+ ## Ticket Types
41
+
42
+ | Type | Label | Mode | Purpose |
43
+ |------|-------|------|---------|
44
+ | Research | `wayfinder:research` | AFK | Surface facts from docs/APIs |
45
+ | Prototype | `wayfinder:prototype` | HITL | Cheap artifact to react to |
46
+ | Grilling | `wayfinder:grilling` | HITL | One-question-at-a-time dialogue |
47
+ | Task | `wayfinder:task` | Either | Manual work unblocking a decision |
48
+
49
+ **HITL** = human in the loop. **AFK** = agent alone.
50
+
51
+ ## Two Modes
52
+
53
+ ### Chart the Map
54
+
55
+ User invokes with a loose idea.
56
+
57
+ 1. **Name the destination** — grill to pin down what this map finds its way to
58
+ 2. **Map the frontier** — breadth-first: surface open decisions and first steps. If no fog emerges, the effort fits one session — skip the map
59
+ 3. **Create the map** issue (label `wayfinder:map`)
60
+ 4. **Create tickets** as child issues, then wire blocking edges in a second pass
61
+ 5. **Fire research subagents** for each `research` ticket in parallel
62
+ 6. Stop — charting resolves nothing
63
+
64
+ ### Work Through the Map
65
+
66
+ User invokes with a map (URL/number). Ticket optional — without one, pick the next frontier ticket.
67
+
68
+ 1. Load the map (low-res view)
69
+ 2. **Choose & claim** — assign ticket before any work
70
+ 3. **Resolve** — zoom into related/closed tickets as needed
71
+ 4. **Record** — post resolution comment, close issue, append to Decisions-so-far
72
+ 5. **Graduate fog** — create new tickets from newly-specifiable fog, clear from Not-yet-specified
73
+ 6. **Rule out of scope** if resolution reveals something past the destination
74
+
75
+ **Never resolve more than one ticket per session** (except research).
76
+
77
+ ## Fog of War
78
+
79
+ Beyond live tickets lies the **fog** — decisions you can tell are coming but can't pin down yet. The **Not yet specified** section holds this dim view.
80
+
81
+ **Fog or ticket?** Test: can you state the question precisely now?
82
+
83
+ - **Ticket** when the question is sharp (even if blocked)
84
+ - **Not yet specified** when you can't phrase it sharply yet
85
+
86
+ ## Out of Scope
87
+
88
+ Work beyond the destination. Ruling something out is a scoping act, not a route step. Never graduates — returns only if destination is redrawn.
89
+
90
+ If an existing ticket sits past the destination, close it and note in Out of scope.
91
+
92
+ ## Blocking & Frontier
93
+
94
+ - **Blocking**: tracker's native dependency relationship
95
+ - **Unblocked**: every blocker is closed
96
+ - **Frontier**: open, unblocked, unclaimed children — the edge of the known
97
+
98
+ ## Detailed Reference
99
+
100
+ See [REFERENCE.md](REFERENCE.md) for ticket anatomy, resolution protocol, and examples.
@@ -74,7 +74,9 @@ gate_trace:
74
74
  ## Integration Points
75
75
 
76
76
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
77
- - **sync-skills.yml** — CI gate (FAIL blocks pipeline).
77
+ - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
78
+ blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
79
+ does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
78
80
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
79
81
 
80
82
  ## References
package/skills-lock.json CHANGED
@@ -131,6 +131,11 @@
131
131
  "sha256": "bb2bf81b5f948e10",
132
132
  "path": "skills/extract-designSKILL.md"
133
133
  },
134
+ "find-way": {
135
+ "description": "Plan a large effort as a shared map of decision tickets on an issue tracker, resolving them one at a time until the way is clear. Use when an idea is too big for one session, needs structured exploration before implementation, or requires mapping decisions before building.",
136
+ "sha256": "dff27aa0dc45c04d",
137
+ "path": "skills/find-waySKILL.md"
138
+ },
134
139
  "fix-bug": {
135
140
  "description": "Bug fix orchestrator — active_flow fix_bug; reads specs/bugs/BUG-*.md; chains investigate-bug, develop-tdd, validate-fix. Use when user reports a defect.",
136
141
  "sha256": "8e9e5922211b7e3b",
@@ -138,7 +143,7 @@
138
143
  },
139
144
  "gate-trace": {
140
145
  "description": "\"Deterministic traceability quality gate — reads coverage matrix + blind-spot data, applies decision rules with oracle confidence downgrade, emits PASS/CONCERNS/FAIL/WAIVED verdict. Use before release-branch to gate merges on traceability.\"",
141
- "sha256": "ed37c315dcef6f87",
146
+ "sha256": "dd52baa85ff939e8",
142
147
  "path": "skills/gate-traceSKILL.md"
143
148
  },
144
149
  "generate-allure-report": {