truecourse 0.6.10 → 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,24 +28,12 @@ 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: **[Install](#install)** · **[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
 
35
35
  ---
36
36
 
37
- # Install
38
-
39
- ```bash
40
- npm install -g truecourse
41
- ```
42
-
43
- This puts the `truecourse` command on your PATH — every example below uses it. Prefer not to install globally? Run any command one-off with `npx truecourse <command>` instead.
44
-
45
- See [Prerequisites](#prerequisites) for Node, Claude Code, and C# requirements.
46
-
47
- ---
48
-
49
37
  # 1. Analyze — code intelligence
50
38
 
51
39
  Static + LLM analysis of your code: architecture, security, bugs, performance, and more.
@@ -54,8 +42,8 @@ Static + LLM analysis of your code: architecture, security, bugs, performance, a
54
42
 
55
43
  ```bash
56
44
  cd <your-repo>
57
- truecourse analyze # Runs the full analysis in-process
58
- truecourse list # Show the violations it found
45
+ npx truecourse analyze # Runs the full analysis in-process
46
+ npx truecourse list # Show the violations it found
59
47
  ```
60
48
 
61
49
  The first analyze creates `.truecourse/` and stores results as plain JSON. View them visually with [`truecourse dashboard`](#dashboard-web-ui).
@@ -187,110 +175,110 @@ pre-commit:
187
175
 
188
176
  ---
189
177
 
190
- # 2. Spec → Verify — business-logic drift
178
+ # 2. Spec → Guard — business-logic drift
179
+
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 deterministic — no 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.
191
181
 
192
- TrueCourse builds a machine-readable spec from your docs and verifies the code against it catching 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.
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.
193
183
 
194
- > **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`.
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.
195
185
 
196
186
  ## Quick Start
197
187
 
198
188
  ```bash
199
189
  cd <your-repo>
200
- truecourse spec scan # Read docs → extract claims surface conflicts
201
- truecourse spec resolve --all-defaults # Accept the recommended pick on each conflict
202
- truecourse contracts generate # Canonical spec → .tc contract artifacts
203
- 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)
204
194
  ```
205
195
 
206
- 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.
207
197
 
208
- > 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.
209
199
 
210
200
  ## How it works
211
201
 
212
- Three stages run in order, each producing artifacts the next consumes:
202
+ Stages run in order, each producing committable artifacts the next consumes:
213
203
 
214
- **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).
215
205
 
216
- 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).
217
207
 
218
- **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).
219
209
 
220
- **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.
221
211
 
222
- **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.
223
213
 
224
214
  ## What it catches
225
215
 
226
- 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.
227
217
 
228
218
  ## Setup
229
219
 
230
- 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/`:
231
221
 
232
222
  ```
233
223
  .truecourse/
234
- ├── specs/ ← canonical spec (committable)
235
- │ ├── claims.json ← structured snapshot: modules + claims + provenance
236
- │ └── decisions.json ← user resolutions + version chains + manual includes
237
- ├── contracts/ ← generated TC contract artifacts (gitignored by default)
238
- └── _inferred/ reverse-engineered, undocumented decisions (`truecourse infer`)
239
- ├── verifier/ drift store (mirrors analyze; `truecourse verify`)
240
- ├── runs/ per-run drift snapshots (gitignored)
241
- ├── 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)
242
234
  │ ├── history.json ← per-run summaries (gitignored)
243
- └── diff.json current-vs-baseline drift diff (gitignored)
244
- └── .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)
245
238
  ```
246
239
 
247
- 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.
248
241
 
249
242
  ## Commands
250
243
 
251
244
  ```bash
252
- # Spec consolidation (docs → canonical spec)
253
- truecourse spec scan # Read docs, extract claims, surface conflicts, write claims.json
254
- truecourse spec resolve --all-defaults # Accept the engine's recommended pick on every open conflict
255
- truecourse spec status # Summary: docs, claims, modules, pending decisions
256
-
257
- # Conflict resolution (also available in the dashboard Spec tab)
258
- truecourse spec conflicts list # List open conflicts (add --decided / --all)
259
- truecourse spec conflicts show <id> # Full detail for one conflict
260
- truecourse spec conflicts pick <id> <index> # Resolve by picking a candidate
261
- truecourse spec conflicts custom <id> --text "…" # Resolve with a custom answer
262
- truecourse spec conflicts revoke <id> # Re-open a decided conflict
263
- truecourse spec chains add --older A --newer B # Manually mark a version chain (escape hatch)
264
- truecourse spec chains list / remove
265
- truecourse spec docs skipped # Docs the LLM relevance filter excluded
266
- truecourse spec docs include <path> # Force-include a skipped doc
267
- truecourse spec docs uninclude <path>
268
-
269
- # Contract extraction (canonical spec .tc artifacts)
270
- truecourse contracts generate # Extract / re-extract TC contract files
271
- truecourse contracts list # List artifacts (kind · identity · location)
272
- truecourse contracts list --inferred / --authored # Only reverse-engineered (_inferred/) / only authored
273
- truecourse contracts validate # Parse + resolve TC files; report unresolved refs
274
-
275
- # Verification (code against contracts)
276
- truecourse verify # Full run: stashes dirty tree (prompts), writes verifier/runs + LATEST + history
277
- truecourse verify --diff # Git diff: working-tree drifts vs committed baseline (added/resolved/unchanged)
278
- truecourse verify --stash / --no-stash # Pre-approve / skip stashing on a full run
279
- truecourse drifts list # List drifts from the latest verify (paginated; reads LATEST, no re-run)
280
- truecourse drifts list --all # Show every drift (no pagination)
281
- truecourse drifts list --offset 20 / --severity critical,high # Page through / filter by severity
282
-
283
- # Inference (code → inferred contracts) — reverse-engineer undocumented decisions
284
- truecourse infer # Write inferred .tc files to contracts/_inferred/
285
- truecourse infer --dry-run # Report what would be written, touch nothing
286
- 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)
287
271
  ```
288
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
+
289
277
  ---
290
278
 
291
279
  # Dashboard (web UI)
292
280
 
293
- 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.
294
282
 
295
283
  ```bash
296
284
  truecourse dashboard # Start + open the dashboard
@@ -302,7 +290,9 @@ truecourse dashboard uninstall # Remove the background service
302
290
  ```
303
291
 
304
292
  - **Code Analysis** — architecture graph, violations list, severity/category analytics, code hotspots, trend over time; toggle rules and silence noisy ones inline.
305
- - **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.)
306
296
 
307
297
  ---
308
298
 
@@ -342,27 +332,27 @@ The first `truecourse analyze` (or `truecourse add`) in a fresh repo asks whethe
342
332
 
343
333
  ## LLM transport (`--llm-transport`)
344
334
 
345
- 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`:
346
336
 
347
337
  | Mode | How it reaches the model | Needs |
348
338
  |---|---|---|
349
339
  | **`cli`** *(default)* | spawns `claude -p …` per call | the `claude` binary on PATH, signed in. No API key. |
350
340
  | **`agent`** | a **filesystem mailbox** under `--io <dir>` | nothing — no `claude` binary, no API key |
351
341
 
352
- 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**.
353
343
 
354
344
  ```bash
355
345
  # default: spawn the claude CLI
356
346
  truecourse analyze --llm
357
- truecourse contracts generate
347
+ truecourse guard generate
358
348
 
359
349
  # agent transport: the tool parks prompts in ./io and an external agent answers them
360
350
  truecourse analyze --llm --llm-transport agent --io ./io
361
- truecourse spec scan --llm-transport agent --io ./io
362
- 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
363
353
  ```
364
354
 
365
- 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.
366
356
 
367
357
  ## Configuration
368
358
 
@@ -378,7 +368,7 @@ CLAUDE_CODE_MAX_RETRIES=2 # retry attempts on parse/validation failu
378
368
  CLAUDE_CODE_MAX_CONCURRENCY=10 # max concurrent `claude` processes per run
379
369
  ```
380
370
 
381
- 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.
382
372
 
383
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.
384
374
 
@@ -388,6 +378,24 @@ For a one-off override, prefix the command:
388
378
  CLAUDE_CODE_MAX_CONCURRENCY=2 truecourse analyze
389
379
  ```
390
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
+
391
399
  ### Excluding files from analysis
392
400
 
393
401
  TrueCourse honors `.gitignore` automatically (including nested `.gitignore` files, `.git/info/exclude`, and your configured global excludes file).