create-anpunkit 2.1.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.
- package/bin/cli.js +2 -2
- package/package.json +2 -3
- package/template/.claude/agents/debugger.md +1 -1
- package/template/.claude/agents/e2e-runner.md +56 -29
- package/template/.claude/agents/implementer.md +14 -8
- package/template/.claude/agents/infra-provisioner.md +53 -59
- package/template/.claude/agents/planner.md +99 -30
- package/template/.claude/agents/researcher.md +16 -1
- package/template/.claude/agents/spec-author.md +126 -0
- package/template/.claude/agents/synthesizer.md +1 -1
- package/template/.claude/agents/test-author.md +81 -63
- package/template/.claude/anpunkit-manifest.json +66 -107
- package/template/.claude/commands/infra.md +7 -3
- package/template/.claude/commands/log-decision.md +1 -1
- package/template/.claude/commands/log-issue.md +1 -1
- package/template/.claude/commands/overview.md +55 -13
- package/template/.claude/commands/phase.md +153 -81
- package/template/.claude/commands/quick.md +1 -1
- package/template/.claude/commands/replan.md +1 -1
- package/template/.claude/commands/store-wisdom.md +1 -1
- package/template/.claude/commands/synthesize.md +1 -1
- package/template/.claude/commands/unstuck.md +1 -1
- package/template/.claude/hooks/session-start.sh +5 -11
- package/template/.claude/ref/compression.md +56 -0
- package/template/.claude/skills/karpathy-guidelines/SKILL.md +1 -1
- package/template/.gitattributes +1 -0
- package/template/AGENTS.md +264 -130
- package/template/CLAUDE.md +5 -9
- package/template/README.md +116 -136
- package/template/commands.src/infra.md +7 -3
- package/template/commands.src/log-decision.md +1 -1
- package/template/commands.src/log-issue.md +1 -1
- package/template/commands.src/overview.md +55 -13
- package/template/commands.src/phase.md +153 -81
- package/template/commands.src/quick.md +1 -1
- package/template/commands.src/replan.md +1 -1
- package/template/commands.src/store-wisdom.md +1 -1
- package/template/commands.src/synthesize.md +1 -1
- package/template/commands.src/unstuck.md +1 -1
- package/template/docs/DESIGN_LOG.md +414 -9
- package/template/knowledge/azure.md +161 -0
- package/template/knowledge/webapp.md +265 -0
- package/template/scripts/spec-conformance.sh +93 -0
- package/template/scripts/spec-staleness.sh +115 -0
- package/template/setup.sh +30 -111
- package/template/tests/helpers/spec-assert.py +162 -0
- package/template/tests/helpers/spec-assert.ts +158 -0
- package/template/.claude/hooks/cursor-session-start.sh +0 -17
- package/template/.claude/skills/caveman/SKILL.md +0 -39
- package/template/.cursor/commands/infra.md +0 -82
- package/template/.cursor/commands/log-decision.md +0 -29
- package/template/.cursor/commands/log-issue.md +0 -23
- package/template/.cursor/commands/overview.md +0 -145
- package/template/.cursor/commands/phase.md +0 -249
- package/template/.cursor/commands/quick.md +0 -25
- package/template/.cursor/commands/replan.md +0 -73
- package/template/.cursor/commands/store-wisdom.md +0 -191
- package/template/.cursor/commands/synthesize.md +0 -22
- package/template/.cursor/commands/unstuck.md +0 -36
- package/template/.cursor/hooks.json +0 -14
- package/template/.cursor/rules/anpunkit.md +0 -11
- package/template/anpunkit.png +0 -0
- package/template/docker-compose.test.yml +0 -34
- package/template/e2e/global-setup.ts +0 -57
- package/template/index.html +0 -340
- package/template/playwright.config.ts +0 -28
- package/template/scripts/auth-setup.sh +0 -51
- package/template/scripts/e2e-stack.sh +0 -65
|
@@ -17,6 +17,53 @@
|
|
|
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
|
+
|
|
42
|
+
- **(v2.2)** — **Spec-driven behavioral contract.** Closes the v2.1 residual:
|
|
43
|
+
fabrication under a thin acceptance surface. In v2.1 a phase's whole contract was
|
|
44
|
+
a single `acceptance:` line; `implementer` and `test-author` each independently
|
|
45
|
+
elaborated it, and when both filled the same gap the same plausible-but-wrong way
|
|
46
|
+
the suite went green on a broken core — TEST REVIEW (review of *coverage*, after
|
|
47
|
+
RED) could not catch a faithful mapping to a *fabricated* criterion. The reframe:
|
|
48
|
+
the reviewable, authoritative artifact is the SPEC, not the test. New mechanism —
|
|
49
|
+
skeleton case-NAMES locked at `/overview` (planner) → per-phase concrete filling by
|
|
50
|
+
a new **`spec-author`** (real inputs/expecteds in shared `fixtures/`) → **SPEC
|
|
51
|
+
REVIEW** human gate UPSTREAM of code → tests GENERATED from the locked rows
|
|
52
|
+
(deep-equality + matcher tokens for `data`, Playwright-from-descriptor for `ui`),
|
|
53
|
+
sharing the same fixtures so a test cannot assert different values than the spec.
|
|
54
|
+
Two new orchestrator-procedure scripts (`spec-staleness.sh`, `spec-conformance.sh`)
|
|
55
|
+
replace the human TEST REVIEW gate. `test-author`/`e2e-runner` become harness
|
|
56
|
+
EMITTERS; `implementer` reads the filled spec as its contract (author ≠ implementer).
|
|
57
|
+
New decision entries §5.43–§5.56. New files: `.claude/agents/spec-author.md`,
|
|
58
|
+
`scripts/spec-staleness.sh`, `scripts/spec-conformance.sh`, `tests/helpers/
|
|
59
|
+
spec-assert.{py,ts}`, plus per-project `docs/spec-phase-<n>.md` + `fixtures/`.
|
|
60
|
+
REMOVED: the v2.1 human TEST REVIEW gate (humans no longer review tests).
|
|
61
|
+
NOTE: the v2.1 entries §5.33–§5.42 (TEST REVIEW gate, mandatory frontend E2E +
|
|
62
|
+
evidence, DATAFLOW coverage, datasource understanding, auth proof/liveness,
|
|
63
|
+
installer tool-selection) live in the v2.1 release bundle and the on-disk AGENTS.md
|
|
64
|
+
hard rules; they were never merged into this on-disk log (a hybrid file). The v2.2
|
|
65
|
+
decision numbers continue at §5.43 for continuity with the requirements doc.
|
|
66
|
+
|
|
20
67
|
- **(v2.0.2)** — KB setup stays PATH-FIRST: the user clones the KB repo
|
|
21
68
|
themselves (`git clone ... ~/anpunkit-kb`) and passes `--kb-path` (or the
|
|
22
69
|
interactive prompt). New: the remote URL is auto-recorded from the clone's
|
|
@@ -178,11 +225,14 @@ Hard-stop at 3 forces either /unstuck (re-research) or human intervention.
|
|
|
178
225
|
Subagent → orchestrator → user. A subagent that directly asks the user would
|
|
179
226
|
fragment the conversation and make the orchestrator's state inconsistent.
|
|
180
227
|
|
|
181
|
-
### 5.8 Skill vendoring is fallback-only
|
|
228
|
+
### 5.8 Skill vendoring is fallback-only *(superseded by §5.62, v2.3)*
|
|
182
229
|
caveman and grill-me are vendored in `.claude/skills/` as fallback copies only.
|
|
183
230
|
The global `~/.claude/skills/` copy is authoritative. The vendored copy is used
|
|
184
231
|
only if the global is missing. This prevents the project copy from diverging
|
|
185
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.
|
|
186
236
|
|
|
187
237
|
### 5.9 DESIGN_LOG.md is append-only
|
|
188
238
|
History is never rewritten. Decisions that turn out to be wrong get a new entry
|
|
@@ -414,7 +464,11 @@ logic; called out for the upgrade smoke test).
|
|
|
414
464
|
**Rejected:** clobbering reinstall; reimplementing setup.sh logic in Node (drift);
|
|
415
465
|
imperative per-version migrations as the default.
|
|
416
466
|
|
|
417
|
-
### 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
|
+
|
|
418
472
|
**Problem:** the kit was Claude-only; maintaining parallel per-tool command copies
|
|
419
473
|
by hand would drift.
|
|
420
474
|
|
|
@@ -452,6 +506,304 @@ native wiring).
|
|
|
452
506
|
|
|
453
507
|
---
|
|
454
508
|
|
|
509
|
+
> **Numbering note (v2.2).** §5.33–§5.42 are the v2.1 decisions (TEST REVIEW gate,
|
|
510
|
+
> mandatory frontend E2E + evidence, DATAFLOW transition coverage, datasource
|
|
511
|
+
> understanding, auth proof/liveness, installer tool-selection). They were recorded
|
|
512
|
+
> in the v2.1 release bundle and are reflected in AGENTS.md's hard rules, but were
|
|
513
|
+
> never merged into this on-disk log. v2.2 continues at §5.43 for continuity with the
|
|
514
|
+
> v2.2 requirements doc rather than renumbering.
|
|
515
|
+
|
|
516
|
+
### 5.43 Spec file is a per-phase artifact, not inline in PLAN.md (v2.2)
|
|
517
|
+
**Problem:** a phase's whole behavioral contract was one `acceptance:` line; two
|
|
518
|
+
agents independently elaborated it and could agree on the same wrong contract.
|
|
519
|
+
**Decision:** `docs/spec-phase-<n>.md` is a new per-phase artifact; `PLAN.md`'s
|
|
520
|
+
`acceptance:` stays a one-line summary, the spec is its concrete expansion. The spec
|
|
521
|
+
is self-correlating: a GENERATED header transcludes that phase's `acceptance:` line +
|
|
522
|
+
the in-scope `DATAFLOW.md` transition rows and embeds a staleness hash; only the
|
|
523
|
+
example-case table is human-authored. **Rejected:** expanding `acceptance:` inline in
|
|
524
|
+
PLAN.md — couples the contract to `/replan` churn, bloats PLAN, mixes phase-ordering
|
|
525
|
+
review with concrete-behavior review.
|
|
526
|
+
|
|
527
|
+
### 5.44 Two-layer baseline/delta — case-names up front, values per phase (v2.2)
|
|
528
|
+
**Decision:** lock behavior in two passes. (1) Up-front case-NAME contract at
|
|
529
|
+
`/overview`: for every DATAFLOW transition + every acceptance criterion, enumerate
|
|
530
|
+
the case names (happy / each edge / each failure + error code), no values yet —
|
|
531
|
+
skeleton spec files. (2) Per-phase concrete filling at `/phase` start (after
|
|
532
|
+
RESEARCH, before SCAFFOLD): `spec-author` fills real payloads/outputs grounded in
|
|
533
|
+
fresh research + the confirmed datasource baseline. **Rationale:** the only design
|
|
534
|
+
that gives BOTH "complete requirements locked before implementation" (names up front)
|
|
535
|
+
and "values grounded in real facts, not plan-time guesses" (per-phase fill). Mirrors
|
|
536
|
+
the established baseline/delta shape (datasource understanding, DATAFLOW coverage).
|
|
537
|
+
|
|
538
|
+
### 5.45 Skeleton→filled single file; "no unfilled stub" is the conformance invariant (v2.2)
|
|
539
|
+
**Decision:** skeletons are generated at `/overview` (names + `TBD`); `spec-author`
|
|
540
|
+
fills in-place. Invariant: no `TBD` may remain when `spec-conformance.sh` runs
|
|
541
|
+
(between RED and GREEN). **Consequence:** a case that cannot be filled from research
|
|
542
|
+
is escalated as UNDERSPEC / `CASE-SET-DIVERGENCE` BEFORE SCAFFOLD, not discovered at
|
|
543
|
+
GREEN.
|
|
544
|
+
|
|
545
|
+
### 5.46 Case structure: table row + boundary-typed fixture-ref (v2.2)
|
|
546
|
+
**Decision:** each case is one row — `case-id | covers | boundary | input-ref |
|
|
547
|
+
expected-ref | error-code | volatile`. `covers` = a DATAFLOW transition-id
|
|
548
|
+
(convention `Object:from->to`, no new DATAFLOW column) or acceptance criterion-id
|
|
549
|
+
(`ACC-<k>`). `boundary` ∈ {`data`,`ui`}. Refs point to `fixtures/<case-id>-*.json`
|
|
550
|
+
(scalars may inline). **Key property:** the harness loads the SAME fixture files the
|
|
551
|
+
spec row references — transcription from spec to test is STRUCTURAL, not honor-system;
|
|
552
|
+
a test physically cannot assert different values than the spec.
|
|
553
|
+
|
|
554
|
+
### 5.47 Boundary test is system-of-record per case; optional mock mirror (v2.2)
|
|
555
|
+
**Decision:** every case has exactly one BOUNDARY test — `data`: real
|
|
556
|
+
HTTP/CLI/message vs the running backend + real services (v2.1's "real API suite");
|
|
557
|
+
`ui`: Playwright vs the deployed/local-docker target (v2.1's E2E). The boundary test
|
|
558
|
+
is the phase gate; green mock alone never closes (rule 5 / §5.38 preserved). A mock
|
|
559
|
+
mirror is optional, shares the fixture + comparator via `TEST_MODE`. "E2E" is
|
|
560
|
+
generalized to *outer-boundary*, not browser-specific: on `has_frontend: false` all
|
|
561
|
+
cases are `data` and `ui` rows are invalid.
|
|
562
|
+
|
|
563
|
+
### 5.48 Human TEST REVIEW removed; assertion is generated deep-equality (v2.2)
|
|
564
|
+
**Decision:** remove the v2.1 human TEST REVIEW gate. Humans never review tests.
|
|
565
|
+
Tests are not authored — a harness generator emits a deep-equality assertion against
|
|
566
|
+
`fixtures/<case-id>-expected.json` per case row. SPEC REVIEW (§5.50) is the
|
|
567
|
+
human gate; it sits upstream (before SCAFFOLD), reviews concrete behavior in plain
|
|
568
|
+
language, and is the correctness-bearing moment TEST REVIEW was not (wrong thing,
|
|
569
|
+
wrong layer: TEST REVIEW reviewed coverage and asked humans to read assertions).
|
|
570
|
+
|
|
571
|
+
### 5.49 Volatile fields use matcher tokens in the fixture (v2.2)
|
|
572
|
+
**Decision:** volatile fields (generated IDs, timestamps, order, cursors) are matched
|
|
573
|
+
by TYPE, not excluded. The expected fixture carries sentinels — `<UUID>`, `<ISO8601>`,
|
|
574
|
+
`<ANY_STRING>`, `<ANY_NUMBER>`, `<UNORDERED>`, `<MATCHES:regex>`. The per-language
|
|
575
|
+
comparator `tests/helpers/spec-assert.*` honors them. A token asserts PRESENCE +
|
|
576
|
+
SHAPE, never "ignore this field" (a missing volatile field still fails). The
|
|
577
|
+
comparator is bounded + kit-versioned (one impl per supported language); no
|
|
578
|
+
per-project comparator code.
|
|
579
|
+
|
|
580
|
+
### 5.50 SPEC REVIEW — per-phase human gate on concrete fillings (v2.2)
|
|
581
|
+
**Decision:** after `spec-author` fills + `spec-staleness.sh` passes, the orchestrator
|
|
582
|
+
surfaces filled cases as FALSIFIABLE CLAIMS; the human confirms/corrects. Rejection
|
|
583
|
+
classification: wrong expected → fix row + fixture (no re-research); wrong input shape
|
|
584
|
+
→ re-dispatch `spec-author`; new case OR contradicted case → hard re-entry (§5.52).
|
|
585
|
+
Unskippable on TDD phases; absent on non-TDD. Replaces v2.1 human TEST REVIEW.
|
|
586
|
+
|
|
587
|
+
### 5.51 Up-front case-name completeness folded into existing /overview approval (v2.2)
|
|
588
|
+
**Decision:** the existing end-of-`/overview` approval (PLAN.md + DATAFLOW.md) is
|
|
589
|
+
EXTENDED to confirm the cross-phase skeleton case-name set (per phase: covered
|
|
590
|
+
transition/acceptance ids + enumerated case names, no values). One confirmation, one
|
|
591
|
+
surface, no new gate — the names derive from the same PLAN + DATAFLOW being approved.
|
|
592
|
+
|
|
593
|
+
### 5.52 Any case-set divergence at phase time → hard re-entry (v2.2)
|
|
594
|
+
**Decision:** if phase-time RESEARCH reveals a case not enumerated up front, or one
|
|
595
|
+
that contradicts an enumerated case (locked contract impossible), the phase STOPS.
|
|
596
|
+
`spec-author` returns `CASE-SET-DIVERGENCE`; the human amends the up-front skeleton;
|
|
597
|
+
re-confirms the amended case(s) + any case sharing their `covers` id (the staleness
|
|
598
|
+
hash certifies the unchanged remainder — not re-read); `spec-author` re-fills from
|
|
599
|
+
scratch. **Rationale:** AI writes code; the human owns the contract. No fold-forward,
|
|
600
|
+
no AI amendment of the case-name set.
|
|
601
|
+
|
|
602
|
+
### 5.53 Dedicated spec-author agent; author ≠ implementer invariant (v2.2)
|
|
603
|
+
**Decision:** a new `spec-author` (Opus) fills the per-phase spec + fixtures after
|
|
604
|
+
RESEARCH, before SPEC REVIEW; returns `CASE-SET-DIVERGENCE` when a case can't be
|
|
605
|
+
filled from real facts or research reveals an unlisted branch. It is NOT
|
|
606
|
+
`implementer`. The **author ≠ implementer** invariant replaces v2.1's test-author
|
|
607
|
+
blindness invariant: `implementer` reads the filled spec as its contract (normal
|
|
608
|
+
spec-driven development), `test-author` becomes a harness emitter (reads spec rows,
|
|
609
|
+
generates the assertion harness, authors no assertions).
|
|
610
|
+
|
|
611
|
+
### 5.54 UI boundary cases use generated Playwright; e2e-runner is an emitter (v2.2)
|
|
612
|
+
**Decision:** for `ui` cases, `e2e-runner` stops authoring blind and EMITS a
|
|
613
|
+
Playwright assertion from the case's `fixtures/<case-id>-ui.json` descriptor. The
|
|
614
|
+
descriptor vocabulary is closed + kit-versioned: `visible`, `text-equals`, `enabled`,
|
|
615
|
+
`count`. Screenshot-on-each-UI-existence-assertion (v2.1 §5.35) is retained as the
|
|
616
|
+
visual backstop. `ui` cases have no mock mirror (the browser is the boundary). On
|
|
617
|
+
`has_frontend: false`, `ui` rows are invalid.
|
|
618
|
+
|
|
619
|
+
### 5.55 spec-staleness.sh + spec-conformance.sh as orchestrator-procedure scripts (v2.2)
|
|
620
|
+
**Decision:** two new scripts, same idiom as `regression.sh` (`set -euo pipefail`,
|
|
621
|
+
nonzero = loud fail). `spec-staleness.sh` re-hashes the current PLAN acceptance line +
|
|
622
|
+
in-scope DATAFLOW rows and compares to the spec's embedded header hash (`stamp`
|
|
623
|
+
subcommand embeds it at `/overview`/regenerate; default `check` compares); runs after
|
|
624
|
+
SPEC fill + at SPEC REVIEW entry. `spec-conformance.sh` asserts no `TBD` remains and
|
|
625
|
+
every case-id is cited by a boundary test (`# spec: <case-id>`); runs between RED and
|
|
626
|
+
GREEN, replacing the human TEST REVIEW gate. Both port to Cursor for free (same
|
|
627
|
+
`/phase` body, same bash scripts — no new hook wiring). **Design choice (impl):**
|
|
628
|
+
hashing lives ONLY in the script (a `stamp` subcommand), not duplicated in agent
|
|
629
|
+
prompts; transition-ids are derived (`Object:from->to`) so DATAFLOW needs no new column.
|
|
630
|
+
|
|
631
|
+
### 5.56 Rule 14 re-seamed: transition→case reachability; conformance owns case→test (v2.2)
|
|
632
|
+
**Decision:** v2.1 rule 14 (DATAFLOW transition coverage) is re-seamed. New
|
|
633
|
+
responsibility: every reachable DATAFLOW transition must have ≥1 FILLED CASE in
|
|
634
|
+
`docs/spec-phase-<n>.md` (was: ≥1 test in `tests/regression/`). Reachability judgment
|
|
635
|
+
unchanged (orchestrator decides at CLOSE; unreachable list PENDING; all live by final
|
|
636
|
+
phase or FAIL HARD). The case→test half moves to `spec-conformance.sh` (every case-id
|
|
637
|
+
cited by a boundary test, which by placement convention lands in `tests/regression/`).
|
|
638
|
+
The full guarantee is preserved by the chain: reachable transition → filled case
|
|
639
|
+
(rule 14) → cited boundary test (conformance) → regression corpus (placement). The
|
|
640
|
+
ENDPOINTS coverage gate is unchanged and orthogonal (endpoint-keyed vs behavior-keyed).
|
|
641
|
+
|
|
642
|
+
**Numbering reconciliation (v2.2).** The requirements doc's hard-rule table labeled
|
|
643
|
+
new rules 12–16, but its own "Non-changes" section retains the frontend-E2E (old 13),
|
|
644
|
+
datasource (old 15) and auth (old 16) rules — so the table is illustrative, not literal.
|
|
645
|
+
AGENTS.md realizes a coherent set: rule **12** = SPEC REVIEW (reuses the slot freed by
|
|
646
|
+
the removed TEST REVIEW); rule **14** = re-seamed DATAFLOW (kept in place); rules
|
|
647
|
+
**13/15/16** keep their v2.1 meanings (frontend E2E / datasource / auth); the remaining
|
|
648
|
+
new rules append as **17** (spec staleness), **18** (spec conformance), **19** (author ≠
|
|
649
|
+
implementer). This preserves every existing `hard rule N` cross-reference except 12.
|
|
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
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
455
807
|
## 6. Current file inventory
|
|
456
808
|
```
|
|
457
809
|
|
|
@@ -542,6 +894,49 @@ create-anpunkit/ npx package (package.json, bin/cli.js, build.
|
|
|
542
894
|
ownership taxonomy, adapter generation, idempotent JSON hook merge, VERIFY,
|
|
543
895
|
backup, `--dry-run`).
|
|
544
896
|
|
|
897
|
+
### v2.2 additions to the inventory
|
|
898
|
+
|
|
899
|
+
```
|
|
900
|
+
.claude/agents/spec-author.md NEW — fills per-phase spec + fixtures (Opus); author ≠ implementer
|
|
901
|
+
scripts/spec-staleness.sh NEW — stamp/check the spec header hash vs PLAN+DATAFLOW (rule 17)
|
|
902
|
+
scripts/spec-conformance.sh NEW — no-TBD + every case-id cited by a boundary test (rule 18)
|
|
903
|
+
tests/helpers/spec-assert.py NEW — kit comparator (matcher tokens), Python
|
|
904
|
+
tests/helpers/spec-assert.ts NEW — kit comparator (matcher tokens), TS/JS
|
|
905
|
+
docs/spec-phase-<n>.md NEW per-project — skeleton at /overview, filled per phase (committed)
|
|
906
|
+
fixtures/<case-id>-*.json NEW per-project — shared by spec row + generated harness (committed)
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
Role deltas: `planner` also writes skeleton specs; `test-author` + `e2e-runner`
|
|
910
|
+
become harness EMITTERS (generate from spec rows, author no assertions);
|
|
911
|
+
`implementer` FILL reads the filled spec as its contract. `.gitattributes` gains
|
|
912
|
+
`*.py text eol=lf` (the kit now ships a Python comparator). REMOVED: the v2.1 human
|
|
913
|
+
TEST REVIEW gate + the `docs/test-plan-phase-<n>.md` artifact.
|
|
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
|
+
|
|
545
940
|
## 7. How each original problem maps to its fix
|
|
546
941
|
|
|
547
942
|
| Problem | Fix |
|
|
@@ -552,7 +947,7 @@ backup, `--dry-run`).
|
|
|
552
947
|
| 4 context rot in debug | `debugger` isolated context + writes noise to file |
|
|
553
948
|
| 5 want orchestration | 8 scoped subagents; `/phase` orchestrates them |
|
|
554
949
|
| 6 handoff bloat | `synthesizer` + PreCompact hook; HISTORY.md for long log |
|
|
555
|
-
| 7
|
|
950
|
+
| 7 cloud auth friction + infra ad-hoc | INFRA.md `## AUTH` liveness ritual (Azure: `knowledge/azure.md`) + `infra-provisioner` Phase 0 when `infra_needed` |
|
|
556
951
|
| 8 re-discovering same gotchas across projects | KB snapshot at session start; researcher checks before web; `/store-wisdom` promotes findings |
|
|
557
952
|
|
|
558
953
|
---
|
|
@@ -560,9 +955,11 @@ backup, `--dry-run`).
|
|
|
560
955
|
## 8. Known gaps & template placeholders
|
|
561
956
|
|
|
562
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.)
|
|
563
959
|
2. `e2e/global-setup.ts` — MSAL cache-key shape is a stub; adjust for app's
|
|
564
|
-
`@azure/msal-browser` version/config.
|
|
565
|
-
3.
|
|
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.
|
|
566
963
|
4. Visual-regression testing — explicit non-goal.
|
|
567
964
|
5. Agent Teams / cross-session parallelism — deferred.
|
|
568
965
|
6. SessionEnd guard hook — discussed, not built.
|
|
@@ -587,13 +984,21 @@ backup, `--dry-run`).
|
|
|
587
984
|
4. The phase gate is a REAL test. A mock suite never closes a phase.
|
|
588
985
|
5. Noisy work writes full output to `docs/research/` and returns only
|
|
589
986
|
terse summary + path — keeps the orchestrator context clean.
|
|
590
|
-
6.
|
|
591
|
-
|
|
987
|
+
6. The behavioral contract is the human-approved SPEC; tests are GENERATED from its
|
|
988
|
+
case rows against shared fixtures (author ≠ implementer). This is the v2.2
|
|
989
|
+
unbiased-test guarantee — it replaces v2.1's "tests written blind" (the spec
|
|
990
|
+
fixtures are authored before code, so the contract can't be shaped to an impl).
|
|
991
|
+
(Non-TDD phases keep blind-from-acceptance tests — no spec to drive them.)
|
|
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)
|
|
592
994
|
8. Fail loud, never silent — especially hook wiring.
|
|
593
995
|
9. Honest failure classification — environment issues never burn the debug budget.
|
|
594
|
-
10.
|
|
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.)
|
|
595
998
|
11. Plan before code: design-research + double grill before OVERVIEW.md is written.
|
|
596
999
|
The planner must never design phases against unverified assumptions.
|
|
597
|
-
12.
|
|
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".)
|
|
598
1003
|
13. The shared KB is human-gated at both ends: you approve before push (/store-wisdom),
|
|
599
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
|
+
```
|