@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,188 @@
1
+ # Gotchas — bugs that took multiple iterations to find
2
+
3
+ Read this when something renders wrong despite following the rules. These are real bugs that appeared during the 7-iteration build and are easy to repeat.
4
+
5
+ ## Layout traps
6
+
7
+ ### Sticky tab bar covers top callouts
8
+
9
+ **Symptom:** Top-side callout labels are missing from the rendered output, or appear *under* the tab strip.
10
+
11
+ **Cause:** Tab bar has `sticky top-3 z-20`. Top callouts are positioned at `bottom: stage_height + margin + label_height` from the bottom of the parent — which puts them in the y-range of the sticky tab bar. Tab bar's z-index wins because `z-20 > z-5` on Callouts.
12
+
13
+ **Fix:** Drop `sticky` from the tab bar. Make it `flex flex-wrap items-center gap-x-4 gap-y-2` in normal flow. Then ensure the stage wrapper has `mt-32` (128 px) to give top callouts breathing room below the tab bar's natural height (~106 px when wrapped to 2 rows).
14
+
15
+ ### Side-callout labels clipped at viewport edge
16
+
17
+ **Symptom:** Left/right callout labels have their first/last few characters cut off.
18
+
19
+ **Cause:** The label box is 230 px wide and sits 60 px outside the stage edge — total footprint 290 px. If the gutter is < 290 px, the label overflows the page container. With a body that has `overflow: hidden` (or even default behavior on narrow viewports), the overflow is clipped.
20
+
21
+ **Fix:** Use 320 px gutters minimum. Update `marginLeft: 320, marginRight: 320, width: "calc(100% - 640px)"` on the stage container. On a 1920 viewport this gives a 1280-px-wide stage at scale 0.667, with 320 px of clear gutter space on each side.
22
+
23
+ ### Stage scaled too small on wide monitors
24
+
25
+ **Symptom:** On a 4K screen, the wireframe sits in the middle at ~60% of available width.
26
+
27
+ **Cause:** Outer container has `max-w-[1680px] mx-auto` and stage has `maxWidth: 1280`. Both cap the size.
28
+
29
+ **Fix:** Outer container `style={{maxWidth: 2400}}`, stage `maxWidth: 1920`. Now on a wide monitor the stage hits 1:1 scale instead of being capped at 67%.
30
+
31
+ ## Callout traps
32
+
33
+ ### Two callouts pointing at the same `id`
34
+
35
+ **Symptom:** Two leader lines terminate at the same dot.
36
+
37
+ **Cause:** The `_CALLOUTS` array has two entries with the same `id`. The Callouts overlay queries by id and renders both leaders pointing to the same anchor.
38
+
39
+ **Fix:** Each callout needs a unique `id`. If you want to label the same element from two angles (rare and usually wrong), give each angle its own anchor with a distinct id.
40
+
41
+ ### Zero-length leader (label flush against stage edge)
42
+
43
+ **Symptom:** A side label has effectively no visible leader; the dot is right at the wireframe boundary.
44
+
45
+ **Cause:** The element is at the wireframe edge AND `anchor` is on the same edge. E.g. WhySheet is flush-right; setting `anchor: "right"` puts the dot on the right edge of the sheet, which IS the wireframe right edge — leader has zero horizontal travel.
46
+
47
+ **Fix:** Flip `anchor` to the opposite edge so the leader has visible travel. `WhySheet` callouts use `anchor: "left"` so the dot lands inside the sheet near its left boundary; the leader travels right across the sheet to the gutter.
48
+
49
+ ### Leader cuts across the wrong row
50
+
51
+ **Symptom:** A multi-column row (e.g. announcements + schedule + recent) gets a callout described as "this row of supporting components" but the leader points at one specific column instead.
52
+
53
+ **Cause:** Anchor span is inside ONE cell of the grid (e.g. the announcements column). Its bounding rect is the cell's right edge, which is in the middle of the row.
54
+
55
+ **Fix:** Move the span out of the inner cell. Add `relative` to the OUTER row wrapper and host the span there at `right-0 top-1/2`. Now the dot lands at the row's outer right edge, which (when the centre extends to the wireframe edge) is at the wireframe's right border. Reads as "this whole row".
56
+
57
+ ### Leader goes to the FAR gutter
58
+
59
+ **Symptom:** A banner at the top of the wireframe has its label in the bottom gutter; a button in the right rail has its label in the bottom gutter.
60
+
61
+ **Cause:** Wrong `side` chosen. The rule is "closest gutter wins" — but it's tempting to default to `bottom` for action buttons or `top` for global elements without thinking about which gutter is actually closest.
62
+
63
+ **Fix:** Pick `side` by physical proximity to the anchor.
64
+ - Top-of-stage banner → `side: "top"`
65
+ - Right-rail button → `side: "right"`
66
+ - Bottom-of-stage footer → `side: "bottom"`
67
+ - Sidebar item → `side: "left"`
68
+
69
+ The leader should never cross the stage to reach a far gutter.
70
+
71
+ ## Demo state traps
72
+
73
+ ### Per-step callouts disappear immediately on demo mount
74
+
75
+ **Symptom:** The demo's step 0 has no callouts visible. Console is clean. The anchor IDs are present in the DOM. The CALLOUTS array has entries.
76
+
77
+ **Cause:** App component has a useEffect that resets dynamic callouts on `[active]`. Parent useEffects run AFTER child useEffects, so:
78
+ 1. Demo mounts, useEffect fires, pushes step-0 callouts
79
+ 2. App's reset useEffect fires, sets dynamicCallouts back to null
80
+ 3. Re-render with no callouts
81
+
82
+ **Fix:** Don't reset in App. Move the reset to the demo's useEffect cleanup function:
83
+
84
+ ```jsx
85
+ React.useEffect(() => {
86
+ if (setCallouts) setCallouts(D1_STEP_CALLOUTS[step] || []);
87
+ return () => { if (setCallouts) setCallouts(null); }; // cleanup on unmount
88
+ }, [step, setCallouts]);
89
+ ```
90
+
91
+ When the demo unmounts (view change), cleanup runs and nulls the callouts. New view's mount push (or static view's nothing) takes over cleanly.
92
+
93
+ ### Inbox highlighted case is wrong channel after Accept
94
+
95
+ **Symptom:** D1 inbound demo accepts a phone call, but the inbox's highlighted row is an EMAIL case for the same customer.
96
+
97
+ **Cause:** The demo passed `activeCaseId="CS-4860"` — but CS-4860 is the existing email row in the shared `LeftCaseList`. The demo treated it as the new call case but the inbox doesn't have a phone row for that customer.
98
+
99
+ **Fix:** Use the `injectActive` prop on `LeftCaseList`:
100
+
101
+ ```jsx
102
+ <LeftCaseList
103
+ activeCaseId="CS-4861"
104
+ injectActive={{id: "CS-4861", name: "Chan · live call", channel: "phone"}}
105
+ />
106
+ ```
107
+
108
+ This prepends a fresh phone row at the top of the Open section, bumps the count by 1, and highlights it. The existing email row stays below — modeling that the customer might have multiple cases.
109
+
110
+ ### Two demos share the wrap-up modal but their content diverges
111
+
112
+ **Symptom:** The demo's wrap-up has fewer fields than S7's. Tags / Case status / merge nudge missing.
113
+
114
+ **Cause:** A `DemoWrapUpModal` was created as a stripped-down clone of S7's `WrapUpModal`. They drift over time — one gets a new field, the other doesn't.
115
+
116
+ **Fix:** Don't fork. Refactor S7's `WrapUpModal` to accept `onSave`, `onHome`, `headline`, `subline`, `saveAnchorId` props (all optional, defaults match S7's existing behavior). Demos use the same component with their props. Single source of truth.
117
+
118
+ ## Component / styling traps
119
+
120
+ ### `bg-amber-50/50` matches the same selector as callout label boxes
121
+
122
+ **Symptom:** Counting callout labels via `[class*="bg-amber-50"][class*="border-amber-200"][class*="rounded"]` returns more labels than expected.
123
+
124
+ **Cause:** The `AnnouncementsBlock` "System" announcement row has `bg-amber-50/50` + `border-amber-200` + `rounded-md`. Selector matches.
125
+
126
+ **Fix:** Use a more specific selector that includes `text-center` (which only callout labels have):
127
+
128
+ ```js
129
+ document.querySelectorAll('.pointer-events-none [class*="text-center"][class*="bg-amber-50"]')
130
+ ```
131
+
132
+ ### Browser caches the artifact across iterations
133
+
134
+ **Symptom:** Edits to the file aren't reflected in the browser even after reload.
135
+
136
+ **Fix:** Add a cache buster to the URL: `http://localhost:8767/index.html?v=2#s1`. Bump `v` each iteration.
137
+
138
+ ### `file://` protocol blocked in Playwright
139
+
140
+ **Symptom:** Trying to navigate to `file:///path/to/index.html` in Playwright produces "Access to file: protocol is blocked".
141
+
142
+ **Fix:** Run a local server: `npx http-server -p 8767 -c-1 .` from the wireframe directory. Then navigate to `http://localhost:8767/index.html`.
143
+
144
+ ## Verification recipe (Playwright)
145
+
146
+ When you've made callout changes, this query batch-checks every state for clipping and tab-overlap issues:
147
+
148
+ ```js
149
+ async () => {
150
+ const views = ['s1','s2','s3','s4','s5','s6','s7','sup1','sup2','sup3','sup4','sup5'];
151
+ const out = {};
152
+ for (const v of views) {
153
+ window.location.hash = '#' + v;
154
+ await new Promise(r => setTimeout(r, 400));
155
+ const labels = Array.from(document.querySelectorAll('.pointer-events-none [class*="text-center"][class*="bg-amber-50"]'));
156
+ const tabbar = document.querySelector('[class*="flex flex-wrap"][class*="border-border"][class*="rounded-md"]');
157
+ const tabBottom = tabbar ? tabbar.getBoundingClientRect().bottom : 0;
158
+ const issues = [];
159
+ labels.forEach(l => {
160
+ const r = l.getBoundingClientRect();
161
+ if (r.left < 0) issues.push('L'+Math.round(-r.left));
162
+ if (r.right > window.innerWidth) issues.push('R'+Math.round(r.right - window.innerWidth));
163
+ if (r.top < tabBottom + 4) issues.push('T');
164
+ });
165
+ out[v] = {n: labels.length, issues};
166
+ }
167
+ return out;
168
+ }
169
+ ```
170
+
171
+ Goal: every state has `issues: []`. If any view shows L/R/T entries, look up the matching gotcha above.
172
+
173
+ ## Iteration history (one-line each)
174
+
175
+ These are the iterations the wireframe artifact went through. Each one closed a real bug.
176
+
177
+ - **i1** — Initial 14-state build.
178
+ - **i2** — Removed duplicate-anchor entries, larger flow thumbs, 3 interactive demo tabs.
179
+ - **i3** — Wider stage, tab-bar wraps, callouts 30→48 (rule 4 flipped from "less" to "comprehensive").
180
+ - **i4** — Playwright-verified everything; tab-bar non-sticky; gutter 220→320; rule 7 v1 added.
181
+ - **i5a–b** — Sidebar/Inbox leaders moved to left edge; KPI footer to bottom; announcements to outer row wrapper.
182
+ - **i5c** — Per-step demo callouts wired up; race condition fixed via cleanup-on-unmount.
183
+ - **i5d** — Rule 7 generalized: closest path wins for `side` AND `anchor`.
184
+ - **i5e** — Wrap-up modal de-duplicated; one canonical component for S7 + demos.
185
+ - **i5f** — D3 email composer became a real editor (toolbar + To/Subject + body) with templates rail.
186
+ - **i5g** — Inbound demo highlights fresh phone case via `injectActive`, not stale email row.
187
+
188
+ Don't try to skip directly to "the final state" — the rules above only make sense if you understand what they're protecting against. When stuck, find the matching iteration and read its log entry.
@@ -0,0 +1,141 @@
1
+ # Legend lines — visual reference
2
+
3
+ This is the consolidated rulebook for the dashed-line callouts that connect outer-gutter labels to dots inside the wireframe. The rules below are non-negotiable.
4
+
5
+ ## The pattern at a glance
6
+
7
+ Every callout has TWO halves: an anchor element inside the wireframe, and a CALLOUTS entry that describes how to draw the leader.
8
+
9
+ ```jsx
10
+ // inside a component — option 1: id directly on a content element
11
+ <div id="a-mything" className="...">...</div>
12
+
13
+ // inside a component — option 2: a 0-size span anchor positioned at a chosen edge
14
+ <div className="relative ...">
15
+ <span id="a-mything" className="absolute right-0 top-1/2" data-callout-anchor="" />
16
+ ... content ...
17
+ </div>
18
+
19
+ // then in the state's _CALLOUTS array
20
+ const SXyz_CALLOUTS = [
21
+ { id: "a-mything", side: "right", anchor: "right", label: "Description ≤ 80 chars" },
22
+ ];
23
+ ```
24
+
25
+ `side` picks the gutter the label lives in (`top` · `bottom` · `left` · `right`).
26
+ `anchor` picks which edge of the anchor element's bounding box the dot lands on (`left` · `right` · `top` · `bottom` · `center`).
27
+
28
+ For 0-size span anchors, the `anchor` field is essentially a no-op (the bbox has zero width/height) — the **span position is what matters**. Use `right-0 top-1/2` to put the dot at the right edge of the parent container, `left-0 top-1/2` for the left edge, `left-1/2 top-0` for top-centre, `left-1/2 bottom-0` for bottom-centre.
29
+
30
+ ## The 7 rules
31
+
32
+ ### 1. Anchor on the section's edge facing its label
33
+
34
+ A right-side label anchors on the section's right edge, not its centre. A left-side label anchors on the left edge. Why: the leader becomes a tiny stub from element-edge to gutter, instead of a long line cutting through the element.
35
+
36
+ ### 2. Leader is mostly a single straight run
37
+
38
+ One horizontal or vertical run from anchor to label. No diagonals through the wireframe. The render code uses an L-shaped elbow when label.y ≠ anchor.y; rule 3 governs where the bend happens.
39
+
40
+ ### 3. Bend only in the gutter, never over UI
41
+
42
+ When the label sits at a different y from the anchor (collision pushed it), the leader bends 16px inside the gutter — never crossing the wireframe stage. The render code already handles this (look for `elbowX = p.lx ± 16` in the Callouts component). Don't introduce custom paths that route through UI.
43
+
44
+ ### 4. Comprehensive over minimal
45
+
46
+ Aim for **5–8 callouts per main-shell state**, covering each chrome column AND each section of the main-content area. Earlier versions of this skill said "less is more · 2-4 callouts" — that turned out to be wrong for presales artifacts where viewers want the entire UI explained. Drop a callout only if its leader genuinely cannot avoid crossing other UI.
47
+
48
+ The split: column callouts describe shell elements (topbar, sidebar, case list, right rail). Section callouts describe content (each section in the main centre). Both are valued.
49
+
50
+ ### 5. Collision-avoidance push direction is fixed
51
+
52
+ When two labels on the same gutter would overlap, push later-in-DOM-order labels:
53
+ - Top/bottom labels: push right (later moves x+).
54
+ - Left/right labels: push down (later moves y+).
55
+
56
+ This keeps each label visually paired with its anchor — the eye expects to look down/right for the next callout, not up or left.
57
+
58
+ ### 6. Overlay states annotate the overlay, not the underlying shell
59
+
60
+ Modal / sheet / banner states (Command palette, Why? sheet, Inbound banner, Wrap-up modal) draw their callouts targeting overlay anchors. The dimmed shell underneath is context, not content. Don't double up by annotating the shell at the same time.
61
+
62
+ ### 7. Closest path wins — for both `side` and `anchor`
63
+
64
+ This is the most important rule and the one most often violated.
65
+
66
+ Pick the **side** whose gutter is NEAREST the anchor element. Then anchor on the element's edge facing that gutter.
67
+
68
+ - A banner at the top of the wireframe gets `side: "top"`, NEVER `"bottom"`. The leader should never cross the stage to reach a far gutter.
69
+ - A toast in the top-right corner gets `side: "top"` (or `"right"`).
70
+ - A button in the right rail gets `side: "right"`, even if the button is at the bottom of the rail.
71
+ - The KPI footer at the bottom of the wireframe gets `side: "bottom"`.
72
+
73
+ For row-spanning components (a row of cards · a footer strip · a multi-column row), the dot lands on the outer ROW's edge — humans read that as "this row of components" rather than "this single column". Add `relative` to the outer row wrapper and host the anchor span there.
74
+
75
+ ## Common patterns table
76
+
77
+ | Component type | Side | Anchor edge |
78
+ |---|---|---|
79
+ | Sidebar (column on left edge of stage) | left | left |
80
+ | Inbox / case list (column to right of sidebar) | left | left (so dot is at the column's left boundary, not buried inside) |
81
+ | Right rail (column on right edge of stage) | right | left or right (whichever is the gutter side) |
82
+ | Topbar | top | bottom or top |
83
+ | Slide-down banner at top of stage | top | top |
84
+ | Top-right corner toast | top | top |
85
+ | KPI footer | bottom | bottom |
86
+ | Channel-cards row | right | right |
87
+ | Multi-column row (announcements + wrap-ups + recent) | right | right of the OUTER row wrapper |
88
+ | Modal centred on screen | left or right | the matching edge of the modal |
89
+ | Slide-in sheet flush against wireframe right | right | left (so the leader has visible travel through the sheet) |
90
+ | Single in-row cell (e.g. table column header on left side of a wide row) | left | matching edge of the cell |
91
+
92
+ ## Anchor placement quick reference
93
+
94
+ Where to put the `<span id="..." className="absolute ..." />` based on what edge you want the dot on:
95
+
96
+ | Span position | Dot lands at | Use case |
97
+ |---|---|---|
98
+ | `right-0 top-1/2` | Right edge, vertical centre | Right-side callout pointing into the parent's right edge |
99
+ | `left-0 top-1/2` | Left edge, vertical centre | Left-side callout |
100
+ | `left-1/2 top-0` | Top edge, horizontal centre | Top-side callout (banner, topbar) |
101
+ | `left-1/2 bottom-0` | Bottom edge, horizontal centre | Bottom-side callout (KPI footer, button at bottom of section) |
102
+
103
+ For row-spanning callouts: put the span at `right-0 top-1/2` of the OUTER row wrapper (which is `relative`), not inside an inner cell.
104
+
105
+ ## Stage geometry (the magic numbers)
106
+
107
+ | Knob | Value | Notes |
108
+ |---|---|---|
109
+ | Stage design size | 1920 × 1080 | Fixed; everything scales from this |
110
+ | Outer container max-width | 2400 px | Allows 1:1 scale on wide monitors |
111
+ | Stage container maxWidth | 1920 px | Caps scale = 1.0 |
112
+ | Gutter (each side) | 320 px | Hard constraint: ≥ label_width + offset + slack |
113
+ | Top/bottom callout offset | 60 px | Distance from stage edge to dot |
114
+ | Top padding above stage | mt-32 = 128 px | Tab-bar clearance |
115
+ | Label width | 230 px | Fits ~2 lines of 12px text |
116
+ | Label height (estimated) | ~32 px | Two-line labels render up to ~45px |
117
+
118
+ The interlocking constraints:
119
+ - `gutter ≥ label_width + offset` — must hold or labels clip past viewport edge
120
+ - `top_padding ≥ tab_bar_height + label_height + buffer` — must hold or top labels hide behind tab bar
121
+ - Outer max-width should be ≥ stage_width + 2 × gutter — so on max-width monitor, no compression
122
+
123
+ ## When NOT to add a callout
124
+
125
+ - Pure flow views (`agent-flow`, `sup-flow`) — the layout itself is the explanation.
126
+ - A second callout pointing at the same `id` as another callout in the same view — one is enough; duplicate IDs produce two leaders into the same dot.
127
+ - An element whose function is obvious from its on-screen label.
128
+
129
+ ## Iteration history (where these rules came from)
130
+
131
+ The rules above didn't come out of one design pass. They came from a 7-iteration loop with a real user reviewing real outputs:
132
+
133
+ 1. **i1**: Initial build of 14 wireframe states. Naive callouts.
134
+ 2. **i2**: Removed duplicate-anchor entries (S4/S6 had two callouts targeting same id), routed flow tabs more cleanly, added 3 interactive demo tabs.
135
+ 3. **i3**: User said "page is too small, tabs cut off, need column callouts". Stage gutters expanded, tab bar wraps, callout density bumped to 5–8/state. **Rule 4 flipped from "less is more" to "comprehensive".**
136
+ 4. **i4**: Playwright-verified everything. Bug found: tab bar covered top callouts (it was `sticky`). Removed sticky, bumped gutter further, added top padding. **Rule 7 v1 added: "edge nearest the gutter wins".**
137
+ 5. **i5a–b**: Sidebar + Inbox leaders moved to left edge of column. KPI footer leader moved to bottom border. Announcements anchor moved to outer row wrapper. **Edge-nearest-gutter became universal.**
138
+ 6. **i5c**: Per-step demo callouts wired up. App-level dynamicCallouts state. Race condition discovered (parent reset useEffect overrides child mount push) → fixed via cleanup-on-unmount.
139
+ 7. **i5d**: User pointed out banner callouts were going to the FAR gutter. **Rule 7 generalized: closest path wins for `side` AND `anchor`.**
140
+
141
+ Each iteration came with browser-rendered evidence. If a rule above feels arbitrary, it isn't — it's the answer to a real bug.
@@ -0,0 +1,192 @@
1
+ ---
2
+ name: concept-slides
3
+ description: "Use to produce a marp slide deck that captures a product or feature concept at low fidelity — hook, concept overview, visual sketches, user journey, what's deferred. Iterate with the user before any wireframing or prototyping. The deck is the lowest-fidelity artifact in the pipeline; it answers 'are we building the right thing?' before fidelity escalates."
4
+ ---
5
+
6
+ # Concept Slides
7
+
8
+ ## Overview
9
+
10
+ A concept deck is the cheapest artifact in the pipeline. It exists to verify the WHAT before the team commits to the HOW. The deck has hook + 3-5 sub-concepts + visual sketches + user journey + explicit deferrals. That's it. If the user can't react to a 5-15 slide deck, they can't react to a wireframe either — start here.
11
+
12
+ **Core principle:** low fidelity is the feature. The deck is meant to be edited, rejected, redirected. It should take an hour or two of dispatched work to produce, not a day. Match that ambition: short, sharp, sketch-grade.
13
+
14
+ **Announce at start:** "I'm using the concept-slides skill to produce a marp deck for [concept]."
15
+
16
+ ## When to Use
17
+
18
+ - Phase 2 of a prototype-driven flow (after discovery, before wireframing)
19
+ - For `/feature` on existing apps: a 1-3 slide mini-deck — what the feature is, where it sits, the visual sketch. Often a single slide.
20
+ - Ad-hoc when a user wants to put a concept on paper before building anything
21
+
22
+ **Do NOT skip when:**
23
+ - The concept "feels obvious" — obvious concepts still benefit from a sketch the user can react to. Skipping the deck is how scope creep starts.
24
+ - The user is in a hurry — the deck is the cheap artifact; resist the temptation to skip to wireframes
25
+
26
+ **Do NOT use this skill for:**
27
+ - Marketing decks, sales pitches, finalized presentations (use the underlying `marp-slides` skill directly with full-fidelity intent)
28
+ - Replacing the wireframe — the deck has visual sketches, NOT a UI specification. The wireframe comes next.
29
+ - Replacing requirements documentation — the deck is a starting artifact; the spec emerges through iteration
30
+
31
+ ## What concept-slides produces
32
+
33
+ A single marp deck at `decks/{name}/slides.md` that conforms to:
34
+
35
+ | Slide | Purpose | Length |
36
+ |---|---|---|
37
+ | 1. Hook | The question the product/feature answers | 1 slide; one sentence |
38
+ | 2. Concept overview | 3-5 sub-concepts, one per slide; each with one-sentence description + tiny visual | 3-5 slides |
39
+ | 3. Visual sketches | Per concept, low-fidelity sketches (HTML/SVG embedded in marp; NOT full UI mockups) | 2-4 slides |
40
+ | 4. User journey | The flow at low fidelity — boxes, arrows, named steps | 1-2 slides |
41
+ | 5. Out of scope | Explicit deferrals; what this concept is NOT | 1 slide |
42
+
43
+ Total target: 8-15 slides for a full product concept; 1-3 slides for a `/feature` concept on an existing app.
44
+
45
+ The deck is rendered with `marp` and previewed in HTML so the user can click through. Iteration takes 1-3 cycles for full products, often 1 for features.
46
+
47
+ ## What concept-slides does NOT produce
48
+
49
+ - A wireframe (Phase 3 deliverable; uses `build-wireframe` skill)
50
+ - A prototype (Phase 4 deliverable; uses `build-prototype` skill)
51
+ - An architecture doc (Phase 5 deliverable; uses `harden`)
52
+ - A finalized pitch deck (use `marp-slides` directly for finalized presentations)
53
+
54
+ If the user asks for "a real deck for the leadership pitch," use `marp-slides` with full presentation intent — concept-slides is intentionally low-fidelity.
55
+
56
+ ## Inputs
57
+
58
+ | Source | Required | Purpose |
59
+ |---|---|---|
60
+ | Rough concept brief from the user | yes | One-sentence to one-paragraph description of what's being built |
61
+ | Discovery output (if existing repo) | if Phase 1 ran | Codebase conventions, existing UX patterns to align with |
62
+ | Reference decks the user likes | optional | Style/composition reference (links or paths) |
63
+
64
+ ## Process
65
+
66
+ ### Step 0: orient
67
+
68
+ Read:
69
+ - `~/.claude/skills/marp-slides/SKILL.md` — the marp engine and its rendering rules
70
+ - 2-3 example decks in `~/.claude/skills/marp-slides/examples/` matching the concept's style (overview decks, journey decks, sketch-style decks)
71
+ - The user's brief
72
+
73
+ If no brief is provided, ask the user 1-3 clarifying questions and stop until answered. Do not invent the concept.
74
+
75
+ ### Step 1: dispatch the concept-designer subagent
76
+
77
+ The deck content (slide drafts, visual sketches, user-journey diagrams) is synthesized by the `concept-designer` subagent in its own context window. The main session aggregates the result and writes the file.
78
+
79
+ Dispatch with:
80
+ - The user's brief
81
+ - Discovery output if available
82
+ - Reference deck paths if any
83
+ - Target output path (`decks/{name}/slides.md`)
84
+
85
+ The subagent returns slide-by-slide content, with sketch markup inline (HTML/SVG that renders in marp).
86
+
87
+ ### Step 2: write the deck file
88
+
89
+ Write the deck to `decks/{name}/slides.md` using the marp frontmatter the subagent returned. Common frontmatter:
90
+
91
+ ```yaml
92
+ ---
93
+ marp: true
94
+ theme: default
95
+ paginate: true
96
+ size: 16:9
97
+ ---
98
+ ```
99
+
100
+ ### Step 3: render and present
101
+
102
+ Render with marp:
103
+
104
+ ```bash
105
+ npx @marp-team/marp-cli decks/{name}/slides.md --html --output decks/{name}/slides.html
106
+ ```
107
+
108
+ Open `decks/{name}/slides.html` in the user's browser. The user clicks through and gives feedback.
109
+
110
+ ### Step 4: iterate
111
+
112
+ User feedback drives revisions:
113
+ - Concept missing → add a slide
114
+ - Concept off → revise the description and sketch
115
+ - Visual sketch wrong → redraw
116
+ - Journey wrong → reorder steps or simplify
117
+
118
+ Re-dispatch the subagent with the feedback as part of the prompt; rewrite the deck; re-render. 1-3 iterations is typical.
119
+
120
+ ### Step 5: lock
121
+
122
+ When the user emits "concept locked" (or equivalent), the phase closes. The locked deck is the input to Phase 3 (`build-wireframe`).
123
+
124
+ Update the manifest at `.forge/work/{type}/{name}/manifest.yaml`:
125
+ - `artifacts.concept.deck_path: decks/{name}/slides.md`
126
+ - `artifacts.concept.locked_at: <ISO-8601 timestamp>`
127
+
128
+ Presence of `artifacts.concept.locked_at` is the lock signal that Phase 3 (`build-wireframe`) reads to confirm the concept is locked before proceeding.
129
+
130
+ ## Visual sketch guidance
131
+
132
+ Sketches inside marp slides use HTML/SVG inline. Keep them low-fidelity:
133
+
134
+ - Boxes, arrows, named regions — NOT pixel-perfect mockups
135
+ - Fonts: system; colors: 2-3 max; no shadows / gradients / animations
136
+ - One concept per sketch; if you need multiple sketches per slide, you have multiple concepts (split slides)
137
+
138
+ Avoid:
139
+ - Full UI mockups (that's the wireframe's job)
140
+ - Detailed icons (the wireframe will have lucide-react; the deck has emoji or simple shapes)
141
+ - Real data (use placeholders like "ITEM 1", "USER A")
142
+ - Click interactions (the deck is static; the wireframe handles interactivity)
143
+
144
+ ## Common mistakes
145
+
146
+ | Mistake | Fix |
147
+ |---|---|
148
+ | Treating the deck as a finalized pitch | It's a draft; lower the polish bar to "sketch-grade" |
149
+ | Embedding real UI mockups | Move that to Phase 3 wireframe; sketches in the deck are boxes-and-arrows |
150
+ | Producing a 30-slide deck | Cap at 15 for full products, 3 for features. Cut or split. |
151
+ | Skipping the "out of scope" slide | The deferral list IS the spec — what NOT to build is as important as what to build |
152
+ | Iterating with the user before the first render | Render first, get feedback against something concrete; don't iterate against imagined slides |
153
+ | Inventing concepts the user didn't ask for | Ask 1-3 clarifying questions instead; do not synthesize beyond the brief |
154
+
155
+ ## Red Flags
156
+
157
+ **Never:**
158
+ - Build a polished marketing deck under this skill (different intent — use `marp-slides` directly)
159
+ - Skip the "out of scope" slide — defining what's NOT built is half the value
160
+ - Iterate without rendering — text-only edits invite imaginary feedback
161
+ - Carry a draft to Phase 3 without an explicit user lock
162
+
163
+ **Always:**
164
+ - Render to HTML and let the user click through — that's the iteration surface
165
+ - Cap iterations at 3 for full products; surface "are we converging?" on iteration 4
166
+ - Update the manifest at lock — the deck path becomes input to wireframe + prototype phases
167
+
168
+ ## I/O Contract
169
+
170
+ | Field | Value |
171
+ |---|---|
172
+ | **Requires** | User brief, target output path, optional discovery output |
173
+ | **Produces** | `decks/{name}/slides.md` (marp source) + `decks/{name}/slides.html` (rendered preview) |
174
+ | **Updates manifest** | `artifacts.concept.{deck_path, locked_at}` |
175
+ | **Feeds into** | `build-wireframe` (Phase 3 — wireframe extends the visual sketches into clickable HTML) |
176
+
177
+ ## Integration
178
+
179
+ | Caller | When |
180
+ |---|---|
181
+ | Phase 2 of `/feature` and `/greenfield` | Default flow entry point for prototype-driven work |
182
+ | Manual invocation | When a concept needs sketching outside a workflow |
183
+
184
+ | Dispatches | For |
185
+ |---|---|
186
+ | `concept-designer` subagent | Slide content synthesis in own context window |
187
+
188
+ | Pairs with | For |
189
+ |---|---|
190
+ | `marp-slides` (user-level skill) | Underlying rendering engine — concept-slides specializes the format |
191
+ | `build-wireframe` | Phase 3 successor — wireframer reads the locked deck as input |
192
+ | `discover-codebase-analysis` | If existing repo, supplies UX/convention context |