truecourse 0.6.9 → 0.7.0-next.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 CHANGED
@@ -20,7 +20,7 @@
20
20
  TrueCourse catches two classes of defect, through two independent tools — use either on its own or both together:
21
21
 
22
22
  - **Code defects** (`truecourse analyze`) — from the categories linters cover (unused code, style, missing types) through to ones they don't reach: circular dependencies, layer violations, dead modules, race conditions, security anti-patterns, performance footguns. Tree-sitter analysis combined with LLM review.
23
- - **Business-logic drift** (`truecourse verify`) — when the implementation no longer matches what the docs say it should do. Wrong response codes, missing entity fields, illegal state transitions, bypassed auth, silently-dropped effects, formulas that have lost an input. TrueCourse extracts a contract from your PRDs/ADRs/READMEs and checks the code against it.
23
+ - **Business-logic drift** (`truecourse guard`) — when the implementation no longer matches what the docs say it should do. TrueCourse curates your PRDs/ADRs/READMEs into a spec corpus, an LLM authors **scenario tests bound to each spec section** once, and `guard run` executes them deterministically a failing scenario means that section and the code disagree.
24
24
 
25
25
  Both store their results under `.truecourse/` and surface them in a shared [dashboard](#dashboard-web-ui) for human review, with plain-text CLI output an agent can read directly.
26
26
 
@@ -28,7 +28,7 @@ Both store their results under `.truecourse/` and surface them in a shared [dash
28
28
  <img src="assets/demo.gif" alt="TrueCourse Screenshot" width="100%" />
29
29
  </p>
30
30
 
31
- Jump to: **[1. Analyze](#1-analyze--code-intelligence)** · **[2. Spec → Verify](#2-spec--verify--business-logic-drift)** · **[Dashboard](#dashboard-web-ui)**
31
+ Jump to: **[1. Analyze](#1-analyze--code-intelligence)** · **[2. Spec → Guard](#2-spec--guard--business-logic-drift)** · **[Dashboard](#dashboard-web-ui)**
32
32
 
33
33
  No setup step and no database: TrueCourse creates `.truecourse/` in your repo on first use and stores everything there as plain JSON files. It uses the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) for LLM-powered work — if `claude` isn't on your PATH, deterministic analysis still runs and LLM-dependent features are skipped.
34
34
 
@@ -175,110 +175,110 @@ pre-commit:
175
175
 
176
176
  ---
177
177
 
178
- # 2. Spec → Verify — business-logic drift
178
+ # 2. Spec → Guard — business-logic drift
179
179
 
180
- TrueCourse builds a machine-readable spec from your docs and verifies the code against itcatching where the implementation has drifted from documented intent. This is a separate pipeline from `analyze`: it answers a different question, has different prerequisites (it reads your docs), and runs on a different time scale.
180
+ TrueCourse builds a curated spec corpus from your docs, then **guards** it: an LLM authors declarative scenario tests bound to each spec section once, and running them is fully deterministicno model in the verification loop. A failing scenario means "this section and the code disagree" (a drift or a bug — the developer's call). This is a separate pipeline from `analyze`: it answers a different question, has different prerequisites (it reads your docs), and runs on a different time scale.
181
181
 
182
- > **Prerequisite:** the contract extractor and conflict resolver shell out to the Claude Code CLI (`claude -p`). Install Claude Code and sign in once before running `spec scan` or `contracts generate`.
182
+ > **Prerequisite:** the spec scan and guard generator shell out to the Claude Code CLI (`claude -p`). Install Claude Code and sign in once before running `spec scan` or `guard generate`. `guard run` needs neither — it's deterministic.
183
+
184
+ > **Discontinued: contract generation & verify.** Guard replaces the earlier contracts pipeline (`contracts generate` / `contracts list` / `contracts validate`, `truecourse verify`, `truecourse drifts`, `truecourse infer`, and the dashboard's BL Drift section). Those commands still exist in this release but are **no longer maintained** and will be removed; the BL Drift section is already hidden from the dashboard. Use Guard instead: it verifies what the code *does* (by executing scenarios) rather than what it *says* (static contract matching), and its coverage isn't capped by a contract-kind taxonomy.
183
185
 
184
186
  ## Quick Start
185
187
 
186
188
  ```bash
187
189
  cd <your-repo>
188
- truecourse spec scan # Read docs → extract claims surface conflicts
189
- truecourse spec resolve --all-defaults # Accept the recommended pick on each conflict
190
- truecourse contracts generate # Canonical spec → .tc contract artifacts
191
- truecourse verify # Check code against the contracts drifts
190
+ truecourse spec scan # Curate docs → corpus (areas + relations + overlap flags)
191
+ truecourse spec conflicts list # Review flagged overlaps (resolve with `spec conflicts resolve`)
192
+ truecourse guard generate # Author scenario tests from spec sections (classify generate birth-validate)
193
+ truecourse guard run # Run the committed scenarios; exits non-zero on any drift (CI gate)
192
194
  ```
193
195
 
194
- Resolve conflicts and review drifts visually in the [dashboard](#dashboard-web-ui)'s BL Drift section, or drive every step from the CLI.
196
+ Resolve conflicts and review section coverage, scenarios, and run results visually in the [dashboard](#dashboard-web-ui)'s Guard section, or drive every step from the CLI.
195
197
 
196
- > Like `analyze`, the spec → contracts → verify track requires a **git repository** — TrueCourse's baselines are commit-anchored (committable `LATEST.json`, diff vs HEAD, stashing the committed state). On a non-git folder these commands stop with a clear message and the dashboard hides their actions.
198
+ > Like `analyze`, the spec → guard track requires a **git repository** — TrueCourse's baselines are commit-anchored (committable `LATEST.json`, diff vs HEAD). On a non-git folder these commands stop with a clear message and the dashboard hides their actions.
197
199
 
198
200
  ## How it works
199
201
 
200
- Three stages run in order, each producing artifacts the next consumes:
202
+ Stages run in order, each producing committable artifacts the next consumes:
201
203
 
202
- **1. Spec consolidation** — Walks every `.md` file in the repo (PRDs, ADRs, RFCs, READMEs, design notes; `.truecourse/`, `node_modules/`, `.git/` etc. are skipped). An LLM relevance filter drops obvious non-spec material (task lists, research logs, AI agent prompts). For the docs that remain, an LLM extracts structured claims per block and groups them by `(topic, subject)`. Agreements auto-merge; genuine disagreements surface as **conflicts** in the dashboard with a plain-English explanation of what differs. Output: `.truecourse/specs/claims.json` (the structured snapshot every downstream stage consumes — modules + per-claim content + provenance) and `.truecourse/specs/decisions.json` (the user's resolutions, version chains, and overrides — committable).
204
+ **1. Spec consolidation** — Walks every `.md` file in the repo (PRDs, ADRs, RFCs, READMEs, design notes; `.truecourse/`, `node_modules/`, `.git/` etc. are skipped). An LLM relevance filter drops obvious non-spec material (task lists, research logs, AI agent prompts). For the docs that remain, an LLM tags each into **areas** (`product/concern`), auto-detects doc→doc **relations** (version chains, supersession), and flags within-area **overlaps** where two docs may disagree. Output: `.truecourse/specs/corpus.json` (the curated corpus every downstream stage consumes — kept docs + area tags, docs grouped by area, overlap flags, relations, and the relevance-dropped docs; committable) and `.truecourse/specs/decisions.json` (the user's resolutions: doc→doc `relations`, `manualAreas`, `manualIncludes`, and `manualExcludes` — committable).
203
205
 
204
- Auto-resolve rules cut the conflict count substantially: byte-identical content, status-tolerant duplicates, same-file consolidation, docKind-dominance pickups, and detected version chains. [Plan](docs/contracts/PLAN_CONFLICT_RESOLUTION.md).
206
+ Only genuine within-area **disagreements** flag as overlaps docs that agree never surface. Version chains (e.g. `v1`→`v2`) are auto-detected; you resolve the rest with doc→doc relations, in the dashboard's Guard → Coverage tab or via `spec conflicts` / `spec chains`. [Plan](docs/SPEC_SCAN_REDESIGN_PLAN.md).
205
207
 
206
- **2. Contract extraction** Reads `claims.json` and emits `.truecourse/contracts/*.tc` files in a hand-written DSL covering 13 artifact kinds: `operation`, `entity`, `enum`, `state-machine`, `auth-requirement`, `authorization-rule`, `error-envelope`, `pagination-contract`, `idempotency-contract`, `effect-group`, `formula`, plus `unenforceable-obligation` for prose the verifier can't structurally check. A post-extraction **repair pass** validates structural completeness and re-prompts the LLM to fix deficient artifacts (missing forbids clauses, broad role selectors, unresolved cross-references). On the bundled fixture this hits **22/22 planted bugs with 0 false positives**.
208
+ **2. Guard generation** (`truecourse guard generate`) — Splits each kept doc into sections and, per section: **classifies** whether the section makes a claim a driver can assert (today's driver runs your project's CLI; a non-testable verdict carries a one-sentence reason and surfaces as a visible coverage gap), **authors** one or more declarative YAML scenarios from the section's claim plus the code, and **birth-validates** each one by running it immediately — a scenario that fails at birth is reported as a finding (the spec and the code already disagree) instead of being silently committed. Output, all committable: `.truecourse/scenarios/<area>/*.yaml` (the scenarios), `scenarios/recipe.json` (how to build/prepare the repo for a run), and `scenarios/manifest.json` (section scenario bindings + section fingerprints, so re-generates only touch changed sections).
207
209
 
208
- **3. Verification** — Parses the contracts, walks the source tree, and runs per-kind comparators (operations, entities, state machines, etc.). Drifts surface in the dashboard alongside code violations, and on the CLI through `truecourse verify`. It's its own command not a stage of `truecourse analyze`.
210
+ **3. Guard run** (`truecourse guard run`) Fully deterministic: builds the repo via the recipe, executes every committed scenario, and writes the run to `.truecourse/guard/` (per-run snapshots, `LATEST.json`, per-failure evidence transcripts). Exits non-zero on any drift, so it drops straight into CI. No LLM, no API key, no `claude` binary.
209
211
 
210
- **4. Inference** — The mirror image of verification. `verify` asks "the spec says X — does the code do X?"; `truecourse infer` asks "the code does X — does any spec mention X?". It runs the code-side extractors *un-driven by a spec*, subtracts whatever the authored contracts already cover, and writes the remainder to `.truecourse/contracts/_inferred/` as `.tc` artifacts tagged with an `inferred-from "<code-path>" a..b` provenance line and a `confidence` level (instead of the authored `origin SOURCE "section" a..b`). It covers the full artifact spread — undocumented endpoints, entities (from ORM schema), enums, named constants, query policies, emitted events, computed formulas, architecture choices, and the cross-cutting conventions (auth, pagination, idempotency, error envelope). Confidence reflects fidelity: a value read straight from code is `high`; a synthesized convention (e.g. an assumed auth scheme) is a `low`-confidence draft to confirm. Because coverage is computed from authored contracts only, a decision drops out of `_inferred/` the moment it's documented the directory is a shrinking backlog of "decisions your code made that your docs never recorded". Inferred contracts are descriptive, not prescriptive, so `verify` skips `_inferred/` by default.
212
+ The section scenario binding is **bidirectional**: code changed its scenarios fail (code-side drift); a spec section edited its scenarios go stale (spec-side drift). The spec document itself becomes the coverage UIevery section visibly carries its proof and its status.
211
213
 
212
214
  ## What it catches
213
215
 
214
- Operations whose responses, status codes, or headers don't match the spec. Entities with missing or mistyped fields. Immutability and lifecycle violations on state machines. Missing or forbidden side-effect emissions. Auth requirements bypassed. Pagination, idempotency, and error-envelope contracts violated. Formulas producing wrong results from drifted inputs.
216
+ Any documented behavior a scenario can drive and assert (today through your project's CLI; api/web/tui drivers are planned): wrong responses and exit codes, missing or mistyped output fields, illegal state transitions, bypassed validation and auth rules, silently-dropped side effects, formulas producing wrong results plus the reverse direction: spec sections whose scenarios went stale because the docs changed out from under them.
215
217
 
216
218
  ## Setup
217
219
 
218
- The spec and a verify baseline are committable so they travel with the repo; everything else is local-only. Per-repo layout under `.truecourse/`:
220
+ The spec, the scenarios, and a guard baseline are committable so they travel with the repo; everything else is local-only. Per-repo layout under `.truecourse/`:
219
221
 
220
222
  ```
221
223
  .truecourse/
222
- ├── specs/ ← canonical spec (committable)
223
- │ ├── claims.json ← structured snapshot: modules + claims + provenance
224
- │ └── decisions.json ← user resolutions + version chains + manual includes
225
- ├── contracts/ ← generated TC contract artifacts (gitignored by default)
226
- └── _inferred/ reverse-engineered, undocumented decisions (`truecourse infer`)
227
- ├── verifier/ drift store (mirrors analyze; `truecourse verify`)
228
- ├── runs/ per-run drift snapshots (gitignored)
229
- ├── LATEST.json current drift state + diff baseline (committable)
224
+ ├── specs/ ← curated corpus (committable)
225
+ │ ├── corpus.json ← kept docs + area tags, docs-by-area, overlap flags, relations, dropped docs
226
+ │ └── decisions.json ← user resolutions: doc→doc relations + manual areas + manual includes/excludes
227
+ ├── scenarios/ ← the guard scenario corpus (committable)
228
+ ├── recipe.json how to build/prepare the repo for a run
229
+ ├── manifest.json section scenario bindings + section fingerprints
230
+ └── <area>/*.yaml the scenario tests
231
+ ├── guard/ guard run store (mirrors analyze; `truecourse guard run`)
232
+ │ ├── runs/ ← per-run snapshots (gitignored)
233
+ │ ├── LATEST.json ← current run state (committable)
230
234
  │ ├── history.json ← per-run summaries (gitignored)
231
- └── diff.json current-vs-baseline drift diff (gitignored)
232
- └── .cache/ LLM + slice cache (gitignored)
235
+ ├── evidence/<runId>/ per-failure transcripts (gitignored)
236
+ └── result.json last `guard generate` summary (gitignored)
237
+ └── .cache/ ← LLM caches (gitignored)
233
238
  ```
234
239
 
235
- Like analyze, `verifier/LATEST.json` is the committable baseline — commit it after merging to `main` (re-run `truecourse verify`, commit the result), not from feature branches. `truecourse verify --diff` then shows the drifts your uncommitted changes add or resolve against it.
240
+ Like analyze, `guard/LATEST.json` is the committable baseline — commit it after merging to `main` (re-run `truecourse guard run`, commit the result), not from feature branches.
236
241
 
237
242
  ## Commands
238
243
 
239
244
  ```bash
240
- # Spec consolidation (docs → canonical spec)
241
- truecourse spec scan # Read docs, extract claims, surface conflicts, write claims.json
242
- truecourse spec resolve --all-defaults # Accept the engine's recommended pick on every open conflict
243
- truecourse spec status # Summary: docs, claims, modules, pending decisions
244
-
245
- # Conflict resolution (also available in the dashboard Spec tab)
246
- truecourse spec conflicts list # List open conflicts (add --decided / --all)
247
- truecourse spec conflicts show <id> # Full detail for one conflict
248
- truecourse spec conflicts pick <id> <index> # Resolve by picking a candidate
249
- truecourse spec conflicts custom <id> --text "…" # Resolve with a custom answer
250
- truecourse spec conflicts revoke <id> # Re-open a decided conflict
251
- truecourse spec chains add --older A --newer B # Manually mark a version chain (escape hatch)
252
- truecourse spec chains list / remove
253
- truecourse spec docs skipped # Docs the LLM relevance filter excluded
254
- truecourse spec docs include <path> # Force-include a skipped doc
255
- truecourse spec docs uninclude <path>
256
-
257
- # Contract extraction (canonical spec .tc artifacts)
258
- truecourse contracts generate # Extract / re-extract TC contract files
259
- truecourse contracts list # List artifacts (kind · identity · location)
260
- truecourse contracts list --inferred / --authored # Only reverse-engineered (_inferred/) / only authored
261
- truecourse contracts validate # Parse + resolve TC files; report unresolved refs
262
-
263
- # Verification (code against contracts)
264
- truecourse verify # Full run: stashes dirty tree (prompts), writes verifier/runs + LATEST + history
265
- truecourse verify --diff # Git diff: working-tree drifts vs committed baseline (added/resolved/unchanged)
266
- truecourse verify --stash / --no-stash # Pre-approve / skip stashing on a full run
267
- truecourse drifts list # List drifts from the latest verify (paginated; reads LATEST, no re-run)
268
- truecourse drifts list --all # Show every drift (no pagination)
269
- truecourse drifts list --offset 20 / --severity critical,high # Page through / filter by severity
270
-
271
- # Inference (code → inferred contracts) — reverse-engineer undocumented decisions
272
- truecourse infer # Write inferred .tc files to contracts/_inferred/
273
- truecourse infer --dry-run # Report what would be written, touch nothing
274
- truecourse contracts list --inferred # Review what infer produced (kind · confidence · code location)
245
+ # Spec consolidation (docs → curated corpus)
246
+ truecourse spec scan # Curate docs into corpus.json (areas + doc relations + overlap flags)
247
+ truecourse spec status # Summary: docs, areas, relations, open vs resolved overlaps
248
+
249
+ # Conflict resolution — flagged within-area overlaps → doc→doc relations
250
+ # (agent-friendly; also available in the dashboard Spec tab)
251
+ truecourse spec conflicts list # List flagged within-area overlaps
252
+ truecourse spec conflicts show <area> # An area's overlapping docs with prose excerpts
253
+ truecourse spec conflicts resolve <area> \ # Resolve one by recording a relation
254
+ --older <doc> --newer <doc> --replace|--precedence|--keep-both
255
+ truecourse spec chains list # List doc→doc relations
256
+ truecourse spec chains add --older A --newer B [--type replace|precedence|keep-both]
257
+ truecourse spec chains remove --older A --newer B
258
+ truecourse spec docs list # List the kept (corpus) docs + area tags
259
+ truecourse spec docs skipped # Docs the relevance filter excluded
260
+ truecourse spec docs include <path> # Force-include a skipped doc (re-scans)
261
+ truecourse spec docs uninclude <path> # Remove a force-include override
262
+ truecourse spec docs exclude <path> # Force-exclude a kept doc (re-scans)
263
+ truecourse spec docs unexclude <path> # Remove a force-exclude override
264
+
265
+ # Guard spec-section-bound scenario tests (author once, run deterministically)
266
+ truecourse guard generate # Author scenarios from spec sections (classify generate → birth-validate)
267
+ truecourse guard run # Build via the recipe + run committed scenarios; exits non-zero on any drift (CI gate)
268
+ truecourse guard run --scenario <id> # Run a single scenario
269
+ truecourse guard status # Compact summary: section coverage, last run, last generate (LLM-free, no re-run)
270
+ truecourse guard drifts # List the latest run's non-pass scenarios, most severe first (paginated; --all / --offset / --json)
275
271
  ```
276
272
 
273
+ ### Discontinued: contracts & verify
274
+
275
+ The static contract pipeline that predates Guard still ships in this release but is **discontinued and unmaintained** — it will be removed in a future release. `truecourse contracts generate|list|validate` (corpus → `.tc` artifacts), `truecourse verify` / `truecourse drifts` (per-kind static comparators), and `truecourse infer` (reverse-engineered `.tc` from code) all keep working against the same spec corpus for now, but new work — including new artifact-kind coverage — happens only in Guard.
276
+
277
277
  ---
278
278
 
279
279
  # Dashboard (web UI)
280
280
 
281
- One web UI for both capabilities — browse code findings and business-logic drift side by side, with the architecture graph, analytics, and the spec/contracts/verify workflow.
281
+ One web UI for both capabilities — browse code findings and business-logic drift side by side, with the architecture graph, analytics, and the spec-curation + guard workflow.
282
282
 
283
283
  ```bash
284
284
  truecourse dashboard # Start + open the dashboard
@@ -290,7 +290,9 @@ truecourse dashboard uninstall # Remove the background service
290
290
  ```
291
291
 
292
292
  - **Code Analysis** — architecture graph, violations list, severity/category analytics, code hotspots, trend over time; toggle rules and silence noisy ones inline.
293
- - **BL Drift** — the Spec tab walks you through resolving each conflict (pick / write custom / mark superseded / include skipped doc); Contracts shows the generated `.tc` artifacts; Verify shows the drift analytics + list, with a Runs history and a Normal / Git-Diff toggle.
293
+ - **Guard** — Coverage shows each spec doc's sections with their scenario coverage and walks you through resolving spec conflicts (pick / write custom / mark superseded / include skipped doc); Scenarios lists the committed scenario corpus with the recipe and last-generate summary; Runs shows each run's drifts with per-failure evidence.
294
+
295
+ (The BL Drift section — the discontinued contracts/verify workflow — is hidden from the section switcher; existing deep links into it still render.)
294
296
 
295
297
  ---
296
298
 
@@ -330,27 +332,27 @@ The first `truecourse analyze` (or `truecourse add`) in a fresh repo asks whethe
330
332
 
331
333
  ## LLM transport (`--llm-transport`)
332
334
 
333
- Every LLM-powered step — `analyze`'s LLM rules, and the whole Spec → Verify pipeline (`spec scan`, `spec resolve`, `contracts generate`) — reaches the model through a pluggable **transport**, chosen with `--llm-transport`:
335
+ Every LLM-powered step — `analyze`'s LLM rules, and the whole Spec → Guard pipeline (`spec scan`, `guard generate`) — reaches the model through a pluggable **transport**, chosen with `--llm-transport`:
334
336
 
335
337
  | Mode | How it reaches the model | Needs |
336
338
  |---|---|---|
337
339
  | **`cli`** *(default)* | spawns `claude -p …` per call | the `claude` binary on PATH, signed in. No API key. |
338
340
  | **`agent`** | a **filesystem mailbox** under `--io <dir>` | nothing — no `claude` binary, no API key |
339
341
 
340
- In **`agent`** mode the tool doesn't call the model itself: for each prompt it writes `requests/<id>.json` (`{ stage, system, user, schema, … }`) into the `--io` directory and waits for a matching `responses/<id>.json` (`{ text }`). An **orchestrating agent that is itself an LLM** — e.g. a [Claude Code routine](https://code.claude.com/docs/en/routines) — watches that directory and answers each prompt. This lets contract generation and `analyze`'s LLM rules run **inside a headless cloud session with no `claude` binary and no API key**.
342
+ In **`agent`** mode the tool doesn't call the model itself: for each prompt it writes `requests/<id>.json` (`{ stage, system, user, schema, … }`) into the `--io` directory and waits for a matching `responses/<id>.json` (`{ text }`). An **orchestrating agent that is itself an LLM** — e.g. a [Claude Code routine](https://code.claude.com/docs/en/routines) — watches that directory and answers each prompt. This lets guard generation and `analyze`'s LLM rules run **inside a headless cloud session with no `claude` binary and no API key**.
341
343
 
342
344
  ```bash
343
345
  # default: spawn the claude CLI
344
346
  truecourse analyze --llm
345
- truecourse contracts generate
347
+ truecourse guard generate
346
348
 
347
349
  # agent transport: the tool parks prompts in ./io and an external agent answers them
348
350
  truecourse analyze --llm --llm-transport agent --io ./io
349
- truecourse spec scan --llm-transport agent --io ./io
350
- truecourse contracts generate --llm-transport agent --io ./io
351
+ truecourse spec scan --llm-transport agent --io ./io
352
+ truecourse guard generate --llm-transport agent --io ./io
351
353
  ```
352
354
 
353
- Accepted by: `analyze`, `spec scan`, `spec resolve`, `contracts generate`. (On `analyze`, `--llm` / `--no-llm` is a *separate* flag — it decides **whether** LLM rules run; `--llm-transport` decides **how** to reach the model.) Both modes send identical prompts and parse identical schema-validated JSON — only the delivery differs.
355
+ Accepted by: `analyze`, `spec scan`, `guard generate` (and the discontinued `contracts generate`). (On `analyze`, `--llm` / `--no-llm` is a *separate* flag — it decides **whether** LLM rules run; `--llm-transport` decides **how** to reach the model.) Both modes send identical prompts and parse identical schema-validated JSON — only the delivery differs.
354
356
 
355
357
  ## Configuration
356
358
 
@@ -366,7 +368,7 @@ CLAUDE_CODE_MAX_RETRIES=2 # retry attempts on parse/validation failu
366
368
  CLAUDE_CODE_MAX_CONCURRENCY=10 # max concurrent `claude` processes per run
367
369
  ```
368
370
 
369
- Every command that uses Claude (`analyze` with LLM rules, `spec scan`, `spec resolve`, `contracts generate`) runs a quick up-front preflight: it makes one tiny `claude` call to confirm the CLI is installed and logged in, and aborts with the CLI's own error message if not — so an expired login is caught immediately instead of failing every extraction subprocess at the end of a long run. `CLAUDE_CODE_BINARY` is the canonical way to point at a non-default binary; `CLAUDE_CODE_BIN` is honored as a legacy alias.
371
+ Every command that uses Claude (`analyze` with LLM rules, `spec scan`, `guard generate`) runs a quick up-front preflight: it makes one tiny `claude` call to confirm the CLI is installed and logged in, and aborts with the CLI's own error message if not — so an expired login is caught immediately instead of failing every extraction subprocess at the end of a long run. `CLAUDE_CODE_BINARY` is the canonical way to point at a non-default binary; `CLAUDE_CODE_BIN` is honored as a legacy alias.
370
372
 
371
373
  **`CLAUDE_CODE_MAX_CONCURRENCY`** caps how many Claude CLI processes TrueCourse spawns in parallel during a single run. Default `10`. Raise it on CI runners with spare headroom; lower it on resource-constrained machines (e.g. 8 GB laptops, shared VMs) to avoid OOM on large repos. Must be a positive integer.
372
374
 
@@ -376,6 +378,24 @@ For a one-off override, prefix the command:
376
378
  CLAUDE_CODE_MAX_CONCURRENCY=2 truecourse analyze
377
379
  ```
378
380
 
381
+ ### Per-stage model selection
382
+
383
+ Each LLM-powered pipeline stage resolves its model independently, so you can run cheap stages on Haiku and reserve Opus for scenario generation. Resolution precedence: `TRUECOURSE_MODEL_<STAGE>` (per-stage) › `TRUECOURSE_MODEL` (global) › `.truecourse/config.json` (`llm.stages.<id>`) › the built-in default. `truecourse config llm` prints the effective model + source for every stage.
384
+
385
+ | stage | env override | default |
386
+ |---|---|---|
387
+ | doc relevance keep/drop | `TRUECOURSE_MODEL_SPEC_RELEVANCE` | haiku |
388
+ | area tagging | `TRUECOURSE_MODEL_SPEC_AREA_TAG` | sonnet |
389
+ | overlap flagging | `TRUECOURSE_MODEL_SPEC_OVERLAP` | haiku |
390
+ | relation detection | `TRUECOURSE_MODEL_SPEC_RELATION` | sonnet |
391
+ | guard section classify/extract | `TRUECOURSE_MODEL_GUARD_EXTRACT` | sonnet |
392
+ | guard scenario generate | `TRUECOURSE_MODEL_GUARD_GENERATE` | opus |
393
+ | guard recipe derivation | `TRUECOURSE_MODEL_GUARD_RECIPE` | sonnet |
394
+
395
+ (The discontinued contract pipeline's stages — `TRUECOURSE_MODEL_CONTRACT_ENUMERATE`, `TRUECOURSE_MODEL_CONTRACT_EXTRACT`, `TRUECOURSE_MODEL_CONTRACT_REPAIR` — still resolve the same way.)
396
+
397
+ `TRUECOURSE_FALLBACK_MODEL` sets the `--fallback-model` used when the primary is overloaded. `TRUECOURSE_MAX_CONCURRENCY` caps concurrent LLM calls across every stage (default `min(cpus, 4)`). `TRUECOURSE_LLM_TIMEOUT_SCALE` multiplies every stage's per-call timeout by a float (default `1`); a slow model or proxy that trips the built-in ceilings can widen them all with one knob — e.g. `TRUECOURSE_LLM_TIMEOUT_SCALE=3` for a slow proxy. `TRUECOURSE_LLM_LOG` / `TRUECOURSE_LLM_DUMP` enable per-call logging.
398
+
379
399
  ### Excluding files from analysis
380
400
 
381
401
  TrueCourse honors `.gitignore` automatically (including nested `.gitignore` files, `.git/info/exclude`, and your configured global excludes file).