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.
- package/README.md +44 -0
- package/bin/cli.js +41 -0
- package/package.json +34 -0
- package/template/.claude/agents/debugger.md +47 -0
- package/template/.claude/agents/e2e-runner.md +63 -0
- package/template/.claude/agents/implementer.md +78 -0
- package/template/.claude/agents/infra-provisioner.md +159 -0
- package/template/.claude/agents/planner.md +78 -0
- package/template/.claude/agents/researcher.md +103 -0
- package/template/.claude/agents/synthesizer.md +74 -0
- package/template/.claude/agents/test-author.md +71 -0
- package/template/.claude/anpunkit-manifest.json +256 -0
- package/template/.claude/commands/infra.md +62 -0
- package/template/.claude/commands/log-decision.md +34 -0
- package/template/.claude/commands/log-issue.md +28 -0
- package/template/.claude/commands/overview.md +106 -0
- package/template/.claude/commands/phase.md +202 -0
- package/template/.claude/commands/quick.md +30 -0
- package/template/.claude/commands/replan.md +64 -0
- package/template/.claude/commands/store-wisdom.md +195 -0
- package/template/.claude/commands/synthesize.md +26 -0
- package/template/.claude/commands/unstuck.md +40 -0
- package/template/.claude/hooks/cursor-session-start.sh +14 -0
- package/template/.claude/hooks/pre-compact.sh +25 -0
- package/template/.claude/hooks/session-start.sh +135 -0
- package/template/.claude/hooks/subagent-stop.sh +11 -0
- package/template/.claude/settings.json +16 -0
- package/template/.claude/skills/caveman/SKILL.md +39 -0
- package/template/.claude/skills/grill-me/SKILL.md +10 -0
- package/template/.claude/skills/karpathy-guidelines/SKILL.md +34 -0
- package/template/.cursor/commands/infra.md +57 -0
- package/template/.cursor/commands/log-decision.md +29 -0
- package/template/.cursor/commands/log-issue.md +23 -0
- package/template/.cursor/commands/overview.md +102 -0
- package/template/.cursor/commands/phase.md +197 -0
- package/template/.cursor/commands/quick.md +25 -0
- package/template/.cursor/commands/replan.md +59 -0
- package/template/.cursor/commands/store-wisdom.md +191 -0
- package/template/.cursor/commands/synthesize.md +22 -0
- package/template/.cursor/commands/unstuck.md +36 -0
- package/template/.cursor/hooks.json +14 -0
- package/template/.cursor/rules/anpunkit.md +11 -0
- package/template/.gitattributes +12 -0
- package/template/AGENTS.md +216 -0
- package/template/CLAUDE.md +70 -0
- package/template/README.md +283 -0
- package/template/commands.src/infra.md +62 -0
- package/template/commands.src/log-decision.md +34 -0
- package/template/commands.src/log-issue.md +28 -0
- package/template/commands.src/overview.md +106 -0
- package/template/commands.src/phase.md +202 -0
- package/template/commands.src/quick.md +30 -0
- package/template/commands.src/replan.md +64 -0
- package/template/commands.src/store-wisdom.md +195 -0
- package/template/commands.src/synthesize.md +26 -0
- package/template/commands.src/unstuck.md +40 -0
- package/template/docker-compose.test.yml +34 -0
- package/template/docs/DESIGN_LOG.md +578 -0
- package/template/e2e/global-setup.ts +57 -0
- package/template/playwright.config.ts +28 -0
- package/template/scripts/auth-setup.sh +51 -0
- package/template/scripts/e2e-stack.sh +65 -0
- package/template/scripts/regression.sh +46 -0
- package/template/setup.sh +236 -0
- package/template/tests/phase-1/README.md +4 -0
- 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
|
+
});
|