create-anpunkit 2.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 (66) hide show
  1. package/README.md +44 -0
  2. package/bin/cli.js +41 -0
  3. package/package.json +34 -0
  4. package/template/.claude/agents/debugger.md +47 -0
  5. package/template/.claude/agents/e2e-runner.md +63 -0
  6. package/template/.claude/agents/implementer.md +78 -0
  7. package/template/.claude/agents/infra-provisioner.md +159 -0
  8. package/template/.claude/agents/planner.md +78 -0
  9. package/template/.claude/agents/researcher.md +103 -0
  10. package/template/.claude/agents/synthesizer.md +74 -0
  11. package/template/.claude/agents/test-author.md +71 -0
  12. package/template/.claude/anpunkit-manifest.json +256 -0
  13. package/template/.claude/commands/infra.md +62 -0
  14. package/template/.claude/commands/log-decision.md +34 -0
  15. package/template/.claude/commands/log-issue.md +28 -0
  16. package/template/.claude/commands/overview.md +106 -0
  17. package/template/.claude/commands/phase.md +202 -0
  18. package/template/.claude/commands/quick.md +30 -0
  19. package/template/.claude/commands/replan.md +64 -0
  20. package/template/.claude/commands/store-wisdom.md +195 -0
  21. package/template/.claude/commands/synthesize.md +26 -0
  22. package/template/.claude/commands/unstuck.md +40 -0
  23. package/template/.claude/hooks/cursor-session-start.sh +14 -0
  24. package/template/.claude/hooks/pre-compact.sh +25 -0
  25. package/template/.claude/hooks/session-start.sh +135 -0
  26. package/template/.claude/hooks/subagent-stop.sh +11 -0
  27. package/template/.claude/settings.json +16 -0
  28. package/template/.claude/skills/caveman/SKILL.md +39 -0
  29. package/template/.claude/skills/grill-me/SKILL.md +10 -0
  30. package/template/.claude/skills/karpathy-guidelines/SKILL.md +34 -0
  31. package/template/.cursor/commands/infra.md +57 -0
  32. package/template/.cursor/commands/log-decision.md +29 -0
  33. package/template/.cursor/commands/log-issue.md +23 -0
  34. package/template/.cursor/commands/overview.md +102 -0
  35. package/template/.cursor/commands/phase.md +197 -0
  36. package/template/.cursor/commands/quick.md +25 -0
  37. package/template/.cursor/commands/replan.md +59 -0
  38. package/template/.cursor/commands/store-wisdom.md +191 -0
  39. package/template/.cursor/commands/synthesize.md +22 -0
  40. package/template/.cursor/commands/unstuck.md +36 -0
  41. package/template/.cursor/hooks.json +14 -0
  42. package/template/.cursor/rules/anpunkit.md +11 -0
  43. package/template/.gitattributes +12 -0
  44. package/template/AGENTS.md +216 -0
  45. package/template/CLAUDE.md +70 -0
  46. package/template/README.md +283 -0
  47. package/template/commands.src/infra.md +62 -0
  48. package/template/commands.src/log-decision.md +34 -0
  49. package/template/commands.src/log-issue.md +28 -0
  50. package/template/commands.src/overview.md +106 -0
  51. package/template/commands.src/phase.md +202 -0
  52. package/template/commands.src/quick.md +30 -0
  53. package/template/commands.src/replan.md +64 -0
  54. package/template/commands.src/store-wisdom.md +195 -0
  55. package/template/commands.src/synthesize.md +26 -0
  56. package/template/commands.src/unstuck.md +40 -0
  57. package/template/docker-compose.test.yml +34 -0
  58. package/template/docs/DESIGN_LOG.md +578 -0
  59. package/template/e2e/global-setup.ts +57 -0
  60. package/template/playwright.config.ts +28 -0
  61. package/template/scripts/auth-setup.sh +51 -0
  62. package/template/scripts/e2e-stack.sh +65 -0
  63. package/template/scripts/regression.sh +46 -0
  64. package/template/setup.sh +236 -0
  65. package/template/tests/phase-1/README.md +4 -0
  66. package/template/tests/regression/README.md +11 -0
@@ -0,0 +1,578 @@
1
+ # anpunkit — Design Log
2
+
3
+ > **What this file is.** The design rationale for the anpunkit: not just *what*
4
+ > was built but *why*, including rejected alternatives — so a future change
5
+ > does not re-break a problem an earlier decision already solved. Read it first
6
+ > in any session where you intend to extend or change the kit.
7
+ >
8
+ > **This is a living document.** Updated ONLY when the kit's *architecture*
9
+ > changes. Run `/log-decision` to update — never silently rewrite history.
10
+ >
11
+ > **What it is NOT.** Not STATE.md (volatile handoff). Not HISTORY.md (project
12
+ > journal). This file is architectural and stable; those are operational.
13
+
14
+ ---
15
+
16
+ ## 0. Changelog
17
+
18
+ Newest first. One entry per architectural change. Appended by `/log-decision`.
19
+
20
+ - **(v2.0)** — Major release. Project name: **anpunkit**; distributed
21
+ as `npx create-anpunkit`. Five locked features: (1) **TDD-first** via a SCAFFOLD
22
+ step (implementer writes stubs only; test-author writes the suite blind against
23
+ stubs; RED gate must fail for the right reason; implementer fills to GREEN);
24
+ (2) **Portability split** — canonical `AGENTS.md` holds all methodology, `CLAUDE.md`
25
+ becomes a thin `@AGENTS.md` shim (anti-drift invariant: every rule in exactly one
26
+ place); (3) **Regression aggregation** — `tests/regression/` mock corpus as an
27
+ always-on guard with a hard ENDPOINTS coverage gate at CLOSE; (4) **Upgrade-safe
28
+ installer** — manifest-driven, non-destructive `setup.sh` + `create-anpunkit` npx
29
+ package; (5) **Multi-tool adapters** — one canonical `commands.src/` body generates
30
+ Claude Code + Cursor commands, with Cursor at near-parity via `.cursor/hooks.json`
31
+ lifecycle hooks (Codex dropped). New decision entries §5.28–§5.32. New files:
32
+ `AGENTS.md`, `commands.src/*`, `.cursor/*`, `.claude/anpunkit-manifest.json`,
33
+ `scripts/regression.sh`, `tests/regression/`, `create-anpunkit/`.
34
+ NOTE: the V2_0_REQUIREMENTS doc referenced §5.26–§5.30, but the real v1.5 log
35
+ already used §5.26–§5.27 (the shared-KB entries), so the v2.0 entries continue
36
+ cleanly at §5.28–§5.32.
37
+
38
+ - **(v1.5)** — Shared KB mechanism added:
39
+ (1) **`/store-wisdom` command**: analyzes resolved issues + completed research
40
+ in the current project, proposes promotion candidates for human review, then
41
+ commits and pushes approved entries to a shared `anpunkit-kb` GitHub repo.
42
+ (2) **Researcher KB check (step 0)**: both DESIGN and IMPL modes check
43
+ `docs/.kb-snapshot.md` before any web search. Fresh hits short-circuit web
44
+ research; stale hits are treated as weak signals and re-researched.
45
+ (3) **Session-start KB pull**: if `.claude/kb-config.json` exists, the
46
+ SessionStart hook pulls the KB and writes a snapshot to `docs/.kb-snapshot.md`
47
+ with staleness markers. Fails silently if offline.
48
+ (4) **`setup.sh` KB prompt**: asks for the local KB repo path + verifies
49
+ connectivity via `git ls-remote` before writing `.claude/kb-config.json`.
50
+ New file: `docs/.kb-snapshot.md` (gitignored, session-local).
51
+
52
+ - **(v1.4)** — Four new capabilities added:
53
+ (1) **Design-researcher + double grill in /overview**: `researcher` gains a
54
+ DESIGN mode for pre-planning domain/constraint research. `/overview` flow is
55
+ now: grill (round 1) → design-researcher → re-grill (round 2, always, research-
56
+ informed) → OVERVIEW.md (written after round 2) → planner. Round 1 answers are
57
+ working context only — OVERVIEW.md is not written until after the re-grill.
58
+ (2) **Deployment in last phase**: `planner` is required to include a deploy task
59
+ block in the final code phase. Enforced at planning time.
60
+ (3) **Usage endpoint summary on final phase close**: `/phase` CLOSE detects
61
+ IS_FINAL_PHASE; after synthesizer runs, surfaces a "READY TO USE" block with
62
+ the deployed base URL and full ENDPOINTS.md table.
63
+ (4) **Final-phase doc update pass**: `synthesizer` receives a FINAL PHASE
64
+ signal and runs an extended pass updating OVERVIEW.md and README.md.
65
+ New file: `docs/ENDPOINTS.md` — API endpoint catalogue maintained by
66
+ implementer per phase. `researcher` gains two modes (DESIGN / IMPL);
67
+ design research writes to `design-<slug>.md`; INDEX.md updated to note the
68
+ prefix convention.
69
+
70
+ ---
71
+
72
+ ## 1. Problem statement
73
+
74
+ Six recurring failure modes in a 10-point manual development practice:
75
+
76
+ 1. Issue log missed — agents repeated known dead ends.
77
+ 2. Manual navigation every session — wasted time re-orienting.
78
+ 3. Agents over-struggling on bugs — no circuit breaker.
79
+ 4. Context rot — debug noise filled the orchestrator's window.
80
+ 5. No subagent orchestration — everything happened inline.
81
+ 6. Uncleaned handoff files — STATE.md grew unbounded.
82
+
83
+ ---
84
+
85
+ ## 2. Core architectural insight
86
+
87
+ **Mandatory steps live in hooks. Discretionary steps live in agents/skills.**
88
+
89
+ Hooks are deterministic — they cannot be skipped, forgotten, or reasoned around.
90
+ Agents are model-driven — they reason, but they can be wrong or lazy.
91
+
92
+ The six failure modes above all came from putting "must happen" steps into
93
+ prompts (discretionary) rather than hooks (deterministic).
94
+
95
+ ---
96
+
97
+ ## 3. The orchestrator-subagent boundary
98
+
99
+ The main Claude Code session is the ORCHESTRATOR. It:
100
+ - reads STATE.md, PLAN.md, and ISSUES.md
101
+ - dispatches subagents for scoped work
102
+ - gates on results (circuit breaker, phase gate)
103
+ - communicates with the user
104
+
105
+ Subagents cannot address the user directly. All escalation is orchestrator-mediated.
106
+ This keeps the human interface clean and predictable.
107
+
108
+ ---
109
+
110
+ ## 4. Test layering
111
+
112
+ Three layers, each with a different trust level:
113
+
114
+ 1. Mock suite (fast, no external dependency) — written blind by test-author.
115
+ 2. Real API suite (real HTTP + real Azure) — written blind by test-author.
116
+ 3. Browser E2E (Playwright, ROPC auth) — written blind by e2e-runner.
117
+
118
+ Phase gate = layer 2 passes + (if frontend) layer 3 passes.
119
+ Layer 1 alone never closes a phase. "Mock-green" is not done.
120
+
121
+ ---
122
+
123
+ ## 5. Decision log
124
+
125
+ ### 5.1 Agent Teams deferred
126
+ Cross-session agent parallelism (Claude Code Agent Teams) deferred. Single
127
+ orchestrator + sequential subagents is simpler to reason about and debug.
128
+ Revisit when the sequential model proves to be the bottleneck.
129
+
130
+ ### 5.2 Three hooks, not more
131
+ SessionStart, PreCompact, SubagentStop. These three cover the mandatory
132
+ injection (start), context protection (compact), and lightweight trace (stop).
133
+ A SessionEnd hook was discussed but rejected — it would be a mechanical snapshot
134
+ only and adds complexity without fixing a real failure mode.
135
+
136
+ ### 5.3 synthesizer is Haiku, not Opus
137
+ Compression and dedup is mechanical, not reasoning-heavy. Haiku is faster and
138
+ cheaper. Opus is reserved for agents that need to reason about design
139
+ (planner, infra-provisioner, implementer, debugger).
140
+
141
+ ### 5.4 /clear does not lose hooks
142
+ `/clear` resets the context window but not the hook configuration. SessionStart
143
+ fires again on the next turn, re-injecting STATE.md + ISSUES.md. Hook wiring
144
+ lives in `.claude/settings.json`, not in the context.
145
+
146
+ ### 5.5 researcher writes to file, returns terse summary
147
+ Full research output is written to `docs/research/<slug>.md`. The orchestrator
148
+ receives only a terse summary + path. This keeps the orchestrator's context
149
+ clean — it reads the file only if it needs detail.
150
+
151
+ ### 5.6 Circuit breaker: warn at 2, hard-stop at 3
152
+ Two attempts are enough to rule out the obvious hypotheses. Three attempts
153
+ without a fix signals the frame is wrong — more in-place attempts waste budget.
154
+ Hard-stop at 3 forces either /unstuck (re-research) or human intervention.
155
+
156
+ ### 5.7 Escalation is always two hops
157
+ Subagent → orchestrator → user. A subagent that directly asks the user would
158
+ fragment the conversation and make the orchestrator's state inconsistent.
159
+
160
+ ### 5.8 Skill vendoring is fallback-only
161
+ caveman and grill-me are vendored in `.claude/skills/` as fallback copies only.
162
+ The global `~/.claude/skills/` copy is authoritative. The vendored copy is used
163
+ only if the global is missing. This prevents the project copy from diverging
164
+ from the user's customized global version.
165
+
166
+ ### 5.9 DESIGN_LOG.md is append-only
167
+ History is never rewritten. Decisions that turn out to be wrong get a new entry
168
+ explaining the correction — not an edit to the original. This preserves the full
169
+ reasoning trail and prevents silent revisionism.
170
+
171
+ ### 5.10 MSAL auth: ROPC + dedicated test account, never UI automation
172
+ Browser E2E must not drive the Microsoft login UI — it is brittle, MFA-blocked,
173
+ and unsupported. ROPC flow with a dedicated test account (MFA excluded via
174
+ Conditional Access policy) is the correct approach. The token is injected
175
+ directly into the MSAL localStorage cache, bypassing the UI entirely.
176
+
177
+ ### 5.11 Failure classification: LOGIC FAIL vs SERVICE UNAVAILABLE
178
+ Outages, rate limits, auth expiry, and stack-not-ready states are environment
179
+ failures, not code bugs. They must not consume the debug budget (3-attempt
180
+ circuit breaker). The test-author and e2e-runner classify every failure before
181
+ routing — only LOGIC FAIL reaches the debugger.
182
+
183
+ ### 5.12 Phase gate requires real API + real E2E, not mock-green
184
+ Mocks verify that the code compiles and the wiring is correct. They cannot
185
+ verify that the real Azure service behaves as expected. The phase gate requires
186
+ both the real API suite and (for frontend phases) the real browser E2E to pass.
187
+
188
+ ### 5.13 Hook path resolution on Windows (Git Bash)
189
+ Claude Code on Windows runs hook commands under Git Bash. The hook command must
190
+ use a bare `bash` prefix (`bash .claude/hooks/session-start.sh`), not an
191
+ explicit path like `C:\Program Files\Git\bin\bash.exe`. The explicit path
192
+ triggers a Claude Code argument-splitter bug that silently misfires the hook.
193
+
194
+ ### 5.14 LF normalization in setup.sh
195
+ Windows Git clones may CRLF-convert shell scripts even with `.gitattributes`.
196
+ `setup.sh` runs `sed -i 's/\r$//'` on all `.sh` files as a self-healing step.
197
+ The `.gitattributes` file pins `*.sh` to LF for future clones.
198
+
199
+ ### 5.15 Hook silent injection (Claude Code 2.1.x)
200
+ SessionStart output is injected into the model context silently — no visible
201
+ UI banner. This is expected behavior in Claude Code 2.1.x. The reliable test:
202
+ ask Claude "what does my STATE.md say?" in a fresh session. A correct answer
203
+ without a file read = hooks working.
204
+
205
+ ### 5.16 CLAUDE_PROJECT_DIR fallback
206
+ Hooks use `cd "${CLAUDE_PROJECT_DIR:-.}"` — falls back to `.` (current
207
+ directory) if the env var is unset. This handles both the normal Claude Code
208
+ invocation (where the var is set) and direct bash invocation for testing.
209
+
210
+ ### 5.17 Bicep over raw Azure CLI for IaC
211
+ Bicep provides declarative, repeatable, reviewable infrastructure definitions.
212
+ Raw `az` commands are imperative and not idempotent. The what-if gate requires
213
+ a declarative format to produce a meaningful diff.
214
+
215
+ ### 5.18 E2E target is design-determined
216
+ local-docker or azure-deployed, set in INFRA.md by infra-provisioner. Not
217
+ hardcoded. `e2e-stack.sh up` is a reachability check when EXTERNAL=1.
218
+
219
+ ### 5.19 Test data lifecycle — seeding is deployment, not E2E
220
+ `e2e-stack.sh` seed/cleanup removed. Test data = deployment-phase concern.
221
+
222
+ ### 5.20 Clone-direct distribution (v1.3)
223
+ `install.sh` removed; replaced by `setup.sh` (in-place, post-clone). Kit is
224
+ the repo itself — `git clone` then `bash setup.sh`.
225
+
226
+ ### 5.21 Design-researcher + double grill (v1.4)
227
+ **Problem:** grill-me in /overview ran with zero domain knowledge, producing
228
+ shallow questions. The planner then designed phases against unverified
229
+ assumptions about service capabilities, quotas, and architectural constraints.
230
+
231
+ **Decision:** split the pre-planning flow into two research passes:
232
+ - Grill round 1: captures initial scope from the user's own knowledge.
233
+ - Design research (DESIGN mode on researcher): verifies external service
234
+ capabilities, quotas, architectural constraints, cost surprises. Writes to
235
+ `design-<slug>.md`.
236
+ - Grill round 2: always runs (not conditional on "unknowns found"); seeded with
237
+ round-1 answers + full research findings. Builds complete, research-informed
238
+ understanding before OVERVIEW.md is written.
239
+
240
+ **Why re-grill always (not conditional):** Research always produces new context,
241
+ even when it finds no blockers. Knowing a service *does* support a pattern is
242
+ just as valuable for shaping the interview as knowing it doesn't.
243
+
244
+ **OVERVIEW.md written after round 2:** round-1 answers are working context only.
245
+ The planner receives the fully-informed picture.
246
+
247
+ ### 5.22 Deployment mandatory in last phase (v1.4)
248
+ **Problem:** deployment was not a guaranteed part of the plan. Projects could
249
+ complete all implementation phases without ever deploying, leaving no
250
+ confirmed live URL.
251
+
252
+ **Decision:** planner is instructed (hard rule) that the final code phase must
253
+ always include a deploy task block: deploy to Azure, smoke-test the deployed
254
+ URL, write the URL back to INFRA.md, update ENDPOINTS.md. Enforced at
255
+ planning time in planner.md. /replan is required to preserve this invariant
256
+ when it re-plans pending phases.
257
+
258
+ ### 5.23 Usage endpoint summary on final phase close (v1.4)
259
+ **Problem:** after the last phase completes, the user has no immediate
260
+ actionable summary — they must hunt across INFRA.md and ENDPOINTS.md.
261
+
262
+ **Decision:** /phase CLOSE detects IS_FINAL_PHASE (no further pending phases
263
+ in PLAN.md). After synthesizer runs, the orchestrator reads docs/ENDPOINTS.md
264
+ and docs/INFRA.md and surfaces a "READY TO USE" block: deployed base URL,
265
+ full endpoint table, quick-start curl example, auth notes.
266
+
267
+ **Implementation:** orchestrator reads ENDPOINTS.md directly (no subagent
268
+ dispatch needed — it's a small file the implementer maintains).
269
+
270
+ ### 5.24 Final-phase doc update pass via synthesizer (v1.4)
271
+ **Problem:** after project completion, OVERVIEW.md and README.md still
272
+ reflect the planning-time state — not the delivered state.
273
+
274
+ **Decision:** synthesizer receives a FINAL PHASE signal from /phase CLOSE
275
+ (and /synthesize when it detects no remaining pending phases). Extended pass
276
+ appends a "Final state" section to OVERVIEW.md and updates the project README.md
277
+ status to "Production — deployed at <URL>". synthesizer does NOT touch
278
+ DESIGN_LOG.md or INFRA.md or ENDPOINTS.md — those are owned by other agents.
279
+
280
+ ### 5.25 Shared KB — separate repo, not inside anpunkit (v1.5)
281
+ **Problem:** research findings and resolved issues die with their project repo.
282
+ Every new project re-discovers the same Azure/Databricks gotchas.
283
+
284
+ **Decision:** a separate `anpunkit-kb` GitHub repo accumulates cross-project
285
+ findings. Separate repo (not a folder inside anpunkit) because:
286
+ - it must survive anpunkit upgrades without being overwritten
287
+ - it is shared across machines independently
288
+ - a project that opts out should not carry KB content in its repo
289
+
290
+ **Why GitHub over other options:** git handles auth transparently (SSH key or
291
+ HTTPS credential manager). The remote is configured once in `kb-config.json`
292
+ and verified at setup time via `git ls-remote`. No additional infrastructure.
293
+
294
+ ### 5.26 KB is loaded once per session (static snapshot) (v1.5)
295
+ **Problem:** if the researcher pulled from KB on every call, mid-session updates
296
+ from other machines could change what the researcher "knows" unpredictably.
297
+
298
+ **Decision:** pull once at session start (SessionStart hook), write to
299
+ `docs/.kb-snapshot.md`. The snapshot is static for the session. The researcher
300
+ reads only the snapshot, never the live KB. This is predictable and fast.
301
+ Stale entries (research > 6 months) are flagged in the snapshot at load time —
302
+ the researcher treats them as weak signals and re-researches locally.
303
+
304
+ ### 5.27 /store-wisdom is human-gated, never automatic (v1.5)
305
+ **Problem:** automatic KB writes could promote incorrect findings, open issues,
306
+ or project-specific noise to the shared store.
307
+
308
+ **Decision:** `/store-wisdom` is the only write path to the KB. It is a slash
309
+ command, not a hook — explicitly triggered by the user. It presents each
310
+ candidate for approval before writing. Only resolved issues (not open ones) and
311
+ generalizable research (not project-specific config or debug traces) qualify.
312
+ This keeps the KB signal-dense rather than becoming a dump.
313
+
314
+ **Staleness rewrite via /store-wisdom only:** when the researcher re-researches
315
+ a stale KB entry locally, the new findings stay in `docs/research/`. They are
316
+ only written back to the KB when the user explicitly runs `/store-wisdom`. The
317
+ hook never modifies the KB repo.
318
+
319
+ ---
320
+
321
+ ### 5.28 Fail-first TDD via a SCAFFOLD step (v2.0)
322
+ **Problem:** tests-after let the implementation shape the test; honor-system
323
+ "write tests first" drifts. (§5.5 was the original misread of this.)
324
+
325
+ **Decision:** TDD phases run `SCAFFOLD -> RED -> GREEN`. The implementer writes
326
+ interface stubs only (no logic); the test-author writes the suite blind against
327
+ those stubs; the RED gate requires every acceptance test to COLLECT cleanly and
328
+ FAIL (assertion / NotImplemented). Green-on-stubs is caught and rejected. Then the
329
+ implementer fills to GREEN. Blindness is now STRUCTURAL — there is no logic to
330
+ peek at — not honor-system. `TDD_PHASE` is a BOOLEAN gate ("does the phase add a
331
+ public callable surface assertable from the spec?"); size thresholds were rejected
332
+ as a bad proxy that reintroduces the §5.5 misread. On ambiguity, default true but
333
+ state the classification so a human can override before SCAFFOLD fires. Rule 11
334
+ (scoped no-rationalization) forbids dodging the gate by downgrading a phase or
335
+ routing it through `/quick`.
336
+
337
+ **Rejected:** classic TDD, tests-after, a separate stub-writer agent. **Cost:**
338
+ +1 dispatch per TDD phase. Partially resolves §5.5.
339
+
340
+ ### 5.29 AGENTS.md / CLAUDE.md portability split (v2.0)
341
+ **Problem:** all methodology lived in a Claude-only `CLAUDE.md`; other tools could
342
+ not consume it, and any second config file risked drifting out of sync.
343
+
344
+ **Decision:** canonical `AGENTS.md` (open standard) holds every rule, role,
345
+ procedure, and ritual. `CLAUDE.md` becomes a thin shim: a bare top-level
346
+ `@AGENTS.md` import plus Claude-native wiring (subagent roster, hook-to-ritual
347
+ mapping, platform notes) and restates NO rule. Anti-drift invariant: every rule
348
+ lives in exactly one place; duplication is a defect. `@import` is confirmed
349
+ reliable (official Claude Code docs: `@path`, recursive to depth 5, not evaluated
350
+ in code spans). `setup.sh` VERIFY hardens it: bare unfenced import + AGENTS.md on
351
+ disk + a SENTINEL string, else loud-fail `exit 1`. Token saving is explicitly NOT
352
+ the motivation (imports expand inline).
353
+
354
+ **Rejected:** fused + hand-written second file, build-time generation of the rule
355
+ source, per-tool full configs.
356
+
357
+ ### 5.30 Regression aggregation — mock corpus guard + ENDPOINTS gate (v2.0)
358
+ **Problem:** a contract built in an early phase could be silently broken by a later
359
+ phase; nothing re-checked it.
360
+
361
+ **Decision:** `tests/regression/` holds cross-phase contract tests (corpus =
362
+ `ls tests/regression/`, visible and auditable). `tests/phase-<n>/` holds
363
+ phase-local tests. The accumulated MOCK corpus is the always-on guard, run at
364
+ every phase CLOSE, after `/quick`, and after `/replan`; the full REAL corpus runs
365
+ only at the final phase and after `/replan`. mock-vs-real is a fixture/env flag
366
+ (`TEST_MODE`) on the same test, not duplicated files. CLOSE asserts ENDPOINTS
367
+ coverage: every `docs/ENDPOINTS.md` entry must have >=1 regression test; zero ->
368
+ fail hard. Rule 5 extended accordingly. No new subagent.
369
+
370
+ **Rejected:** re-run all REAL every phase (doesn't scale); a `@regression` marker
371
+ (a forgotten marker = a silent gap); no cross-phase guard (the status-quo
372
+ failure). Residual soft edge (accepted): non-endpoint contracts still rely on
373
+ author judgment.
374
+
375
+ ### 5.31 Upgrade-safe installer + npx distribution (v2.0)
376
+ **Problem:** clone-direct could clobber a user's existing files on upgrade; there
377
+ was no end-user-friendly install path.
378
+
379
+ **Decision:** `npx create-anpunkit` (public) and clone+`setup.sh` coexist (end
380
+ users vs kit developers). A declarative `.claude/anpunkit-manifest.json`
381
+ (version + per-file sha256) drives a non-destructive upgrade: kit-owned files are
382
+ refreshed; user-modified kit files are KEPT and the new version written as
383
+ `<file>.anpunkit-new` (+ `--force` to overwrite); user-owned files are never
384
+ touched; hybrid files (`settings.json`, `.cursor/hooks.json`, `.gitignore`,
385
+ `DESIGN_LOG.md`) are merged idempotently. A timestamped `.anpunkit-backup-*/` is
386
+ written before any upgrade write, and `--dry-run` prints the plan without writing
387
+ — the install-time analogue of the `az deployment what-if` review gate. The npx
388
+ bin reimplements NO logic; it delegates to `bash setup.sh --src <template>`.
389
+ NOTE: `.claude/commands/` is now GENERATED output, so a v1.5->v2.0 upgrade moves
390
+ the command source-of-truth from the file to the generator (handled by checksum
391
+ logic; called out for the upgrade smoke test).
392
+
393
+ **Rejected:** clobbering reinstall; reimplementing setup.sh logic in Node (drift);
394
+ imperative per-version migrations as the default.
395
+
396
+ ### 5.32 Multi-tool adapters — Claude Code + Cursor (v2.0)
397
+ **Problem:** the kit was Claude-only; maintaining parallel per-tool command copies
398
+ by hand would drift.
399
+
400
+ **Decision:** one canonical body per procedure in `commands.src/<name>.md`,
401
+ generated at install into `.claude/commands/` (Claude) and `.cursor/commands/`
402
+ (Cursor; frontmatter stripped, `$ARGUMENTS` works). Cursor reaches near-parity:
403
+ `.cursor/rules/anpunkit.md` points at `AGENTS.md` for methodology, and
404
+ `.cursor/hooks.json` wires `sessionStart`/`preCompact`/`subagentStop` to the SAME
405
+ shared hook scripts used by Claude (one copy of each script; only the wiring
406
+ differs). Codex DROPPED entirely (its repo-committed command path is deprecated
407
+ home-dir custom-prompts; divergent UX). Honest degradation for Cursor is captured
408
+ in the README "verify at build" notes (context injection, named subagents, hook
409
+ path resolution).
410
+
411
+ **Rejected:** symmetric Claude/Cursor/Codex generation; hand-maintained per-tool
412
+ copies; generating into `~/.codex/prompts`; claiming full support for every tool.
413
+
414
+ **Verified at build (cursor.com/docs/hooks, /docs/subagents,
415
+ /docs/reference/third-party-hooks — 2026-06):** all three open items resolved.
416
+ (1) `sessionStart` DOES inject context via an `additional_context` JSON output
417
+ field; Cursor expects JSON where Claude injects raw stdout, so the wiring runs
418
+ the shared body through a thin `cursor-session-start.sh` JSON-envelope wrapper
419
+ (body remains one copy). (2) Cursor DOES support named subagents and reads
420
+ `.claude/agents/*.md` natively via its documented Claude-compatibility path — no
421
+ `.cursor/agents/` duplication; `model: haiku/opus` tiers fall back to
422
+ inherit/compatible. (3) Project `.cursor/hooks.json` commands resolve from the
423
+ PROJECT ROOT (not `.cursor/`); wiring uses `bash .claude/hooks/<script>.sh`, and
424
+ Cursor sets `CLAUDE_PROJECT_DIR` as a compat alias so shared scripts run
425
+ unchanged. Cursor's `preCompact` is observational only — acceptable (the
426
+ snapshot is a side effect). Decision: explicit native `.cursor/hooks.json`
427
+ wiring. Rejected: relying on Cursor's third-party loading of
428
+ `.claude/settings.json` hooks (requires a manual "Third-party skills" settings
429
+ toggle — silent no-op if forgotten — and would double-fire if combined with
430
+ native wiring).
431
+
432
+ ---
433
+
434
+ ## 6. Current file inventory
435
+ ```
436
+
437
+ anpunkit/
438
+ CLAUDE.md always-loaded project memory
439
+ README.md user-facing guide
440
+ .gitattributes pins .sh to LF
441
+ .gitignore runtime scratch files excluded (committed)
442
+ .env.test.example E2E secrets template
443
+ setup.sh one-time post-clone setup + optional KB config
444
+ playwright.config.ts E2E config
445
+ docker-compose.test.yml app containers -> real Azure (TEMPLATE)
446
+ .claude/
447
+ settings.json wires the 3 hooks
448
+ kb-config.json shared KB path + remote (optional, written by setup.sh)
449
+ hooks/
450
+ session-start.sh injects STATE, ISSUES, INDEX, rules, Azure nudge, KB snapshot
451
+ pre-compact.sh snapshots before compaction
452
+ subagent-stop.sh lightweight trace per subagent
453
+ agents/
454
+ researcher DESIGN mode + IMPL mode; KB snapshot check step 0 — Haiku
455
+ planner OVERVIEW -> PLAN.md, Phase 0 first, last phase deploys — Opus
456
+ infra-provisioner Bicep, what-if, apply, INFRA.md + .env.test — Opus
457
+ implementer one phase, no tests, maintains ENDPOINTS.md — Opus
458
+ test-author blind tests, mock + real API suites — Opus
459
+ debugger isolated debug context, writes to file — Opus
460
+ e2e-runner Playwright E2E, reads INFRA.md for target — Opus
461
+ synthesizer per-phase cleanup + final-phase extended pass — Haiku
462
+ commands/
463
+ overview design-research + double grill + plan
464
+ infra provision/verify Azure infra
465
+ phase run one phase; final-phase: endpoint summary
466
+ quick small direct change
467
+ unstuck deep re-research after circuit breaker
468
+ synthesize compress handoff docs; final-phase: extended pass
469
+ replan revise PLAN.md; auto design-research on new services
470
+ log-issue append to ISSUES.md
471
+ log-decision append to DESIGN_LOG.md
472
+ store-wisdom promote findings to shared KB (human-gated)
473
+ skills/
474
+ karpathy-guidelines kit-owned coding + debug discipline
475
+ caveman fallback only
476
+ grill-me fallback only
477
+ e2e/
478
+ global-setup.ts ROPC token fetch + MSAL injection
479
+ scripts/
480
+ auth-setup.sh session Azure credential check
481
+ e2e-stack.sh up|down lifecycle
482
+ infra/
483
+ main.bicep top-level Bicep (generated)
484
+ params.json parameters, no secrets (generated)
485
+ modules/ per-service Bicep modules (generated)
486
+ docs/
487
+ STATE.md operational handoff (rewritten each phase)
488
+ ISSUES.md error log (deduped by synthesizer)
489
+ PLAN.md phase plan (Phase 0 first; last phase deploys)
490
+ HISTORY.md one line per finished phase
491
+ DESIGN_LOG.md architectural rationale
492
+ OVERVIEW.md project scope (written after double grill)
493
+ INFRA.md Azure resource manifest + cost estimates
494
+ ENDPOINTS.md API endpoint catalogue (maintained by implementer)
495
+ research/INDEX.md searchable map (design-*, impl, debug-*)
496
+ .snapshots/ pre-compact recovery markers (gitignored)
497
+ .kb-snapshot.md KB INDEX loaded this session (gitignored, session-local)
498
+
499
+ ```
500
+ ---
501
+
502
+
503
+ ### v2.0 additions to the inventory
504
+
505
+ ```
506
+ AGENTS.md portable single source of truth (methodology + rules + SENTINEL)
507
+ CLAUDE.md thin @AGENTS.md shim (Claude-native wiring only)
508
+ commands.src/<name>.md canonical command bodies (one per procedure) — source of truth
509
+ .claude/commands/<name>.md GENERATED from commands.src (Claude adapter)
510
+ .cursor/commands/<name>.md GENERATED from commands.src (Cursor adapter, frontmatter stripped)
511
+ .cursor/rules/anpunkit.md GENERATED pointer at AGENTS.md (Cursor methodology)
512
+ .cursor/hooks.json Cursor lifecycle wiring -> shared .claude/hooks/*.sh (hybrid-merge)
513
+ .claude/anpunkit-manifest.json {version, files:[{path, sha256}]} — drives upgrade taxonomy
514
+ scripts/regression.sh runs tests/regression/ (default mock, --real)
515
+ tests/regression/ cross-phase contract corpus (the regression guard)
516
+ tests/phase-<n>/ phase-local suites
517
+ create-anpunkit/ npx package (package.json, bin/cli.js, build.sh) -> `npx create-anpunkit`
518
+ ```
519
+
520
+ `setup.sh` is rewritten as the non-destructive installer engine (manifest-driven
521
+ ownership taxonomy, adapter generation, idempotent JSON hook merge, VERIFY,
522
+ backup, `--dry-run`).
523
+
524
+ ## 7. How each original problem maps to its fix
525
+
526
+ | Problem | Fix |
527
+ |---|---|
528
+ | 1 miss issue log | SessionStart hook injects open ISSUES.md every session |
529
+ | 2 manual navigation | SessionStart hook injects STATE.md + git + research INDEX |
530
+ | 3 stuck too long | warn at 2; first hard-stop at 3 stops and asks; `/unstuck` for deep re-research |
531
+ | 4 context rot in debug | `debugger` isolated context + writes noise to file |
532
+ | 5 want orchestration | 8 scoped subagents; `/phase` orchestrates them |
533
+ | 6 handoff bloat | `synthesizer` + PreCompact hook; HISTORY.md for long log |
534
+ | 7 Azure auth friction + infra ad-hoc | `auth-setup.sh` + `infra-provisioner` Phase 0 |
535
+ | 8 re-discovering same gotchas across projects | KB snapshot at session start; researcher checks before web; `/store-wisdom` promotes findings |
536
+
537
+ ---
538
+
539
+ ## 8. Known gaps & template placeholders
540
+
541
+ 1. `docker-compose.test.yml` — build contexts and ports are TEMPLATE values.
542
+ 2. `e2e/global-setup.ts` — MSAL cache-key shape is a stub; adjust for app's
543
+ `@azure/msal-browser` version/config.
544
+ 3. caveman vendored copy is the base version, not the user's ULTRA variant.
545
+ 4. Visual-regression testing — explicit non-goal.
546
+ 5. Agent Teams / cross-session parallelism — deferred.
547
+ 6. SessionEnd guard hook — discussed, not built.
548
+ 7. Attempt counter is model self-tracked.
549
+ 8. `infra/modules/*.bicep` — generated per-project by infra-provisioner.
550
+ 9. `scripts/setup-entra.sh` — generated by infra-provisioner when needed.
551
+ 10. Test data seeding scripts — deployment-phase concern.
552
+ 11. `infra/params.json` — no secrets; Key Vault references for secrets.
553
+ 12. docs/ENDPOINTS.md — starter template only; populated by implementer.
554
+ 13. KB domain structure — fully dynamic (inferred by /store-wisdom from content).
555
+ No pre-seeded domain directories ship with the kit.
556
+ 14. Multi-machine KB sync — git push/pull via SSH or HTTPS credential manager.
557
+ No automatic sync outside of session-start pull and /store-wisdom push.
558
+
559
+ ---
560
+
561
+ ## 9. Principles to preserve in any future upgrade
562
+
563
+ 1. Mandatory steps live in hooks; discretionary in agents/skills.
564
+ 2. Subagents cannot talk to the user. Escalation is always orchestrator-mediated.
565
+ 3. The orchestrator routes; it does not implement or debug.
566
+ 4. The phase gate is a REAL test. A mock suite never closes a phase.
567
+ 5. Noisy work writes full output to `docs/research/` and returns only
568
+ terse summary + path — keeps the orchestrator context clean.
569
+ 6. Tests are written blind — this is the unbiased-test guarantee.
570
+ 7. Never script the Microsoft login UI.
571
+ 8. Fail loud, never silent — especially hook wiring.
572
+ 9. Honest failure classification — environment issues never burn the debug budget.
573
+ 10. Azure infra is Phase 0: provisioned once, reviewed before apply, recorded in INFRA.md.
574
+ 11. Plan before code: design-research + double grill before OVERVIEW.md is written.
575
+ The planner must never design phases against unverified assumptions.
576
+ 12. Deployment is always in the last phase — never omitted, never a separate phase.
577
+ 13. The shared KB is human-gated at both ends: you approve before push (/store-wisdom),
578
+ and findings are static within a session (KB snapshot). The KB never writes itself.
@@ -0,0 +1,57 @@
1
+ // e2e/global-setup.ts
2
+ import { chromium } from '@playwright/test';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+
6
+ const AUTH_DIR = path.join(__dirname, '.auth');
7
+ const STATE = path.join(AUTH_DIR, 'state.json');
8
+
9
+ async function fetchRopcToken() {
10
+ const tenant = reqEnv('E2E_TENANT_ID');
11
+ const body = new URLSearchParams({
12
+ grant_type: 'password',
13
+ client_id: reqEnv('E2E_CLIENT_ID'),
14
+ username: reqEnv('E2E_TEST_USER'),
15
+ password: reqEnv('E2E_TEST_PASSWORD'),
16
+ scope: `openid profile ${reqEnv('E2E_SCOPE')}`,
17
+ });
18
+ const url = `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`;
19
+ const res = await fetch(url, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
22
+ body,
23
+ });
24
+ if (!res.ok) {
25
+ const txt = await res.text();
26
+ throw new Error(
27
+ `ROPC token fetch failed (${res.status}). ` +
28
+ `Check: ROPC enabled on app registration, test user exists, ` +
29
+ `MFA excluded via Conditional Access. Response: ${txt}`
30
+ );
31
+ }
32
+ return res.json() as Promise<{ access_token: string; id_token: string; expires_in: number }>;
33
+ }
34
+
35
+ function reqEnv(k: string): string {
36
+ const v = process.env[k];
37
+ if (!v) throw new Error(`Missing env ${k} — run /infra to regenerate .env.test`);
38
+ return v;
39
+ }
40
+
41
+ async function globalSetup() {
42
+ fs.mkdirSync(AUTH_DIR, { recursive: true });
43
+ const token = await fetchRopcToken();
44
+ const baseURL = reqEnv('E2E_BASE_URL');
45
+ const browser = await chromium.launch();
46
+ const page = await browser.newPage();
47
+ await page.goto(baseURL);
48
+ await page.evaluate((t) => {
49
+ localStorage.setItem('e2e.access_token', t.access_token);
50
+ localStorage.setItem('e2e.id_token', t.id_token);
51
+ }, token);
52
+ await page.context().storageState({ path: STATE });
53
+ await browser.close();
54
+ console.log('E2E auth ready — real Entra token injected, MFA UI bypassed.');
55
+ }
56
+
57
+ export default globalSetup;
@@ -0,0 +1,28 @@
1
+ // playwright.config.ts
2
+ import { defineConfig, devices } from '@playwright/test';
3
+
4
+ export default defineConfig({
5
+ testDir: './e2e',
6
+ globalSetup: './e2e/global-setup.ts',
7
+ fullyParallel: false,
8
+ forbidOnly: !!process.env.CI,
9
+ retries: process.env.CI ? 1 : 0,
10
+ workers: 1,
11
+ reporter: [['list'], ['html', { open: 'never' }]],
12
+ use: {
13
+ baseURL: process.env.E2E_BASE_URL || 'http://localhost:8080',
14
+ storageState: 'e2e/.auth/state.json',
15
+ trace: 'retain-on-failure',
16
+ screenshot: 'only-on-failure',
17
+ actionTimeout: 15_000,
18
+ },
19
+ projects: [
20
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
21
+ ],
22
+ webServer: process.env.E2E_STACK_EXTERNAL ? undefined : {
23
+ command: 'bash scripts/e2e-stack.sh up',
24
+ url: process.env.E2E_BASE_URL || 'http://localhost:8080',
25
+ timeout: 180_000,
26
+ reuseExistingServer: !process.env.CI,
27
+ },
28
+ });