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 +2 -2
- package/.pi/prompts/find-way.md +190 -0
- package/.pi/prompts/gate-trace.md +3 -1
- package/.pi/skills/find-way/SKILL.md +191 -0
- package/.pi/skills/gate-trace/SKILL.md +3 -1
- package/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/SKILL-INDEX.md +1 -1
- package/package.json +1 -1
- package/scripts/golden-g11-gitignore-venv.sh +14 -0
- package/scripts/lib/__pycache__/link_utils.cpython-312.pyc +0 -0
- package/scripts/lib/blind-spots.py +3 -0
- package/scripts/lib/completeness-critic.sh +13 -3
- package/scripts/lib/plan-consistency-check.sh +22 -11
- package/scripts/lib/simple_yaml.py +64 -13
- package/scripts/lib/srp-engine.py +15 -4
- package/scripts/sync-skills.sh +1 -1
- package/scripts/sync-status-from-epics.sh +3 -2
- package/scripts/test-setup-no-pyyaml.sh +83 -0
- package/scripts/validate-skill-yaml.py +12 -5
- package/skills/find-way/REFERENCE.md +90 -0
- package/skills/find-way/SKILL.md +100 -0
- package/skills/gate-trace/SKILL.md +3 -1
- package/skills-lock.json +6 -1
package/.pi/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bigpowers",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
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** —
|
|
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** —
|
|
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
|

|
|
4
4
|

|
|
5
|
-

|
|
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
package/package.json
CHANGED
|
@@ -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
|
|
Binary file
|
|
@@ -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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
39
|
+
EPIC_STORIES=()
|
|
32
40
|
if [[ -f "$EPIC_YAML" ]]; then
|
|
33
41
|
while IFS= read -r sid; do
|
|
34
|
-
[[ -n "$sid" ]] && EPIC_STORIES
|
|
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
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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 "${
|
|
71
|
-
|
|
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 #
|
|
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
|
-
#
|
|
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
|
-
|
|
23
|
-
|
|
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:
|
|
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]
|
|
107
|
+
tgt = container[-1]
|
|
94
108
|
elif isinstance(container, dict):
|
|
95
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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:
|
package/scripts/sync-skills.sh
CHANGED
|
@@ -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._-]+)
|
|
50
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
sys.
|
|
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 =
|
|
34
|
-
except
|
|
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** —
|
|
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": "
|
|
146
|
+
"sha256": "dd52baa85ff939e8",
|
|
142
147
|
"path": "skills/gate-traceSKILL.md"
|
|
143
148
|
},
|
|
144
149
|
"generate-allure-report": {
|