create-anpunkit 2.2.0 → 2.3.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 (63) hide show
  1. package/bin/cli.js +2 -2
  2. package/package.json +2 -3
  3. package/template/.claude/agents/debugger.md +1 -1
  4. package/template/.claude/agents/e2e-runner.md +13 -3
  5. package/template/.claude/agents/implementer.md +1 -1
  6. package/template/.claude/agents/infra-provisioner.md +53 -59
  7. package/template/.claude/agents/planner.md +35 -29
  8. package/template/.claude/agents/researcher.md +16 -1
  9. package/template/.claude/agents/spec-author.md +9 -1
  10. package/template/.claude/agents/synthesizer.md +1 -1
  11. package/template/.claude/agents/test-author.md +9 -1
  12. package/template/.claude/anpunkit-manifest.json +52 -113
  13. package/template/.claude/commands/infra.md +7 -3
  14. package/template/.claude/commands/log-decision.md +1 -1
  15. package/template/.claude/commands/log-issue.md +1 -1
  16. package/template/.claude/commands/overview.md +37 -12
  17. package/template/.claude/commands/phase.md +50 -25
  18. package/template/.claude/commands/quick.md +1 -1
  19. package/template/.claude/commands/replan.md +1 -1
  20. package/template/.claude/commands/store-wisdom.md +1 -1
  21. package/template/.claude/commands/synthesize.md +1 -1
  22. package/template/.claude/commands/unstuck.md +1 -1
  23. package/template/.claude/hooks/session-start.sh +5 -11
  24. package/template/.claude/ref/compression.md +56 -0
  25. package/template/.claude/skills/karpathy-guidelines/SKILL.md +1 -1
  26. package/template/AGENTS.md +115 -84
  27. package/template/CLAUDE.md +4 -9
  28. package/template/README.md +91 -122
  29. package/template/commands.src/infra.md +7 -3
  30. package/template/commands.src/log-decision.md +1 -1
  31. package/template/commands.src/log-issue.md +1 -1
  32. package/template/commands.src/overview.md +37 -12
  33. package/template/commands.src/phase.md +50 -25
  34. package/template/commands.src/quick.md +1 -1
  35. package/template/commands.src/replan.md +1 -1
  36. package/template/commands.src/store-wisdom.md +1 -1
  37. package/template/commands.src/synthesize.md +1 -1
  38. package/template/commands.src/unstuck.md +1 -1
  39. package/template/docs/DESIGN_LOG.md +222 -8
  40. package/template/knowledge/azure.md +161 -0
  41. package/template/knowledge/webapp.md +265 -0
  42. package/template/setup.sh +30 -111
  43. package/template/.claude/hooks/cursor-session-start.sh +0 -17
  44. package/template/.claude/skills/caveman/SKILL.md +0 -39
  45. package/template/.cursor/commands/infra.md +0 -82
  46. package/template/.cursor/commands/log-decision.md +0 -29
  47. package/template/.cursor/commands/log-issue.md +0 -23
  48. package/template/.cursor/commands/overview.md +0 -162
  49. package/template/.cursor/commands/phase.md +0 -296
  50. package/template/.cursor/commands/quick.md +0 -25
  51. package/template/.cursor/commands/replan.md +0 -73
  52. package/template/.cursor/commands/store-wisdom.md +0 -191
  53. package/template/.cursor/commands/synthesize.md +0 -22
  54. package/template/.cursor/commands/unstuck.md +0 -36
  55. package/template/.cursor/hooks.json +0 -14
  56. package/template/.cursor/rules/anpunkit.md +0 -11
  57. package/template/anpunkit.png +0 -0
  58. package/template/docker-compose.test.yml +0 -34
  59. package/template/e2e/global-setup.ts +0 -57
  60. package/template/index.html +0 -339
  61. package/template/playwright.config.ts +0 -28
  62. package/template/scripts/auth-setup.sh +0 -51
  63. package/template/scripts/e2e-stack.sh +0 -65
@@ -2,7 +2,7 @@
2
2
  description: Compress handoff docs, dedup the issue log, prune snapshots. Run before /clear.
3
3
  ---
4
4
 
5
- Caveman ULTRA mode.
5
+ compression: user (.claude/ref/compression.md).
6
6
 
7
7
  Trigger: end of a phase, or any time STATE.md / ISSUES.md feel bloated.
8
8
 
@@ -2,7 +2,7 @@
2
2
  description: Forced re-research after a circuit breaker. Stops flailing, re-routes to deep research with full memory of dead ends.
3
3
  ---
4
4
 
5
- Caveman ULTRA mode.
5
+ compression: user (.claude/ref/compression.md).
6
6
 
7
7
  Trigger: I chose "re-research" at a circuit breaker (see /phase step 5).
8
8
 
@@ -17,6 +17,28 @@
17
17
 
18
18
  Newest first. One entry per architectural change. Appended by `/log-decision`.
19
19
 
20
+ - **(v2.3)** — **Compression internalized + core/use-case separation + Cursor drop.**
21
+ Two streams. **Stream A:** the external `caveman` skill (vendored fallback +
22
+ "global-authoritative if installed" resolution + model-invoked triggers) becomes a
23
+ kit-native, single-source, always-on reference (`.claude/ref/compression.md`) with
24
+ two named profiles — `user` (human-facing, keeps the Auto-Clarity exception) and
25
+ `internal` (agent↔agent, harder). ~40 scattered "Caveman ULTRA mode" phrases
26
+ collapse to one-line profile pointers; exact-output artifacts are exempted by
27
+ structural gates in the `spec-author` / `test-author` / `e2e-runner` prompts.
28
+ **Stream B:** the invariant core (loop, gates, doc system, commands) is separated
29
+ from web/Azure practice, which becomes preinstalled knowledge docs
30
+ (`knowledge/webapp.md`, `knowledge/azure.md`) consulted deterministically by
31
+ `researcher` at RESEARCH. New OVERVIEW flags — `project_type`, `infra_needed`,
32
+ `e2e_kind`, `deploy_kind`, `knowledge_docs` — declared at `/overview`, bound
33
+ per-phase at RESEARCH: Phase 0 exists iff `infra_needed`; the boundary run is
34
+ selected by `e2e_kind`; the final phase completes `deploy_kind`. Cursor support is
35
+ hard-dropped (Claude Code only). New decision entries §5.57–§5.71. NEW files:
36
+ `.claude/ref/compression.md`, `knowledge/webapp.md`, `knowledge/azure.md`. REMOVED:
37
+ `.claude/skills/caveman/`, `.cursor/**`, `.claude/hooks/cursor-session-start.sh`,
38
+ and the installer tool-selection machinery. CUT from default install (embedded as
39
+ templates in the knowledge docs): `playwright.config.ts`, `e2e/`,
40
+ `docker-compose.test.yml`, `scripts/e2e-stack.sh`, `scripts/auth-setup.sh`.
41
+
20
42
  - **(v2.2)** — **Spec-driven behavioral contract.** Closes the v2.1 residual:
21
43
  fabrication under a thin acceptance surface. In v2.1 a phase's whole contract was
22
44
  a single `acceptance:` line; `implementer` and `test-author` each independently
@@ -203,11 +225,14 @@ Hard-stop at 3 forces either /unstuck (re-research) or human intervention.
203
225
  Subagent → orchestrator → user. A subagent that directly asks the user would
204
226
  fragment the conversation and make the orchestrator's state inconsistent.
205
227
 
206
- ### 5.8 Skill vendoring is fallback-only
228
+ ### 5.8 Skill vendoring is fallback-only *(superseded by §5.62, v2.3)*
207
229
  caveman and grill-me are vendored in `.claude/skills/` as fallback copies only.
208
230
  The global `~/.claude/skills/` copy is authoritative. The vendored copy is used
209
231
  only if the global is missing. This prevents the project copy from diverging
210
232
  from the user's customized global version.
233
+ > **Superseded (v2.3, §5.62):** caveman is internalized to `.claude/ref/
234
+ > compression.md`; grill-me becomes the sole authoritative source. The
235
+ > "global-authoritative if installed" resolution rule is dropped entirely.
211
236
 
212
237
  ### 5.9 DESIGN_LOG.md is append-only
213
238
  History is never rewritten. Decisions that turn out to be wrong get a new entry
@@ -439,7 +464,11 @@ logic; called out for the upgrade smoke test).
439
464
  **Rejected:** clobbering reinstall; reimplementing setup.sh logic in Node (drift);
440
465
  imperative per-version migrations as the default.
441
466
 
442
- ### 5.32 Multi-tool adapters — Claude Code + Cursor (v2.0)
467
+ ### 5.32 Multi-tool adapters — Claude Code + Cursor (v2.0) *(reversed by §5.69, v2.3)*
468
+ > **Reversed (v2.3, §5.69):** Cursor support is hard-dropped. The `.cursor/**`
469
+ > tree, `cursor-session-start.sh`, and the installer tool-selection machinery are
470
+ > removed; `commands.src/` now generates only `.claude/commands/`. Claude Code only.
471
+
443
472
  **Problem:** the kit was Claude-only; maintaining parallel per-tool command copies
444
473
  by hand would drift.
445
474
 
@@ -619,6 +648,160 @@ the removed TEST REVIEW); rule **14** = re-seamed DATAFLOW (kept in place); rule
619
648
  new rules append as **17** (spec staleness), **18** (spec conformance), **19** (author ≠
620
649
  implementer). This preserves every existing `hard rule N` cross-reference except 12.
621
650
 
651
+ ### 5.57 Compression rules: standalone kit-native reference file (v2.3)
652
+ **Decision:** compression rules live in exactly one kit-native reference file,
653
+ `.claude/ref/compression.md` (replaces the vendored `caveman` skill). AGENTS.md and
654
+ every crafted subagent prompt POINT to it; none restate the rules. Not placed under
655
+ `.claude/skills/` — no description/trigger surface, so §5.61 is satisfied
656
+ structurally, not by discipline.
657
+ **Rejected:** rules inside AGENTS.md (subagents don't inherit it — they run their own
658
+ crafted prompts by design); rules duplicated per prompt (drift).
659
+
660
+ ### 5.58 Two named compression profiles (v2.3)
661
+ **Decision:** `user` (human-facing; keeps the Auto-Clarity exception — security
662
+ warnings, irreversible-action confirmations, multi-step sequences, clarification
663
+ requests) and `internal` (agent→agent; harder compression, no clarity exception).
664
+ **Rejected:** one rule set with a conditional exception (implicit, weaker than a named
665
+ structural declaration).
666
+
667
+ ### 5.59 Invocation sites collapse to profile pointers (v2.3)
668
+ **Decision:** ~40 "Caveman ULTRA mode" phrases rewrite to a one-line profile pointer
669
+ per prompt — subagents `compression: internal (.claude/ref/compression.md).`, commands
670
+ `compression: user (…)`; AGENTS.md declares the `user` directive; the SessionStart hook
671
+ reminder references the profile system.
672
+ **Rejected:** outright deletion (breaks coverage — no inheritance); keeping full phrases
673
+ (redundant, drift-prone).
674
+
675
+ ### 5.60 Positive artifact exemption as structural gate (v2.3)
676
+ **Decision:** exact-output artifacts (RED harness assertions, spec-author contract
677
+ output, fixtures, emitted Playwright, `# spec:`/`// spec:` citations, quoted errors)
678
+ are exempted from compression via a positive structural gate declared in the
679
+ `spec-author`, `test-author`, and `e2e-runner` prompts — not a rule inside the
680
+ reference file. Compression is always-on, so exemption must be explicit at the artifact
681
+ boundary.
682
+ **Rejected:** exemption as a rule in the reference file (mixes concerns); inline prose
683
+ per agent (non-structural, unverifiable).
684
+
685
+ ### 5.61 Model-invoked compression triggers killed (v2.3)
686
+ **Decision:** the skill description triggers ("caveman mode", "be brief", `/caveman`)
687
+ are deleted. Compression exists only as an always-on directive + profile pointers.
688
+ Mandatory behavior is never model-invoked.
689
+
690
+ ### 5.62 grill-me vendored as sole source (v2.3)
691
+ **Decision:** the vendored `grill-me` copy is authoritative; the "global-authoritative
692
+ if installed" resolution rule is dropped entirely (it existed to serve two external
693
+ skills; caveman's internalization leaves one consumer, which does not justify a
694
+ resolution mechanism). §5.8 is superseded by this.
695
+ **Rejected:** keeping the resolution rule for one consumer (dead mechanism).
696
+
697
+ ### 5.63 User freedom = composition, not enforcement (v2.3)
698
+ **Decision:** users choose use case and modules; every installed gate stays HARD. No
699
+ config to soften gates to warnings. Non-agent-behavior files (templates, scaffold,
700
+ infra scripts) must be use-case-agnostic.
701
+ **Rejected:** gate-softening config (a soft gate is a discipline rule; discipline rules
702
+ are ruled out — "worry-free or fail hard").
703
+
704
+ ### 5.64 LCD core + matured practice as preinstalled knowledge docs (v2.3)
705
+ **Decision:** the core ships nothing use-case-specific. Matured practices become
706
+ preinstalled knowledge docs — `knowledge/webapp.md`, `knowledge/azure.md` — consulted
707
+ by `researcher` (new STEP 0.5) at RESEARCH when the declared use case matches. Branch-
708
+ only reference via pointer, loaded only when the branch triggers; reuses existing
709
+ machinery (researcher already consults `research/INDEX.md` + KB snapshot). Selection is
710
+ a deterministic table in `commands.src/overview.md`, recorded as OVERVIEW
711
+ `knowledge_docs:` — never researcher-inferred (§5.66).
712
+ **Rejected:** project-type scaffolds at install (designs N scaffolds upfront, guesses at
713
+ demand); pure LCD without knowledge docs (guts the "ready-made" value users praised).
714
+
715
+ ### 5.65 Adaptive loop: RESEARCH binds E2E_KIND / DEPLOY_KIND (v2.3)
716
+ **Decision:** the TDD APPLICABILITY check at RESEARCH is extended to bind two further
717
+ per-phase values, same idiom as `TDD_PHASE`: `E2E_KIND` (browser via Playwright, CLI
718
+ invocation, HTTP, library public API — generalizes v2.2's browser-specific boundary
719
+ concept to outer-boundary) and, on the final phase, `DEPLOY_KIND` confirmation. "Last
720
+ phase always includes deployment" is re-worded to "last phase always completes
721
+ DEPLOY_KIND."
722
+ **Realization (ordering split):** §5.65's literal "RESEARCH sets the flags" cannot hold
723
+ for `INFRA_NEEDED` because `/phase` PRE-FLIGHT (which blocks on Phase 0) runs BEFORE
724
+ RESEARCH. Resolved by splitting DECLARATION from BINDING: the project-level facts
725
+ (`project_type`, `infra_needed`, `e2e_kind` baseline, `deploy_kind`, `knowledge_docs`)
726
+ are declared at `/overview` and recorded in OVERVIEW.md (same idiom as `has_frontend`),
727
+ where PRE-FLIGHT can read them; RESEARCH does the per-phase binding (effective
728
+ `E2E_KIND` = browser iff frontend phase else baseline; `DEPLOY_KIND` re-confirm on final
729
+ phase).
730
+ **Realization (browser-only dispatch):** `e2e-runner` stays a Playwright emitter,
731
+ dispatched only when the bound `E2E_KIND` is `browser`; for cli/http/library-api the
732
+ generalized rule 13 is satisfied by `test-author`'s real-mode boundary suite against the
733
+ outer surface, with its transcript captured as evidence — no separate shipped-artifact
734
+ runner.
735
+ **Rejected:** dropping deploy/E2E from the core loop (weakens the completeness
736
+ guarantee); a universal outer-boundary emitter for all kinds (more prompt surface than
737
+ the real-mode suite already provides).
738
+
739
+ ### 5.66 Project type declared at /overview (v2.3)
740
+ **Decision:** the `/overview` grill asks `project_type` (web app / desktop app / script
741
+ / library / other). The answer is recorded in `docs/OVERVIEW.md` (alongside
742
+ `has_frontend` + frontend root). `researcher` reads it to select knowledge docs;
743
+ RESEARCH uses it (via the derived flags) to shape the loop.
744
+ **Rejected:** researcher infers type from description (a model-invoked trigger for
745
+ mandatory behavior — the pattern §5.61 kills).
746
+
747
+ ### 5.67 Minimal core cut line (v2.3)
748
+ **Decision:** default install ships `AGENTS.md`, hooks, slash commands, agent prompts,
749
+ the doc system (STATE/ISSUES/synthesizer/snapshots), knowledge docs, gate scripts. Cut
750
+ from default install: `playwright.config.ts`, `e2e/`, `docker-compose.test.yml`,
751
+ `scripts/e2e-stack.sh`, `scripts/auth-setup.sh`. Cut items are NOT deleted from the kit
752
+ — they are embedded as fenced templates in `knowledge/webapp.md` / `knowledge/azure.md`
753
+ and materialized per-project on demand (then user-owned, like `fixtures/`). Chosen over
754
+ a non-installed templates dir, which would need new manifest/setup.sh gating machinery
755
+ right after the Cursor gating was removed.
756
+ **Retention constraint (user feedback #4):** doc auto-update + slash commands survive
757
+ untouched in the minimal core.
758
+ **Rejected:** interactive module picker at install (defer until real packs exist).
759
+
760
+ ### 5.68 Infra-provisioner conditional; Azure → knowledge doc (v2.3)
761
+ **Decision:** `infra-provisioner` stays in the kit, prompt generalized (Azure specifics
762
+ — Bicep, `az`, Entra/MSAL, THB costs, Key Vault — move to `knowledge/azure.md`). The
763
+ `infra_needed` flag (declared at `/overview`: application types → true; script/library →
764
+ false) gates Phase 0 dispatch. The SessionStart Azure reminder line and `auth-setup.sh`
765
+ leave core; the Azure knowledge doc reintroduces the auth ritual (materializable
766
+ `scripts/auth-setup.sh` template) when Azure is in play, and the project's liveness
767
+ command is recorded in `docs/INFRA.md ## AUTH` so `/phase` PRE-FLIGHT stays generic.
768
+ Old hard rule 10 (Azure auth once-per-session) moves to `knowledge/azure.md`; its slot
769
+ is repurposed for the new deterministic-flags rule (zero renumbering — all `hard rule N`
770
+ cross-references keep their numbers).
771
+ **Rejected:** dropping infra-provisioner from core (loses mandatory coverage for
772
+ application use cases); keeping the Azure-fitted prompt (violates §5.63); renumbering the
773
+ hard rules (needless churn — rules 1–19 are contiguous, the slot repurposes cleanly).
774
+
775
+ ### 5.69 Cursor hard drop (v2.3)
776
+ **Decision:** delete `.cursor/hooks.json`, `.cursor/commands/` generation, `.cursor/
777
+ rules/` pointer, Cursor sections in README/docs, `cursor-session-start.sh`, and the
778
+ dual-tool wiring in `setup.sh` + `create-anpunkit` (the whole `--tools`/`--add-tool`/
779
+ `anpunkit-tools.json` tool-selection machinery, dead once one tool remains). README
780
+ states "Claude Code only." A deliberate reversal of the v2.x Cursor-parity investment
781
+ (v2.2 explicitly maintained parity): maintenance cost exceeds demand per user feedback.
782
+ §5.32 (installer tool selection) is reversed by this.
783
+ **Rejected:** soft drop with a "portable if you re-wire" note (half-promise, support
784
+ burden).
785
+
786
+ ### 5.70 spec-author / test-author tool grants settled (v2.3, v2.2 carry-over)
787
+ **Decision:** `spec-author` and `test-author` are granted `Read, Grep, Glob, Write,
788
+ Bash` — deliberately NO `Edit`. `spec-author` authors fresh spec rows and fixtures
789
+ (Write), never patches source; at SPEC REVIEW a "wrong expected" correction is applied
790
+ by the ORCHESTRATOR patching the fixture directly (`phase.md §3`), not by re-granting
791
+ `spec-author` edit rights. Recorded here to close the v2.2 carry-over.
792
+ **Rejected:** granting `Edit` for in-place fixture patches (blurs author ≠ implementer,
793
+ §5.53; the orchestrator-patch path already covers it).
794
+
795
+ ### 5.71 fixtures/ committed, never gitignored (v2.3, v2.2 carry-over)
796
+ **Decision:** `fixtures/<case-id>-{input,expected,ui}.json` are COMMITTED — they are the
797
+ behavioral contract that both the spec row and the generated test harness load
798
+ (transcription is structural, not honor-system). Deliberate contrast with
799
+ `docs/evidence/`, `docs/research/*.md`, `.env.test`, `docs/.snapshots/`, and
800
+ `docs/.kb-snapshot.md`, which ARE gitignored. `.gitignore` therefore must NOT list
801
+ `fixtures/`. Recorded here to close the v2.2 carry-over.
802
+ **Rejected:** gitignoring fixtures (would break the contract-load guarantee — the test
803
+ would have no expected values to assert against on a fresh checkout).
804
+
622
805
  ---
623
806
 
624
807
  ## 6. Current file inventory
@@ -729,6 +912,31 @@ become harness EMITTERS (generate from spec rows, author no assertions);
729
912
  `*.py text eol=lf` (the kit now ships a Python comparator). REMOVED: the v2.1 human
730
913
  TEST REVIEW gate + the `docs/test-plan-phase-<n>.md` artifact.
731
914
 
915
+ ### v2.3 changes to the inventory
916
+
917
+ ```
918
+ .claude/ref/compression.md NEW — kit-native compression profiles (user, internal); single source
919
+ knowledge/webapp.md NEW — matured web-app practice + browser-E2E stack templates
920
+ knowledge/azure.md NEW — matured Azure practice + auth-ritual/provisioning templates
921
+ .claude/skills/caveman/ REMOVED — internalized to .claude/ref/compression.md
922
+ .cursor/ REMOVED — hooks.json, rules/anpunkit.md, commands/ (Cursor dropped, §5.69)
923
+ .claude/hooks/cursor-session-start.sh REMOVED — Cursor JSON-envelope wrapper
924
+ .claude/anpunkit-tools.json REMOVED (per-project) — installer tool-selection machinery gone
925
+ playwright.config.ts CUT from default install — now a template in knowledge/webapp.md
926
+ e2e/global-setup.ts CUT — template in knowledge/webapp.md
927
+ docker-compose.test.yml CUT — template in knowledge/webapp.md
928
+ scripts/e2e-stack.sh CUT — template in knowledge/webapp.md
929
+ scripts/auth-setup.sh CUT — template in knowledge/azure.md
930
+ ```
931
+
932
+ Role deltas (v2.3): `researcher` gains STEP 0.5 (consult OVERVIEW `knowledge_docs`
933
+ before web search); `infra-provisioner` generalized (cloud specifics → knowledge/
934
+ azure.md, dispatched only when `infra_needed`); `planner` Phase-0-conditional +
935
+ `deploy_kind` completion; `e2e-runner` = browser E2E_KIND emitter. `commands.src/`
936
+ now generates only `.claude/commands/`. Hard rule 10 repurposed (Azure auth →
937
+ knowledge/azure.md; new slot = deterministic flags); rules 8/13/16 generalized in
938
+ place; rules stay 1–19 contiguous.
939
+
732
940
  ## 7. How each original problem maps to its fix
733
941
 
734
942
  | Problem | Fix |
@@ -739,7 +947,7 @@ TEST REVIEW gate + the `docs/test-plan-phase-<n>.md` artifact.
739
947
  | 4 context rot in debug | `debugger` isolated context + writes noise to file |
740
948
  | 5 want orchestration | 8 scoped subagents; `/phase` orchestrates them |
741
949
  | 6 handoff bloat | `synthesizer` + PreCompact hook; HISTORY.md for long log |
742
- | 7 Azure auth friction + infra ad-hoc | `auth-setup.sh` + `infra-provisioner` Phase 0 |
950
+ | 7 cloud auth friction + infra ad-hoc | INFRA.md `## AUTH` liveness ritual (Azure: `knowledge/azure.md`) + `infra-provisioner` Phase 0 when `infra_needed` |
743
951
  | 8 re-discovering same gotchas across projects | KB snapshot at session start; researcher checks before web; `/store-wisdom` promotes findings |
744
952
 
745
953
  ---
@@ -747,9 +955,11 @@ TEST REVIEW gate + the `docs/test-plan-phase-<n>.md` artifact.
747
955
  ## 8. Known gaps & template placeholders
748
956
 
749
957
  1. `docker-compose.test.yml` — build contexts and ports are TEMPLATE values.
958
+ (v2.3: now a fenced template in `knowledge/webapp.md`, materialized per project.)
750
959
  2. `e2e/global-setup.ts` — MSAL cache-key shape is a stub; adjust for app's
751
- `@azure/msal-browser` version/config.
752
- 3. caveman vendored copy is the base version, not the user's ULTRA variant.
960
+ `@azure/msal-browser` version/config. (v2.3: template in `knowledge/webapp.md`.)
961
+ 3. *(resolved v2.3, §5.57)* caveman is now kit-native and single-source
962
+ (`.claude/ref/compression.md`) — there is no vendored-vs-user-variant divergence.
753
963
  4. Visual-regression testing — explicit non-goal.
754
964
  5. Agent Teams / cross-session parallelism — deferred.
755
965
  6. SessionEnd guard hook — discussed, not built.
@@ -779,12 +989,16 @@ TEST REVIEW gate + the `docs/test-plan-phase-<n>.md` artifact.
779
989
  unbiased-test guarantee — it replaces v2.1's "tests written blind" (the spec
780
990
  fixtures are authored before code, so the contract can't be shaped to an impl).
781
991
  (Non-TDD phases keep blind-from-acceptance tests — no spec to drive them.)
782
- 7. Never script the Microsoft login UI.
992
+ 7. Boundary auth uses a dedicated headless test credential; never script an
993
+ interactive login UI (Microsoft/Entra specifics: `knowledge/azure.md`). (v2.3)
783
994
  8. Fail loud, never silent — especially hook wiring.
784
995
  9. Honest failure classification — environment issues never burn the debug budget.
785
- 10. Azure infra is Phase 0: provisioned once, reviewed before apply, recorded in INFRA.md.
996
+ 10. Infra is Phase 0 when `INFRA_NEEDED`: provisioned once, reviewed before apply,
997
+ recorded in INFRA.md. (v2.3: conditional on the declared use case.)
786
998
  11. Plan before code: design-research + double grill before OVERVIEW.md is written.
787
999
  The planner must never design phases against unverified assumptions.
788
- 12. Deployment is always in the last phase never omitted, never a separate phase.
1000
+ 12. The last phase always completes `DEPLOY_KIND`cloud deploy, package publish, or
1001
+ verified install/run — never omitted (only `none` with a recorded reason), never a
1002
+ separate phase. (v2.3: generalized from "deployment".)
789
1003
  13. The shared KB is human-gated at both ends: you approve before push (/store-wisdom),
790
1004
  and findings are static within a session (KB snapshot). The KB never writes itself.
@@ -0,0 +1,161 @@
1
+ # knowledge/azure.md — matured Azure practice (preinstalled)
2
+
3
+ > **What this file is.** Use-case knowledge for Azure-deployed projects,
4
+ > consulted by `researcher` (STEP 0.5) when `knowledge/azure.md` appears in
5
+ > the `knowledge_docs:` list of `docs/OVERVIEW.md`. Selection is deterministic
6
+ > — declared at `/overview`, never inferred (hard rule 10).
7
+ >
8
+ > **Templates below are materialized per-project** — copied to the stated path
9
+ > the first time the phase work needs them. Once materialized they are
10
+ > user-owned project files.
11
+
12
+ ## Session auth ritual (was core hard rule 10, v2.1–v2.2)
13
+
14
+ **Azure project? Run `scripts/auth-setup.sh` ONCE per session before any Azure
15
+ work. Never debug an auth error without checking this first.**
16
+
17
+ - Materialize the template below to `scripts/auth-setup.sh` at Phase 0.
18
+ - `/infra` records the project's liveness command in `docs/INFRA.md` under
19
+ `## AUTH` — for Azure that command is `bash scripts/auth-setup.sh` — so the
20
+ `/phase` PRE-FLIGHT auth-liveness gate (hard rule 16) knows what to run.
21
+ - An expired `az` token mid-session looks like a resource failure. It is not a
22
+ bug: re-run the ritual, then retry.
23
+
24
+ ## Entra / MSAL practice
25
+
26
+ - E2E auth = ROPC (resource-owner password credential) with a **dedicated test
27
+ account excluded from MFA via Conditional Access**. NEVER script the
28
+ Microsoft login UI (hard rule 8's Azure concretes).
29
+ - Requirements on the app registration: ROPC enabled; test user exists; MFA
30
+ exclusion applied. The ROPC token-fetch template lives in
31
+ `knowledge/webapp.md` (`e2e/global-setup.ts`).
32
+ - AUTH PROOF (hard rule 16): the Entra/MSAL app login must be obtainable
33
+ headlessly TWICE IN A ROW (prime + cached/refresh reuse) with zero prompts,
34
+ alongside every external datasource credential (Azure SQL, Tableau, …).
35
+ - Failure alias: **AZURE UNAVAILABLE** ≙ SERVICE UNAVAILABLE (hard rule 7) —
36
+ outage/throttle/expired auth, not a bug, no debug budget.
37
+
38
+ ## Provisioning practice (infra-provisioner, Azure mode)
39
+
40
+ IaC = Bicep, committed to `infra/` (audit trail):
41
+
42
+ - `infra/main.bicep` — top-level deployment
43
+ - `infra/modules/<service>.bicep` — one module per service type
44
+ - `infra/params.json` — parameters (committed; no secrets in it)
45
+
46
+ Bicep rules:
47
+
48
+ - All resource names as parameters with defaults
49
+ - All secrets written to Key Vault, not hardcoded
50
+ - Tags on every resource: `project`, `environment`, `managedBy: anpunkit`
51
+ - Cost comment on every resource: `// est. cost: <THB/month>`
52
+ - Production sizing by default; region default: Southeast Asia
53
+ - Naming convention default: `<project-short>-<resource-type>-<env>`
54
+ - No hardcoded subscription/tenant IDs
55
+
56
+ Commands:
57
+
58
+ - What-if (NEVER skip, human reviews before apply):
59
+ `az deployment group what-if --resource-group <rg> --template-file infra/main.bicep --parameters @infra/params.json`
60
+ (new RG: `az deployment sub what-if --location <region> --template-file infra/main.bicep`)
61
+ - Apply (only after human APPROVED):
62
+ `az deployment group create --resource-group <rg> --template-file infra/main.bicep --parameters @infra/params.json`
63
+ - Verify: `az resource show --ids <resource-id>` per resource; SKU match;
64
+ Key Vault secrets present by name.
65
+
66
+ ### `docs/INFRA.md` Azure sections (template)
67
+
68
+ ```markdown
69
+ ## Azure environment
70
+ - Subscription: <name> (<id>)
71
+ - Resource group: <name>
72
+ - Region: <location>
73
+
74
+ ## Entra / MSAL
75
+ - Tenant ID: <value>
76
+ - App Client ID: <value>
77
+ - ROPC enabled: yes/no/check-manually
78
+ - Test user: <upn or "not yet created">
79
+ - Conditional Access MFA exclusion: <yes/pending>
80
+
81
+ ## AUTH
82
+ - liveness command: bash scripts/auth-setup.sh
83
+
84
+ ## Cost summary
85
+ | Resource | SKU | Est. THB/month |
86
+ |---|---|---|
87
+ | **Total** | | **~X THB/month** |
88
+ ```
89
+
90
+ ### `.env.test` Azure variable set
91
+
92
+ ```bash
93
+ # .env.test — generated by infra-provisioner
94
+ E2E_TENANT_ID=<from INFRA.md>
95
+ E2E_CLIENT_ID=<from INFRA.md>
96
+ E2E_TEST_USER=<from INFRA.md or "FILL_IN">
97
+ E2E_TEST_PASSWORD=<fetch from Key Vault>
98
+ E2E_SCOPE=api://<client-id>/.default
99
+ E2E_SQL_SERVER=<from INFRA.md>
100
+ E2E_SQL_DB=<from INFRA.md>
101
+ E2E_SQL_USER=<from INFRA.md or "FILL_IN">
102
+ E2E_SQL_PASSWORD=<fetch from Key Vault>
103
+ E2E_BASE_URL=<deployed app URL or http://localhost:8080>
104
+ E2E_STACK_EXTERNAL=<1 if azure-deployed, empty if local Docker>
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Templates
110
+
111
+ ### `scripts/auth-setup.sh` — materialize to `scripts/`
112
+
113
+ ```bash
114
+ #!/usr/bin/env bash
115
+ # scripts/auth-setup.sh — verify Azure CLI session is ready.
116
+ set -euo pipefail
117
+ cd "${CLAUDE_PROJECT_DIR:-.}"
118
+
119
+ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
120
+ ok() { printf "${GREEN} OK${NC} %s\n" "$1"; }
121
+ warn() { printf "${YELLOW} WARN${NC} %s\n" "$1"; }
122
+ fail() { printf "${RED} FAIL${NC} %s\n" "$1"; }
123
+
124
+ echo ""
125
+ echo "=== Azure session check ==="
126
+ echo ""
127
+
128
+ if ! command -v az >/dev/null 2>&1; then
129
+ fail "az CLI not found. Install from https://learn.microsoft.com/cli/azure/install-azure-cli"
130
+ exit 1
131
+ fi
132
+ AZ_VER=$(az version --query '"azure-cli"' -o tsv 2>/dev/null || echo "unknown")
133
+ ok "az CLI present (version: ${AZ_VER})"
134
+
135
+ ACCOUNT_JSON=$(az account show 2>/dev/null || true)
136
+ if [ -z "$ACCOUNT_JSON" ]; then
137
+ warn "Not logged in. Running az login..."
138
+ az login
139
+ ACCOUNT_JSON=$(az account show 2>/dev/null || true)
140
+ fi
141
+
142
+ if [ -z "$ACCOUNT_JSON" ]; then
143
+ fail "az login failed or was cancelled."
144
+ exit 1
145
+ fi
146
+
147
+ SUB_NAME=$(echo "$ACCOUNT_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['name'])" 2>/dev/null || echo "unknown")
148
+ SUB_ID=$(echo "$ACCOUNT_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])" 2>/dev/null || echo "unknown")
149
+ ok "Logged in — subscription: ${SUB_NAME} (${SUB_ID})"
150
+
151
+ # Token freshness check
152
+ TOKEN_EXP=$(az account get-access-token --query expiresOn -o tsv 2>/dev/null || echo "")
153
+ if [ -n "$TOKEN_EXP" ]; then
154
+ ok "Token valid until: ${TOKEN_EXP}"
155
+ else
156
+ warn "Could not determine token expiry — may need re-login during session."
157
+ fi
158
+
159
+ echo ""
160
+ echo "Azure session ready. You can now run /infra or Azure-dependent phases."
161
+ ```