@jamie-tam/forge 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +389 -0
  3. package/agents/architect.md +92 -0
  4. package/agents/builder.md +122 -0
  5. package/agents/code-reviewer.md +107 -0
  6. package/agents/concept-designer.md +207 -0
  7. package/agents/craft-reviewer.md +132 -0
  8. package/agents/critic.md +130 -0
  9. package/agents/doc-writer.md +85 -0
  10. package/agents/dreamer.md +129 -0
  11. package/agents/e2e-runner.md +89 -0
  12. package/agents/gotcha-hunter.md +127 -0
  13. package/agents/prototype-builder.md +193 -0
  14. package/agents/prototype-codifier.md +204 -0
  15. package/agents/prototype-reviewer.md +163 -0
  16. package/agents/security-reviewer.md +108 -0
  17. package/agents/spec-reviewer.md +94 -0
  18. package/agents/tracer.md +98 -0
  19. package/agents/wireframer.md +109 -0
  20. package/commands/abort.md +25 -0
  21. package/commands/bugfix.md +151 -0
  22. package/commands/evolve.md +118 -0
  23. package/commands/feature.md +236 -0
  24. package/commands/forge.md +100 -0
  25. package/commands/greenfield.md +185 -0
  26. package/commands/hotfix.md +98 -0
  27. package/commands/refactor.md +147 -0
  28. package/commands/resume.md +25 -0
  29. package/commands/setup.md +201 -0
  30. package/commands/status.md +27 -0
  31. package/commands/task-force.md +110 -0
  32. package/commands/validate.md +12 -0
  33. package/dist/__tests__/active-manifest.test.js +272 -0
  34. package/dist/__tests__/copy.test.js +96 -0
  35. package/dist/__tests__/gate-check.test.js +384 -0
  36. package/dist/__tests__/wiki.test.js +472 -0
  37. package/dist/__tests__/work-manifest.test.js +304 -0
  38. package/dist/active-manifest.js +229 -0
  39. package/dist/cli.js +158 -0
  40. package/dist/copy.js +124 -0
  41. package/dist/gate-check.js +326 -0
  42. package/dist/hooks.js +60 -0
  43. package/dist/init.js +140 -0
  44. package/dist/manifest.js +90 -0
  45. package/dist/merge.js +77 -0
  46. package/dist/paths.js +36 -0
  47. package/dist/uninstall.js +216 -0
  48. package/dist/update.js +158 -0
  49. package/dist/verify-manifest.js +65 -0
  50. package/dist/verify.js +98 -0
  51. package/dist/wiki-ui.js +310 -0
  52. package/dist/wiki.js +364 -0
  53. package/dist/work-manifest.js +798 -0
  54. package/hooks/config/gate-requirements.json +79 -0
  55. package/hooks/hooks.json +143 -0
  56. package/hooks/scripts/analyze-telemetry.sh +114 -0
  57. package/hooks/scripts/gate-enforcer.sh +164 -0
  58. package/hooks/scripts/pre-compact.sh +90 -0
  59. package/hooks/scripts/session-start.sh +81 -0
  60. package/hooks/scripts/telemetry.sh +41 -0
  61. package/hooks/scripts/wiki-lint.sh +87 -0
  62. package/hooks/templates/AGENTS.md.template +48 -0
  63. package/hooks/templates/CLAUDE.md.template +45 -0
  64. package/package.json +55 -0
  65. package/protocols/README.md +40 -0
  66. package/protocols/codex.md +151 -0
  67. package/protocols/graphify.md +156 -0
  68. package/references/common/agent-coordination.md +65 -0
  69. package/references/common/coding-standards.md +54 -0
  70. package/references/common/feature-tracking.md +21 -0
  71. package/references/common/io-protocol.md +36 -0
  72. package/references/common/phases.md +57 -0
  73. package/references/common/quality-gates.md +130 -0
  74. package/references/common/skill-authoring.md +154 -0
  75. package/references/common/skill-compliance.md +30 -0
  76. package/references/python/standards.md +44 -0
  77. package/references/react/standards.md +61 -0
  78. package/references/typescript/standards.md +42 -0
  79. package/rules/common/forge-system.md +59 -0
  80. package/rules/common/git-workflow.md +40 -0
  81. package/rules/common/guardrails.md +37 -0
  82. package/rules/common/quality-gates.md +18 -0
  83. package/rules/common/security.md +50 -0
  84. package/rules/common/skill-selection.md +78 -0
  85. package/rules/common/testing.md +58 -0
  86. package/rules/common/verification.md +39 -0
  87. package/skills/build-pr-workflow/SKILL.md +301 -0
  88. package/skills/build-pr-workflow/references/pr-template.md +62 -0
  89. package/skills/build-pr-workflow/references/subagent-merge.md +47 -0
  90. package/skills/build-pr-workflow/references/worktree-setup.md +125 -0
  91. package/skills/build-prototype/SKILL.md +264 -0
  92. package/skills/build-scaffold/SKILL.md +340 -0
  93. package/skills/build-tdd/SKILL.md +89 -0
  94. package/skills/build-wireframe/SKILL.md +110 -0
  95. package/skills/build-wireframe/assets/baseline-template.html +486 -0
  96. package/skills/build-wireframe/references/demo-walkthroughs.md +170 -0
  97. package/skills/build-wireframe/references/gotchas.md +188 -0
  98. package/skills/build-wireframe/references/legend-lines.md +141 -0
  99. package/skills/concept-slides/SKILL.md +192 -0
  100. package/skills/deliver-db-migration/SKILL.md +466 -0
  101. package/skills/deliver-deploy/SKILL.md +407 -0
  102. package/skills/deliver-onboarding/SKILL.md +198 -0
  103. package/skills/deliver-onboarding/references/document-templates.md +393 -0
  104. package/skills/deliver-onboarding/templates/getting-started.md +122 -0
  105. package/skills/discover-codebase-analysis/SKILL.md +448 -0
  106. package/skills/discover-requirements/SKILL.md +418 -0
  107. package/skills/discover-requirements/templates/prd.md +99 -0
  108. package/skills/discover-requirements/templates/technical-spec.md +123 -0
  109. package/skills/discover-requirements/templates/user-stories.md +76 -0
  110. package/skills/harden/SKILL.md +214 -0
  111. package/skills/iterate-prototype/SKILL.md +241 -0
  112. package/skills/plan-architecture/SKILL.md +457 -0
  113. package/skills/plan-architecture/templates/adr-template.md +52 -0
  114. package/skills/plan-architecture/templates/api-contract.md +99 -0
  115. package/skills/plan-architecture/templates/db-schema.md +81 -0
  116. package/skills/plan-architecture/templates/system-design.md +111 -0
  117. package/skills/plan-brainstorm/SKILL.md +433 -0
  118. package/skills/plan-design-system/SKILL.md +279 -0
  119. package/skills/plan-task-decompose/SKILL.md +454 -0
  120. package/skills/quality-code-review/SKILL.md +286 -0
  121. package/skills/quality-security-audit/SKILL.md +292 -0
  122. package/skills/quality-security-audit/references/audit-report-template.md +89 -0
  123. package/skills/quality-security-audit/references/owasp-checks.md +178 -0
  124. package/skills/quality-test-execution/SKILL.md +435 -0
  125. package/skills/quality-test-plan/SKILL.md +297 -0
  126. package/skills/quality-test-plan/references/test-type-guide.md +263 -0
  127. package/skills/quality-test-plan/templates/e2e-test-plan.md +72 -0
  128. package/skills/quality-test-plan/templates/integration-test-plan.md +74 -0
  129. package/skills/quality-test-plan/templates/load-test-plan.md +111 -0
  130. package/skills/quality-test-plan/templates/smoke-test-plan.md +68 -0
  131. package/skills/quality-test-plan/templates/unit-test-plan.md +56 -0
  132. package/skills/quality-uiux/SKILL.md +481 -0
  133. package/skills/support-debug/SKILL.md +464 -0
  134. package/skills/support-dream/SKILL.md +213 -0
  135. package/skills/support-gotcha/SKILL.md +249 -0
  136. package/skills/support-runtime-reachability/SKILL.md +190 -0
  137. package/skills/support-runtime-reachability/scripts/__fixtures__/case-01-passes-app-use/src/app.ts +7 -0
  138. package/skills/support-runtime-reachability/scripts/__fixtures__/case-01-passes-app-use/src/handlers/cases.ts +7 -0
  139. package/skills/support-runtime-reachability/scripts/__fixtures__/case-02-orphan-no-app-use/src/app.ts +8 -0
  140. package/skills/support-runtime-reachability/scripts/__fixtures__/case-02-orphan-no-app-use/src/handlers/cases.ts +7 -0
  141. package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/App.tsx +5 -0
  142. package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/components/RingingBanner.tsx +7 -0
  143. package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/hooks/useTwilio.ts +6 -0
  144. package/skills/support-runtime-reachability/scripts/__fixtures__/case-04-jsx-component-rendered/src/App.tsx +5 -0
  145. package/skills/support-runtime-reachability/scripts/__fixtures__/case-04-jsx-component-rendered/src/components/MyComp.tsx +3 -0
  146. package/skills/support-runtime-reachability/scripts/__fixtures__/case-05-jsx-component-not-rendered/src/App.tsx +3 -0
  147. package/skills/support-runtime-reachability/scripts/__fixtures__/case-05-jsx-component-not-rendered/src/components/Orphan.tsx +3 -0
  148. package/skills/support-runtime-reachability/scripts/__fixtures__/case-06-class-instantiated/src/lib/Service.ts +6 -0
  149. package/skills/support-runtime-reachability/scripts/__fixtures__/case-06-class-instantiated/src/main.ts +4 -0
  150. package/skills/support-runtime-reachability/scripts/__fixtures__/case-07-class-not-instantiated/src/lib/Lonely.ts +5 -0
  151. package/skills/support-runtime-reachability/scripts/__fixtures__/case-07-class-not-instantiated/src/main.ts +2 -0
  152. package/skills/support-runtime-reachability/scripts/__fixtures__/case-08-default-export-imported-and-called/src/handler.ts +3 -0
  153. package/skills/support-runtime-reachability/scripts/__fixtures__/case-08-default-export-imported-and-called/src/main.ts +3 -0
  154. package/skills/support-runtime-reachability/scripts/__fixtures__/case-09-default-export-orphan/src/handler.ts +3 -0
  155. package/skills/support-runtime-reachability/scripts/__fixtures__/case-09-default-export-orphan/src/main.ts +2 -0
  156. package/skills/support-runtime-reachability/scripts/__fixtures__/case-10-aliased-named-export/src/lib.ts +5 -0
  157. package/skills/support-runtime-reachability/scripts/__fixtures__/case-10-aliased-named-export/src/main.ts +3 -0
  158. package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/lib/index.ts +1 -0
  159. package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/lib/internal.ts +3 -0
  160. package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/main.ts +3 -0
  161. package/skills/support-runtime-reachability/scripts/__fixtures__/case-12-test-only-caller/src/util.test.ts +5 -0
  162. package/skills/support-runtime-reachability/scripts/__fixtures__/case-12-test-only-caller/src/util.ts +3 -0
  163. package/skills/support-runtime-reachability/scripts/__fixtures__/case-13-gated-pending-annotation/src/future.ts +4 -0
  164. package/skills/support-runtime-reachability/scripts/__fixtures__/case-14-untraceable-annotation/src/decorated.ts +4 -0
  165. package/skills/support-runtime-reachability/scripts/__fixtures__/case-15-untraceable-empty/src/lazy.ts +4 -0
  166. package/skills/support-runtime-reachability/scripts/__fixtures__/case-16-python-module/src/lib.py +15 -0
  167. package/skills/support-runtime-reachability/scripts/__fixtures__/case-16-python-module/src/main.py +5 -0
  168. package/skills/support-runtime-reachability/scripts/__fixtures__/case-17-router-use/src/parent.ts +5 -0
  169. package/skills/support-runtime-reachability/scripts/__fixtures__/case-17-router-use/src/routes/cases.ts +5 -0
  170. package/skills/support-runtime-reachability/scripts/__fixtures__/case-18-shadowed-name-fp/src/lib/foo.ts +3 -0
  171. package/skills/support-runtime-reachability/scripts/__fixtures__/case-18-shadowed-name-fp/src/other.ts +8 -0
  172. package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/handlers/cases.ts +4 -0
  173. package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/handlers/users.ts +4 -0
  174. package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/main.ts +5 -0
  175. package/skills/support-runtime-reachability/scripts/__fixtures__/case-20-aliased-import-usage/src/handlers/cases.ts +3 -0
  176. package/skills/support-runtime-reachability/scripts/__fixtures__/case-20-aliased-import-usage/src/main.ts +4 -0
  177. package/skills/support-runtime-reachability/scripts/__fixtures__/case-21-mixed-default-and-named/src/lib.ts +5 -0
  178. package/skills/support-runtime-reachability/scripts/__fixtures__/case-21-mixed-default-and-named/src/main.ts +5 -0
  179. package/skills/support-runtime-reachability/scripts/__fixtures__/case-22-dynamic-import-then-caller/src/lib.ts +3 -0
  180. package/skills/support-runtime-reachability/scripts/__fixtures__/case-22-dynamic-import-then-caller/src/main.ts +8 -0
  181. package/skills/support-runtime-reachability/scripts/__fixtures__/case-23-dynamic-import-with-space/src/lib.ts +3 -0
  182. package/skills/support-runtime-reachability/scripts/__fixtures__/case-23-dynamic-import-with-space/src/main.ts +7 -0
  183. package/skills/support-runtime-reachability/scripts/check.mjs +638 -0
  184. package/skills/support-runtime-reachability/scripts/check.test.mjs +244 -0
  185. package/skills/support-skill-validator/SKILL.md +194 -0
  186. package/skills/support-skill-validator/references/false-positives.md +59 -0
  187. package/skills/support-skill-validator/references/validation-checks.md +280 -0
  188. package/skills/support-system-guide/SKILL.md +311 -0
  189. package/skills/support-task-force/SKILL.md +265 -0
  190. package/skills/support-task-force/references/dispatch-pattern.md +178 -0
  191. package/skills/support-task-force/references/synthesis-template.md +126 -0
  192. package/skills/support-wiki-bootstrap/SKILL.md +37 -0
  193. package/skills/support-wiki-lint/SKILL.md +196 -0
  194. package/skills/support-wiki-lint/scripts/lint.mjs +488 -0
  195. package/skills/support-wiki-lint/scripts/lint.test.mjs +196 -0
  196. package/templates/README.md +23 -0
  197. package/templates/aiwiki/CLAUDE.md.template +78 -0
  198. package/templates/aiwiki/schemas/architecture.md +118 -0
  199. package/templates/aiwiki/schemas/convention.md +112 -0
  200. package/templates/aiwiki/schemas/decision.md +144 -0
  201. package/templates/aiwiki/schemas/gotcha.md +118 -0
  202. package/templates/aiwiki/schemas/oracle.md +105 -0
  203. package/templates/aiwiki/schemas/session.md +125 -0
  204. package/templates/manifests/bugfix.yaml +41 -0
  205. package/templates/manifests/feature.yaml +69 -0
  206. package/templates/manifests/greenfield.yaml +61 -0
  207. package/templates/manifests/hotfix.yaml +45 -0
  208. package/templates/manifests/refactor.yaml +44 -0
  209. package/templates/manifests/v5/SCHEMA.md +327 -0
  210. package/templates/manifests/v5/feature.yaml +77 -0
  211. package/templates/manifests/v6/SCHEMA.md +199 -0
  212. package/templates/wiki-html/dream-detail.html +378 -0
  213. package/templates/wiki-html/dreams-list.html +155 -0
@@ -0,0 +1,249 @@
1
+ ---
2
+ name: support-gotcha
3
+ description: "Use when a subtle bug, surprising behavior, or hard-won lesson is uncovered that could recur — records it as a typed wiki page for future prevention."
4
+ ---
5
+
6
+ # Support: Gotcha
7
+
8
+ ## Overview
9
+
10
+ Every bug fixed, every failed approach, every "I wish I'd known that earlier" moment contains a lesson. This skill captures those lessons as typed wiki pages in `aiwiki/gotchas/` so future sessions hit them before the same mistake happens again.
11
+
12
+ **Core Principle:** A gotcha recorded once should prevent the same mistake forever.
13
+
14
+ **Announce at start:** "I'm using the support-gotcha skill to record this lesson learned."
15
+
16
+ ## When to Use
17
+
18
+ **Automatic triggers (invoked by other skills):**
19
+ - After `support-debug` resolves a bug
20
+ - After `deliver-db-migration` encounters an issue
21
+ - After `deliver-deploy` has a rollback or complication
22
+ - After any skill encounters an unexpected failure
23
+
24
+ **Manual triggers:**
25
+ - User says "remember this" or "note this for next time"
26
+ - A non-obvious library or tool behavior is discovered
27
+ - A workaround is needed that should eventually be fixed properly
28
+ - A pattern keeps recurring across features
29
+
30
+ **Always record a gotcha when:**
31
+ - A bug took longer than expected to fix
32
+ - The fix was different from the initial hypothesis
33
+ - A library behaved differently than documented
34
+ - An environment-specific issue wasted time
35
+ - A process step was skipped and caused problems
36
+
37
+ **Do NOT write a gotcha for:** bugs in your own code that weren't framework- or pattern-related, one-off typos, problems already documented in `aiwiki/conventions/` (write the convention instead).
38
+
39
+ ## I/O Contract
40
+
41
+ | Field | Value |
42
+ |---|---|
43
+ | **Requires** | Concrete lesson: what broke, why, how to prevent |
44
+ | **Produces** | Gotcha page in `aiwiki/gotchas/{YYYY-MM-DD}-{slug}.md` (project) or `~/.claude/gotchas/{YYYY-MM-DD}-{slug}.md` (global) |
45
+ | **Schema** | `aiwiki/schemas/gotcha.md` — LINT validates frontmatter + sections on write |
46
+ | **Feeds into** | Future sessions (prevention), `gotcha-hunter` agent (surfaces relevant entries to reviewers), `/evolve` (skill improvement) |
47
+
48
+ ### Storage tiers
49
+
50
+ | Tier | Location | Scope | When |
51
+ |---|---|---|---|
52
+ | Project | `aiwiki/gotchas/` | This codebase only | Issue specific to this stack, patterns, or config |
53
+ | Global | `~/.claude/gotchas/` | All future projects | Issue applies universally — library behavior, general pattern |
54
+
55
+ When uncertain, default to project. The gotcha can be promoted later by writing a global copy and retiring the project one.
56
+
57
+ ## The Gotcha Process
58
+
59
+ ### Step 1: Extract the lesson
60
+
61
+ Three pieces of information must be concrete:
62
+
63
+ 1. **What broke** — the failure or surprise, not the symptom
64
+ 2. **Root cause** — what was actually wrong, not what looked wrong
65
+ 3. **Prevention** — actionable steps ("Check X before doing Y"), not "be more careful"
66
+
67
+ ### Step 2: Classify project vs global
68
+
69
+ | Question | Answer |
70
+ |---|---|
71
+ | Would this happen in a different project with a different stack? | Yes → global |
72
+ | Is this about a specific library version, config, or this project's architecture? | Yes → project |
73
+ | Is this about Docker, CI, or deployment in general? | Yes → global |
74
+
75
+ When genuinely ambiguous, ask the user: "Project-specific or universal?"
76
+
77
+ ### Step 3: Search for existing matches
78
+
79
+ Before writing a new file, grep `aiwiki/gotchas/` (and `~/.claude/gotchas/` for global) for the same pattern. If a matching gotcha exists:
80
+
81
+ - Bump its frontmatter `occurrences` field by 1
82
+ - Update `status` per the auto-promotion path (see Step 5): 1 → `active`, 2 → `watch`, 3 → `promotion-pending`
83
+ - Append a one-line entry under `## Reproducer` if the new occurrence has a different reproducer
84
+
85
+ Do NOT write a duplicate file.
86
+
87
+ ### Step 4: Write the gotcha page
88
+
89
+ **File naming:** `{YYYY-MM-DD}-{slug}.md`. Slug is kebab-case, ≤8 words, describes the pattern (not the symptom).
90
+
91
+ **Schema-aligned frontmatter:**
92
+
93
+ ```yaml
94
+ ---
95
+ schema_id: gotcha
96
+ schema_version: 1
97
+ severity: low | medium | high | critical
98
+ date: YYYY-MM-DD
99
+ occurrences: 1
100
+ status: active
101
+ ---
102
+ ```
103
+
104
+ **Required sections** (LINT will reject the file if any are missing or out of order):
105
+
106
+ | Section | Content | Citation |
107
+ |---|---|---|
108
+ | `## What broke` | One sentence — what failed and where | Cite the failure point |
109
+ | `## Reproducer` | Minimal steps or code link | Cite if linking to an existing test |
110
+ | `## Root cause` | What was actually wrong | Cite with `file:line@<sha7>` |
111
+ | `## Fix` | The resolution | Cite the fix code |
112
+ | `## Prevention` | Rule, check, or pattern that prevents recurrence | Cite a convention or rule if one exists |
113
+
114
+ **Line caps:** hard cap 150 lines, soft target 50-100. A gotcha that needs more than 150 lines is two gotchas, or it's narrative debug logs that don't belong here.
115
+
116
+ **Citations:** every section needs at least one. Code references use `file:line@<sha7>` form. LINT auto-fills missing `@<sha7>` on first save.
117
+
118
+ Skeleton example: see `aiwiki/schemas/gotcha.md`.
119
+
120
+ ### Step 5: Auto-promotion path (1 → 2 → 3 occurrences)
121
+
122
+ The `occurrences` field drives `status` automatically. Step 3 bumps the count; this step records the corresponding state transition.
123
+
124
+ | Occurrences | `status` | What happens |
125
+ |---|---|---|
126
+ | 1 | `active` | Newly recorded. Default state. |
127
+ | 2 | `watch` | Pattern is accumulating. `gotcha-hunter` surfaces as "one more triggers promotion". No rule drafted yet. |
128
+ | 3 | `promotion-pending` | Auto-draft a proposed rule into frontmatter (see below). The next session-start surfaces this as a hard-interrupt. |
129
+ | 4+ | stays `promotion-pending` | Until the user reviews (Step 6), additional occurrences keep bumping `occurrences` but `status` stays at `promotion-pending`. |
130
+
131
+ **When occurrences hits 3 — draft the proposed rule (mechanical only):**
132
+
133
+ 1. Set frontmatter `status: promotion-pending`.
134
+ 2. Identify the target rule file from the **Promotion paths** table below.
135
+ 3. Draft a one-to-three-sentence actionable rule based on the `## Prevention` section. Keep it imperative ("must / never / always") so it reads as a rule, not a tip.
136
+ 4. Add a `proposed_rule:` block to the frontmatter:
137
+
138
+ ```yaml
139
+ proposed_rule:
140
+ rule_path: rules/common/{relevant-rule}.md
141
+ draft: |
142
+ {actionable rule statement}
143
+ drafted_at: YYYY-MM-DD
144
+ ```
145
+
146
+ 5. Surface to the user: "3rd occurrence of <title> recorded. Proposed rule drafted in frontmatter; awaiting review at next session start."
147
+
148
+ Do not edit any rule file at this step. Drafting is mechanical; promotion is reviewed at Step 6.
149
+
150
+ **Promotion paths:**
151
+
152
+ | Current page | Promotion target | When |
153
+ |---|---|---|
154
+ | Project gotcha (`aiwiki/gotchas/`) | Same gotcha as global (`~/.claude/gotchas/`) | Same pattern seen in a different project |
155
+ | Project gotcha at occurrences=3 | Project rule (`.claude/rules/<area>.md`) — typically a phase-conditional or always-on rule | 3× in same project |
156
+ | Global gotcha at occurrences=3 | Common rule (`rules/common/<area>.md`) | 3× across projects |
157
+
158
+ ### Step 6: Session-start review of pending promotions
159
+
160
+ When session-start surfaces a gotcha with `status: promotion-pending` (always carries a `proposed_rule:` block), do not start new work. Process each one:
161
+
162
+ 1. Read the gotcha's `## Prevention` section and the `proposed_rule.draft` value.
163
+ 2. Ask the user: approve, defer, or reject.
164
+
165
+ **On approve:**
166
+ - Append the `draft` rule text to the file at `proposed_rule.rule_path` (create the file if needed; match its existing list/heading style).
167
+ - Invoke `support-skill-validator` against the modified rule file. If it reports a contradiction with existing rules, revert the rule-file edit and surface the contradiction. The user resolves the conflict and re-runs this step.
168
+ - On validator pass: change the gotcha's frontmatter `status: promotion-pending` → `status: promoted-to-rule`. Remove the `proposed_rule:` block. Cross-reference the rule path under `## Prevention`.
169
+ - Also update any other gotchas that share the same pattern with a `Promoted to rule: <rule_path>` line under `## Prevention`.
170
+
171
+ **On defer:** Leave `status: promotion-pending` and the `proposed_rule:` block in frontmatter. Append a `defer:` field with the user's rationale and date. Next session-start surfaces it again.
172
+
173
+ **On reject:** Change frontmatter `status: promotion-pending` → `status: rejected`. Remove the `proposed_rule:` block. Add a one-line note under `## Prevention`: "Proposed rule rejected ({date}): {reason}". The gotcha is retained as a recorded lesson, but session-start will not re-prompt and `gotcha-hunter` skips it at session-start scope.
174
+
175
+ ### Step 7: Retiring a gotcha
176
+
177
+ When a gotcha no longer applies (framework upgrade made it impossible, code path was removed, library fixed the underlying issue):
178
+
179
+ - Change frontmatter `status` → `status: retired` (regardless of prior state)
180
+ - Add a one-line note under `## Prevention`: "Retired ({date}): {reason}"
181
+
182
+ LINT preserves retired entries — they're still searchable history. `gotcha-hunter` skips retired entries when surfacing context to reviewers.
183
+
184
+ ## Severity classification
185
+
186
+ | Severity | Criteria | Example |
187
+ |---|---|---|
188
+ | `critical` | Data loss, security breach, production outage | Migration deletes production data |
189
+ | `high` | Significant time waste (1+ hour), incorrect behavior shipped | Subtle race condition in auth |
190
+ | `medium` | Moderate time waste (15-60 min), development friction | Docker seed duplication |
191
+ | `low` | Minor inconvenience, cosmetic, easily worked around | Wrong import path convention |
192
+
193
+ ## Status values (schema-canonical)
194
+
195
+ The unified status vocabulary spans the auto-promotion path and manual retirement. Transitions are governed by `occurrences` (auto) and user review (manual). `gotcha-hunter` and `support-wiki-lint` both validate against this enum.
196
+
197
+ | Status | Meaning | How entered |
198
+ |---|---|---|
199
+ | `active` | Newly recorded; `occurrences = 1`. Default state. | Step 4 writes a new gotcha. |
200
+ | `watch` | `occurrences = 2`. Pattern is accumulating; one more occurrence triggers promotion drafting. | Step 3 bumps a 1× gotcha to 2×. |
201
+ | `promotion-pending` | `occurrences = 3` (or more, until reviewed). A `proposed_rule:` block is attached. Awaits user review at next session-start. | Step 5 auto-drafts. |
202
+ | `promoted-to-rule` | The proposed rule was approved and appended to a rule file. Gotcha retained as cross-referenced provenance. | Step 6 approve path. |
203
+ | `rejected` | User reviewed a `promotion-pending` gotcha and declined promotion. Do not re-prompt. Lesson stays recorded; `gotcha-hunter` skips at session-start scope. | Step 6 reject path. |
204
+ | `retired` | Gotcha no longer applies (framework upgrade, code path removed, library fixed). Kept for searchable history; `gotcha-hunter` skips. | Step 7 manual. |
205
+
206
+ ## Session-start integration
207
+
208
+ A session-start hook scans `aiwiki/gotchas/INDEX.md` (and `~/.claude/gotchas/INDEX.md`) for rows whose `status` cell is `promotion-pending` and surfaces them as a hard-interrupt before new work begins. Step 6 is the gate between "draft rule" and "ship rule".
209
+
210
+ When no pending promotions exist, `gotcha-hunter` (separate agent) reads `aiwiki/gotchas/` to surface diff-relevant entries to reviewers in the `quality-code-review` chain.
211
+
212
+ ## Red flags — record a gotcha
213
+
214
+ | Situation | Why |
215
+ |---|---|
216
+ | "Oh, I've seen this before" | If you've seen it and it isn't recorded, it will happen again |
217
+ | Fix took more than 3 attempts | The debugging path itself is worth recording |
218
+ | Library behaved differently than documented | Future sessions will hit this too |
219
+ | "Works on my machine" issues | Environment differences are always gotcha-worthy |
220
+ | Skipped a process step and paid for it | Process-compliance lessons are valuable |
221
+ | Found a workaround instead of a fix | Both the workaround and the underlying issue need documenting |
222
+ | Deployment had unexpected complications | Deployment lessons prevent outages |
223
+ | Test was flaky or timing-dependent | Document the pattern; flaky tests erode confidence |
224
+
225
+ ## Integration with other skills and agents
226
+
227
+ | Skill / agent | Interaction |
228
+ |---|---|
229
+ | `support-debug` | Invokes this skill after a successful fix |
230
+ | `deliver-db-migration` | Records migration-related lessons |
231
+ | `deliver-deploy` | Records deployment-related lessons |
232
+ | `deliver-onboarding` | Reads `aiwiki/gotchas/` to populate troubleshooting sections |
233
+ | `support-skill-validator` | Validates promoted rules at the approve step (Step 6) |
234
+ | `gotcha-hunter` (agent) | Reads `aiwiki/gotchas/` and surfaces diff-relevant entries to reviewers; never writes |
235
+ | `support-wiki-lint` | Validates frontmatter + sections on every write |
236
+ | `support-dream` | At phase-close / PreCompact: consolidates raw entries, may merge similar gotchas, prunes retired entries (writes proposals to `aiwiki/proposed/{dream_id}/` for user review) |
237
+ | `/evolve` command | Reads gotchas to identify skill improvement opportunities |
238
+
239
+ ## Quick reference
240
+
241
+ | Step | Action | Output |
242
+ |---|---|---|
243
+ | 1. Extract | What broke / root cause / prevention | Three-part lesson |
244
+ | 2. Classify | Project or global? | Storage location chosen |
245
+ | 3. Search | Existing matching gotcha? | Bump `occurrences` instead of writing duplicate |
246
+ | 4. Write | Schema-aligned page with required frontmatter + 5 sections | `aiwiki/gotchas/{YYYY-MM-DD}-{slug}.md` (`status: active`) |
247
+ | 5. Auto-promote | 1 → `active`; 2 → `watch`; 3 → `promotion-pending` + `proposed_rule:` block | Awaits session-start review |
248
+ | 6. Session-start review | Approve / defer / reject the proposed rule | `promoted-to-rule`, deferred (stays `promotion-pending`), or `rejected` |
249
+ | 7. Retire (when no longer applies) | Set `status: retired` | Kept as searchable history |
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: support-runtime-reachability
3
+ description: "Use to verify that every export in the slice diff has a production caller — the runtime-reach gate. Catches orphan exports (code defined but never reached at runtime) before the slice closes."
4
+ ---
5
+
6
+ # Support: Runtime Reachability
7
+
8
+ ## Overview
9
+
10
+ An export with no production caller is wiring debt: code that exists, passes its own tests against mocks, and ships unreached by production. Pattern review can't catch this — each file looks correct in isolation. This skill walks the slice diff, finds the exports it introduced, and proves each one is actually called.
11
+
12
+ **Core principle:** wiring is reachability. If production never calls it, it doesn't exist.
13
+
14
+ **Announce at start:** "I'm using the support-runtime-reachability skill to verify slice exports are wired."
15
+
16
+ ## When to Use
17
+
18
+ - Per-slice gate after `build-tdd` completes — writes the `runtime-reach` gate result on the slice.
19
+ - Before invoking `quality-code-review`'s pattern-conformance pass.
20
+ - On-demand when investigating "code shipped but doesn't run".
21
+
22
+ **Do NOT skip when:**
23
+ - The slice "feels small" — the most common orphans are tiny (a hook, a route, a handler).
24
+ - Tests pass — mocked tests prove behavior under test, not under production wiring.
25
+ - A reviewer already approved the diff — pattern review and reachability are different bug classes.
26
+
27
+ ## Scope
28
+
29
+ Catches **export-level orphans** in TS/JS and Python: a top-level `export` / `def` / `class` with no production call site and no escape-hatch annotation. Other failure classes are someone else's job:
30
+
31
+ | Failure | Owner |
32
+ |---|---|
33
+ | No-op stubs (function called, body empty) | `craft-reviewer` Pass 2 |
34
+ | Internal binding misses (handler not bound to JSX prop) | human review |
35
+ | Method-on-object misses (`store.setUser` never called, where `store` is the export) | human review |
36
+ | Behavioral completeness (caller exists, logic incomplete) | behavioral review |
37
+ | Transitive runtime unreachability (caller exists, but its calling path is dead) | the `quality-test-execution` phase |
38
+
39
+ Do not try to fold these into the runtime-reach result.
40
+
41
+ ## I/O Contract
42
+
43
+ | Field | Value |
44
+ |---|---|
45
+ | **Requires** | Slice diff file list, manifest path |
46
+ | **Produces** | `runtime-reach` gate result (pass / fail with orphan list) |
47
+ | **Feeds into** | `quality-code-review` (Pass 2 + reachability), slice-close gate |
48
+ | **Updates manifest** | `slice_graph.slices.<id>.gates.runtime-reach.{status, gate-passed}` |
49
+
50
+ ## Process
51
+
52
+ ### Step 0: locate inputs
53
+
54
+ Discover what the script needs before invoking it. Each input has a fallback if the primary source isn't available.
55
+
56
+ | Input | How to find |
57
+ |---|---|
58
+ | Repo root | `git rev-parse --show-toplevel`, or `${CLAUDE_PROJECT_DIR}` if set. |
59
+ | Manifest path | The single in-progress manifest under `.forge/work/*/*/manifest.yaml`. If multiple, ask the user which slice graph to validate against. |
60
+ | Current slice id | `slice_graph.current_slice` field of that manifest. If the manifest has no `slice_graph` block, surface the gap and stop — this gate has nothing to attach its result to. |
61
+ | Slice-parent ref | `slice_graph.slices.<current>.depends_on[0]`'s commit pointer if recorded; otherwise fall back to `origin/main`. If the repo has no `origin/main`, ask the user which ref to diff against. |
62
+ | Script path | `<repo-root>/.claude/skills/support-runtime-reachability/scripts/check.mjs`. If missing, the skill isn't installed in this project — surface the gap. |
63
+
64
+ ### Step 1: collect slice diff files
65
+
66
+ ```bash
67
+ git -C <repo-root> diff --name-only <slice-parent>...HEAD \
68
+ -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs' '*.cjs' '*.py'
69
+ ```
70
+
71
+ Skip deleted files. If the diff is empty (slice has no code changes), the gate passes trivially with `exports_found: 0`.
72
+
73
+ ### Step 2: invoke the check script
74
+
75
+ ```bash
76
+ node "<repo-root>/.claude/skills/support-runtime-reachability/scripts/check.mjs" \
77
+ --files <comma-separated-paths-from-step-1> \
78
+ --root "<repo-root>"
79
+ ```
80
+
81
+ Output: JSON to stdout, exit 0 on success and exit 2 on internal error. The `exports` array reports per-export production callers, test callers, and any escape-hatch annotation found.
82
+
83
+ ### Step 3: cross-check annotations against the manifest
84
+
85
+ The script reports what it sees; you decide validity using the slice graph.
86
+
87
+ **Decision rules:**
88
+
89
+ | Script reports | Manifest cross-check | Verdict |
90
+ |---|---|---|
91
+ | `production_callers ≥ 1` | — | reachable |
92
+ | `production_callers = 0`, `annotation = null` | — | **orphan** |
93
+ | `annotation.kind = "untraceable"`, `value` non-empty | — | annotated, pass |
94
+ | `annotation.kind = "untraceable"`, `value` empty/whitespace | — | **malformed annotation** |
95
+ | `annotation.kind = "gated-pending"`, slice exists, status ∉ {complete} | — | annotated, pass |
96
+ | `annotation.kind = "gated-pending"`, slice missing OR status `complete` | — | **stale annotation** |
97
+
98
+ ### Step 4: write the gate result
99
+
100
+ | Outcome | Manifest write |
101
+ |---|---|
102
+ | All exports reachable or validly annotated | `gates.runtime-reach: { status: complete, gate-passed: true }` |
103
+ | Any orphan / stale / malformed | `gates.runtime-reach: { status: complete, gate-passed: false }` |
104
+
105
+ ### Step 5: report
106
+
107
+ If pass: one line — `runtime-reach: <N> exports checked, all wired.`
108
+
109
+ If fail: structured report:
110
+
111
+ ```markdown
112
+ ## runtime-reach: FAILED (<N> findings)
113
+
114
+ ### Orphan exports
115
+ 1. `src/handlers/cases.ts:3` exports `casesRouter` (const)
116
+ Fix: wire it (e.g. `app.use('/cases', casesRouter)`), annotate `// gated-pending: <slice-id>`, or delete.
117
+
118
+ ### Stale gated-pending
119
+ 2. `src/future.ts:5` exports `futureHandler` annotated `// gated-pending: phase-2-wiring`,
120
+ but slice `phase-2-wiring` is `complete`. Wire it now.
121
+
122
+ ### Malformed untraceable
123
+ 3. `src/lazy.ts:2` exports `lazy` annotated `// untraceable:` with no rationale.
124
+ ```
125
+
126
+ The user fixes; re-run.
127
+
128
+ ## Annotations
129
+
130
+ Place on the line immediately preceding the export, or trailing on the same line:
131
+
132
+ ```ts
133
+ // gated-pending: <slice-id>
134
+ export function futureHandler() { ... }
135
+
136
+ export const lateMount = ...; // gated-pending: phase-2-wiring
137
+ ```
138
+
139
+ ```python
140
+ # gated-pending: phase-2-wiring
141
+ def future_handler():
142
+ pass
143
+ ```
144
+
145
+ | Annotation | Use when |
146
+ |---|---|
147
+ | `// gated-pending: <slice-id>` | Export is intentionally orphan in this slice; another (named, non-complete) slice will wire it. |
148
+ | `// untraceable: <rationale>` | Export is consumed via mechanism the gate can't see: framework auto-discovery (Next.js routes), decorator mounts (NestJS, Spring), reflection-based loaders, defensive exports invoked only by upstream failures. |
149
+
150
+ Use line comments (`//` for TS/JS, `#` for Python). Block comments and annotations separated from the export by blank lines, JSDoc, or decorators are not parsed — place the annotation immediately above.
151
+
152
+ ## When to Suspect a Finding
153
+
154
+ When the script's verdict disagrees with what you can verify by reading the code, the cause is usually one of these. Each row's "signal" is something you can observe in the diff or a quick grep — no script-internals knowledge required.
155
+
156
+ | Signal in the code | Likely cause | Fix |
157
+ |---|---|---|
158
+ | Script reports orphan, but you can grep a usage in a file that imports via `@/...`, `~/...`, or a non-relative path other than a known npm package | Path-aliased import — script can't resolve the alias to the export's file | Annotate `// untraceable: path-alias-consumer` |
159
+ | Script reports orphan, but the only usage is inside an `import('./X')` call | Dynamic import not detected | Annotate `// untraceable: dynamic-import` |
160
+ | Script reports orphan on a Next.js route, NestJS controller, Spring-annotated handler, or similar framework-mounted file | Framework auto-discovery — no explicit caller exists | Annotate `// untraceable: framework-mounted` |
161
+ | Script reports orphan, but you can grep `export * from '<path-resolving-to-this-file>'` in a re-export module | Wildcard re-export not followed | Convert to a named re-export (`export { name } from ...`), or annotate |
162
+ | Two source files in the diff both export the same identifier name | Same-name across modules — caller could be credited to the wrong export | Read the script output's `production_callers[].file` to confirm which export the call resolves to; restructure if needed |
163
+ | Script reports a caller, but the caller file both imports `name` AND declares its own `const name = ...` / `function name(...)` | Local shadow — the call resolves to the local declaration, not the import | Rename the local; the script's name-based fallback can't distinguish shadows |
164
+
165
+ The script's other constraints: regex (not AST); ESM only (no CommonJS `module.exports`); commented-out callers don't count (line comments are stripped).
166
+
167
+ ## Red Flags
168
+
169
+ **Never:**
170
+ - Add a no-op caller (`useless();`) to satisfy reachability — that's gaming, not wiring.
171
+ - Annotate every orphan as `untraceable` to silence the gate. High annotation density signals architecture problems, not gate problems.
172
+ - Suppress findings to make the slice look clean.
173
+
174
+ **Always:**
175
+ - Resolve orphans before closing the slice.
176
+ - Re-run the script after every fix attempt.
177
+ - Annotate genuine framework-mounted exports so the next reviewer doesn't relitigate.
178
+
179
+ ## Integration
180
+
181
+ | Caller | When |
182
+ |---|---|
183
+ | `quality-code-review` | After Pass 1 safety and Pass 2 craft, before Codex adversarial cross-check. |
184
+ | `/feature` and `/refactor` slice-close | Per-slice gate. |
185
+ | Ad-hoc | When a reviewer suspects orphan code. |
186
+
187
+ | Pairs with | For |
188
+ |---|---|
189
+ | `craft-reviewer` | Stubs the reachability gate accepts as called-but-empty. |
190
+ | `verify-manifest` | Validates the slice graph this skill cross-references. |
@@ -0,0 +1,7 @@
1
+ import express from 'express';
2
+ import { casesRouter } from './handlers/cases';
3
+
4
+ const app = express();
5
+ app.use('/cases', casesRouter);
6
+
7
+ export { app };
@@ -0,0 +1,7 @@
1
+ import { Router } from 'express';
2
+
3
+ export const casesRouter = Router();
4
+
5
+ casesRouter.get('/', (req, res) => {
6
+ res.json({ cases: [] });
7
+ });
@@ -0,0 +1,8 @@
1
+ import express from 'express';
2
+ // REACH-style failure: imported but never mounted.
3
+ import { casesRouter } from './handlers/cases';
4
+
5
+ const app = express();
6
+ // app.use('/cases', casesRouter) <-- forgotten
7
+
8
+ export { app };
@@ -0,0 +1,7 @@
1
+ import { Router } from 'express';
2
+
3
+ export const casesRouter = Router();
4
+
5
+ casesRouter.get('/', (req, res) => {
6
+ res.json({ cases: [] });
7
+ });
@@ -0,0 +1,5 @@
1
+ import { RingingBanner } from './components/RingingBanner';
2
+
3
+ export function App() {
4
+ return <RingingBanner />;
5
+ }
@@ -0,0 +1,7 @@
1
+ // REACH-style failure: hook imported but never invoked.
2
+ import { useTwilioDevice } from '../hooks/useTwilio';
3
+
4
+ export function RingingBanner() {
5
+ // useTwilioDevice() <-- forgotten
6
+ return <div>Incoming call</div>;
7
+ }
@@ -0,0 +1,6 @@
1
+ export function useTwilioDevice() {
2
+ return {
3
+ accept: () => {},
4
+ reject: () => {},
5
+ };
6
+ }
@@ -0,0 +1,5 @@
1
+ import { MyComp } from './components/MyComp';
2
+
3
+ export function App() {
4
+ return <MyComp label="hello" />;
5
+ }
@@ -0,0 +1,3 @@
1
+ export function MyComp({ label }: { label: string }) {
2
+ return <span>{label}</span>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export function App() {
2
+ return <div>Hello</div>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export function Orphan() {
2
+ return <div>I'm not rendered anywhere</div>;
3
+ }
@@ -0,0 +1,6 @@
1
+ export class Service {
2
+ constructor(public name: string) {}
3
+ greet() {
4
+ return `hello from ${this.name}`;
5
+ }
6
+ }
@@ -0,0 +1,4 @@
1
+ import { Service } from './lib/Service';
2
+
3
+ const svc = new Service('alpha');
4
+ console.log(svc.greet());
@@ -0,0 +1,5 @@
1
+ export class Lonely {
2
+ greet() {
3
+ return 'no one calls me';
4
+ }
5
+ }
@@ -0,0 +1,2 @@
1
+ // Lonely is exported but never instantiated.
2
+ console.log('main runs without using Lonely');
@@ -0,0 +1,3 @@
1
+ export default function handler(req: { id: string }) {
2
+ return { ok: true, id: req.id };
3
+ }
@@ -0,0 +1,3 @@
1
+ import handle from './handler';
2
+
3
+ handle({ id: '42' });
@@ -0,0 +1,3 @@
1
+ export default function handler(req: { id: string }) {
2
+ return { ok: true, id: req.id };
3
+ }
@@ -0,0 +1,2 @@
1
+ // Default export from handler.ts is never imported.
2
+ console.log('main runs');
@@ -0,0 +1,5 @@
1
+ function foo() {
2
+ return 'foo';
3
+ }
4
+
5
+ export { foo as bar };
@@ -0,0 +1,3 @@
1
+ import { bar } from './lib';
2
+
3
+ console.log(bar());
@@ -0,0 +1,3 @@
1
+ export function doStuff(value: number) {
2
+ return value * 2;
3
+ }
@@ -0,0 +1,3 @@
1
+ import { doStuff } from './lib';
2
+
3
+ console.log(doStuff(21));
@@ -0,0 +1,5 @@
1
+ import { helperOnly } from './util';
2
+
3
+ test('helperOnly increments', () => {
4
+ if (helperOnly(1) !== 2) throw new Error('fail');
5
+ });
@@ -0,0 +1,3 @@
1
+ export function helperOnly(n: number) {
2
+ return n + 1;
3
+ }
@@ -0,0 +1,4 @@
1
+ // gated-pending: phase-2-wiring
2
+ export function futureHandler() {
3
+ return { ok: true };
4
+ }
@@ -0,0 +1,4 @@
1
+ // untraceable: framework-mounted (Next.js app router)
2
+ export default function GET(req: Request) {
3
+ return new Response('ok');
4
+ }
@@ -0,0 +1,4 @@
1
+ // untraceable:
2
+ export function lazy() {
3
+ return 1;
4
+ }
@@ -0,0 +1,15 @@
1
+ def public_fn(value):
2
+ return value + 1
3
+
4
+
5
+ def _private_helper(value):
6
+ return value * 2
7
+
8
+
9
+ class PublicClass:
10
+ def __init__(self, name):
11
+ self.name = name
12
+
13
+
14
+ class _PrivateClass:
15
+ pass
@@ -0,0 +1,5 @@
1
+ from .lib import public_fn
2
+
3
+ # PublicClass is exported but never instantiated -> orphan.
4
+
5
+ print(public_fn(41))