tldr-experts 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +2 -1
  3. package/dist/hooks/budget-gate.js +1 -1
  4. package/dist/hooks/{chunk-pf9d8ag8.js → chunk-6ytv36jd.js} +1 -1
  5. package/dist/hooks/{chunk-md1c7z7q.js → chunk-c4p5x1b4.js} +33 -16
  6. package/dist/hooks/session-start.js +12 -4
  7. package/dist/hooks/statusline.js +2 -2
  8. package/dist/tldrx.js +2438 -1359
  9. package/package.json +1 -1
  10. package/plugin/.claude-plugin/plugin.json +1 -1
  11. package/templates/experts/stack/dotnet.md +95 -0
  12. package/templates/experts/stack/javascript.md +86 -0
  13. package/templates/experts/stack/overlays/aspnet-controllers.md +47 -0
  14. package/templates/experts/stack/overlays/aspnet-minimal-apis.md +51 -0
  15. package/templates/experts/stack/overlays/efcore-npgsql.md +57 -0
  16. package/templates/experts/stack/overlays/expo-router.md +50 -0
  17. package/templates/experts/stack/overlays/fastapi.md +55 -0
  18. package/templates/experts/stack/overlays/mediatr-cqrs.md +47 -0
  19. package/templates/experts/stack/overlays/next-app-router.md +61 -0
  20. package/templates/experts/stack/overlays/node-express.md +55 -0
  21. package/templates/experts/stack/overlays/postgres-testcontainers.md +55 -0
  22. package/templates/experts/stack/overlays/prisma.md +52 -0
  23. package/templates/experts/stack/overlays/react.md +62 -0
  24. package/templates/experts/stack/overlays/sqlalchemy-alembic.md +51 -0
  25. package/templates/experts/stack/overlays/vite-react-spa.md +49 -0
  26. package/templates/experts/stack/python.md +90 -0
  27. package/templates/experts/stack/typescript.md +89 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # Changelog
2
2
 
3
+
4
+ ## 0.9.0 — 2026-09-06
5
+
6
+ ### Added
7
+
8
+ - **`AGENTS.md` is the canonical agent context, and `CLAUDE.md` imports it.** Four days of
9
+ live-maintenance tribal knowledge written down where every agent — Claude Code sessions in
10
+ fresh windows, Codex, anything reading `AGENTS.md` — starts with it: the golden evidence
11
+ rules (including "never quote a CLI flag from memory"), worktree+merge-wave discipline, the
12
+ five gates, the `gh run list --commit` lie and the non-empty-checks assertion, CHANGELOG
13
+ union and release ritual, the pinned house invariants, hermetic test rules, public-surface
14
+ guard rules, provider facts, and the known live traps. One file; the other imports it,
15
+ because two rule files drifting apart is the failure mode this repo exists to kill.
16
+ - **Stack packs — `tldrx expert packs <enable|disable|status>`.** A `<lang>-stack` expert was a
17
+ name-only stub: the language name was the only stack-specific token in its body, so the
18
+ developer built from the model's habits and the reviewer had no stack checklist to hold a
19
+ story against (the repo's own audit scored the experts' knowledge 6/10). Off by default,
20
+ behind one per-project switch: `enable` materialises four language packs (TypeScript,
21
+ JavaScript, Python, .NET) and thirteen manifest-detected framework overlays (`react`,
22
+ `next-app-router`, `vite-react-spa`, `expo-router`, `node-express`, `prisma`,
23
+ `aspnet-minimal-apis`, `aspnet-controllers`, `mediatr-cqrs`, `efcore-npgsql`, `fastapi`,
24
+ `sqlalchemy-alembic`, `postgres-testcontainers`) into `.tldrx/experts/<lang>-stack/`, kept apart
25
+ from `knowledge/`, which no packs command ever touches. A pack is interrogative by owner
26
+ decision, never prescriptive over a repo's own conventions: **Defaults** apply only where the
27
+ project is silent and each one names the signal that overrides it, and **Checks** are questions
28
+ the Build reviewer now asks under every diff, each carrying a `verify:` hint and yielding to
29
+ the project's own convention when it has one. `enable` replaces a body only when it is an
30
+ untouched stub — an edited body is `kept:` and the command says so — and a body one shipment
31
+ behind is `upgraded:` rather than silently frozen at the version it was first materialised
32
+ with, told apart by two additive front-matter keys the materialised body carries: `pack:
33
+ <lang>@<hash>` names the shipment and `pack_body:` names the body's own bytes, so "somebody
34
+ edited this" (bytes differ) and "this is one shipment behind" (bytes match, shipment differs)
35
+ are never the same answer. `disable` removes `overlays/` and nothing else. Overlays are
36
+ detected from manifests (`package.json`'s two dependency groups kept apart, `*.csproj` /
37
+ `Directory.Packages.props`, `pyproject.toml` and `requirements*.txt`) and never inferred from
38
+ the language, because two workspaces of the same language can run opposite architectures — one
39
+ prescriptive ".NET pack" would be wrong for whichever one it disagreed with. No new prompt
40
+ mechanism: every renderer already prints an expert's body, so `body + overlays` composes once
41
+ in `loadExpertBundles` and reaches every stage prompt and the Build developer unchanged; the
42
+ reviewer, which carried no expert content at all, is handed the active packs' Checks explicitly
43
+ under a new `## Stack checks (the repo's own conventions win)` section, rendered only when BOTH
44
+ the workspace switch and the story's own stage-level `stack_experts` are on — so a developer
45
+ turn that was never shown a stack expert is never graded against Checks it was never briefed
46
+ on. `workspace.yml` gains `stack_packs` and `repos[].overlays` (each with its evidence string)
47
+ — additive, `version: 1` unchanged, and read back on every re-init so a regenerate never
48
+ silently turns the switch off. For a workspace that never threw the switch, this is today's
49
+ bytes, byte for byte. Once `enable` has run, `disable` stops the overlays and the reviewer's
50
+ Checks, but a pack body already materialised into `expert.md` stays there until you delete
51
+ it — `disable` removes only `overlays/`, by design (fix round 2, ruled) — and `enable` re-seeds
52
+ a body it finds missing or still an untouched stub.
53
+ - **Project skills are named to the developer, independent of the packs switch.** A repo's own
54
+ `.claude/skills/*/SKILL.md` files are detected into `repos[].skills` and rendered under
55
+ `## Project skills` in every stage prompt and the Build developer's — name, description, and
56
+ the path to read — because skills are for doing and packs are for checking: the harness runs a
57
+ skill, the framework only says it exists. The Build developer's allowed tools gain `Skill` only
58
+ when that story's repo has one (whether an agent CLI's print mode actually denies an unlisted
59
+ `Skill` call is unverified — the framework's job is the list, not the enforcement). A skill git
60
+ does not track is flagged in the prompt and warned at Build start, by name and by repo, because
61
+ a story worktree carries tracked files only and the path the prompt names would not exist
62
+ there.
63
+ - **The context ledger separates `expert.md` bytes from overlay bytes.** `pending.json` gains
64
+ `overlays`/`overlay_bytes` on a stack expert's row — additive and absent (not `0` or `[]`) when
65
+ nothing was inlined, so a bundle the switch never touched stays byte-identical to one written
66
+ before packs existed — and a new `project_skills_bytes` beside the other context groups, a
67
+ required count like its siblings, `0` when the workspace detected no project skills rather than
68
+ absent. `expert_md_bytes` keeps the meaning it has always had, `expert.md`'s own bytes and never
69
+ the composed total, and the operator line prints the two counts separately for the same reason.
70
+ - Two out-of-scope findings from building this: stack-expert naming has two independent
71
+ derivations that agree today only because a mismatch fails silently (#152), and a project's own
72
+ `AGENTS.md`/`CLAUDE.md`/`CONTRIBUTING.md` are detected by presence only — their content never
73
+ reaches a stage prompt (#153).
74
+
3
75
  ## 0.8.0 — 2026-09-04
4
76
 
5
77
  ### Added
package/README.md CHANGED
@@ -288,7 +288,7 @@ draws before installing anything.
288
288
  The reference guide, in `docs/guide/`: [1 Quick start](docs/guide/01-quick-start.md) ·
289
289
  [2 The loop](docs/guide/02-the-loop.md) (the four steps, what a stage file controls, the two execution modes) ·
290
290
  [3 Runs and gates](docs/guide/03-runs-and-gates.md) (`run new`→`retro`, gate policy, `run auto`, unlock/cancel, dashboard, tickets) ·
291
- [4 Experts](docs/guide/04-experts.md) (loading rules, role experts, training, levels) ·
291
+ [4 Experts](docs/guide/04-experts.md) (loading rules, role experts, stack packs, training, levels) ·
292
292
  [5 Seeds and triage](docs/guide/05-seeds-and-triage.md) (`--seed`, `--from`, splitting a big seed) ·
293
293
  [6 Budgets and cost](docs/guide/06-budgets-and-cost.md) · [7 Claude Code](docs/guide/07-claude-code.md) (plugin, hooks, `/tldrx`) ·
294
294
  [8 CLI reference](docs/guide/08-cli-reference.md) (every command, flag and exit code) ·
@@ -314,6 +314,7 @@ back on the registry is 0.3.0.
314
314
 
315
315
  | Version | Date | Status | Contains |
316
316
  |---|---|---|---|
317
+ | 0.9.0 | 2026-09-06 | `beta` | opt-in stack packs: four language pack bodies and thirteen framework overlays detected from manifests, interrogative by default (Defaults yield to any repo signal, Checks are questions with `verify:` hints), behind one per-project switch (`tldrx expert packs enable`) that materialises into `.tldrx/experts/<lang>-stack/` without touching trained knowledge; the Build reviewer gets `## Stack checks (the repo's own conventions win)`; every stage names the project's `.claude/skills` and the developer may invoke them; workspace.yml records overlays with evidence and skills with a tracked flag, `version: 1` unchanged |
317
318
  | 0.8.0 | 2026-09-04 | `beta` | the unattended mandate learns to keep going: a `## Do not stop` section that defines the one thing allowed to end a run early (a strict blocker, named against the work it does not block), product questions parked as **guided** ones with lettered options and a pre-declared fallback, and a budget stop that asks instead of halting — written against 26 `budget.raised` and 26 `question.answered` events on a real ten-run workspace where the owner had to re-authorise "unattended" mid-run. Plus `tldrx drive --tldr`: a reporting contract for runs nobody will audit — the `run status` block plus three bullets of delta, no operator notes, minimal gate evidence, handoffs trimmed of prose but never of the citations `claim-sources` gates on. The ask channel stays the console and the framework names no chat vendor |
318
319
  | 0.7.0 | 2026-09-03 | `beta` | Codex as a second honest automated runner: recorded JSONL contract, structured envelopes, role-based sandboxes, token/session provenance, and explicitly unmetered USD accounting; Claude remains the default and the pilot harness. Also absorbs the citation-honesty work previously staged as 0.6.2: a `file` src resolves against the branches the run RECORDED and NAMES the unmerged ref instead of passing in silence or breaking with the temp dir (#140), watcher cards name it the same way (#143), and every run close reports the questions nobody answered (#141) |
319
320
  | 0.6.1 | 2026-09-03 | `beta` | dogfooding fixes from the first fully-unattended runs: blocked stories never lose uncommitted work (rescue commits + `story.work_rescued`), a fix is only Resolved with a reachable sha, re-entered stages reconstruct their handoff from the ledger instead of degrading it, the Cost header reports the phase and names its lower bound (shared `spendBasis`), feature presets resolve their per-repo maps, Plan sees the command allowlist its gate enforces, the landing sells the unattended flow |
@@ -29,7 +29,7 @@ import {
29
29
  validateRunBudget,
30
30
  wouldExceed,
31
31
  wouldExceedHostTokens
32
- } from "./chunk-md1c7z7q.js";
32
+ } from "./chunk-c4p5x1b4.js";
33
33
  import {
34
34
  EventLog
35
35
  } from "./chunk-b9rjx4qc.js";
@@ -7,7 +7,7 @@ import {
7
7
  nowRfc3339,
8
8
  openRunViews,
9
9
  validateRunBudget
10
- } from "./chunk-md1c7z7q.js";
10
+ } from "./chunk-c4p5x1b4.js";
11
11
  import {
12
12
  EventLog,
13
13
  asRunFile,
@@ -752,33 +752,50 @@ function pathsIntersect(a, b) {
752
752
  // src/core/experts/expertKnowledge.ts
753
753
  var DEFAULT_KNOWLEDGE_MAX_BYTES = 48 * 1024;
754
754
 
755
- // src/core/facilitator/dispatchNotes.ts
756
- var DISPATCH_NOTES_MAX_BYTES = 8 * 1024;
757
-
758
- // src/core/experts/stackExperts.ts
755
+ // src/core/experts/workspaceRepos.ts
759
756
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
760
757
  import { join as join5 } from "node:path";
761
- function stackExpertNames(root, repos) {
758
+ function readWorkspaceDocument(root) {
762
759
  const path = join5(root, PROJECT_FRAMEWORK_DIR, "workspace.yml");
763
760
  if (!existsSync4(path))
764
- return [];
765
- let doc;
761
+ return null;
766
762
  try {
767
- doc = parseYaml(readFileSync4(path, "utf8"));
763
+ return parseYaml(readFileSync4(path, "utf8"));
768
764
  } catch {
769
- return [];
765
+ return null;
770
766
  }
771
- const list = doc?.repos;
767
+ }
768
+ function workspaceRepoRows(doc) {
769
+ if (typeof doc !== "object" || doc === null)
770
+ return [];
771
+ const list = doc.repos;
772
772
  if (!Array.isArray(list))
773
773
  return [];
774
- const names = [];
774
+ const rows = [];
775
775
  for (const row of list) {
776
- if (typeof row?.name !== "string" || !repos.includes(row.name))
776
+ if (typeof row !== "object" || row === null)
777
777
  continue;
778
- const stack = Array.isArray(row.stack) ? row.stack : [];
779
- for (const language of stack) {
780
- if (typeof language !== "string" || language === "")
781
- continue;
778
+ const record = row;
779
+ if (typeof record.name !== "string")
780
+ continue;
781
+ rows.push(record);
782
+ }
783
+ return rows;
784
+ }
785
+ function strings(value) {
786
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
787
+ }
788
+
789
+ // src/core/facilitator/dispatchNotes.ts
790
+ var DISPATCH_NOTES_MAX_BYTES = 8 * 1024;
791
+
792
+ // src/core/experts/stackExperts.ts
793
+ function stackExpertNames(root, repos) {
794
+ const names = [];
795
+ for (const row of workspaceRepoRows(readWorkspaceDocument(root))) {
796
+ if (!repos.includes(row.name))
797
+ continue;
798
+ for (const language of strings(row.stack).filter((item) => item !== "")) {
782
799
  const expert = `${language}-stack`;
783
800
  if (!names.includes(expert))
784
801
  names.push(expert);
@@ -15,7 +15,7 @@ import {
15
15
  openQuestions,
16
16
  runSnapshot,
17
17
  whatIsWaiting
18
- } from "./chunk-pf9d8ag8.js";
18
+ } from "./chunk-6ytv36jd.js";
19
19
  import {
20
20
  evidencePath,
21
21
  expertsDir,
@@ -25,7 +25,7 @@ import {
25
25
  pathsIntersect,
26
26
  readExpertDomain,
27
27
  stackExpertNames
28
- } from "./chunk-md1c7z7q.js";
28
+ } from "./chunk-c4p5x1b4.js";
29
29
  import {
30
30
  parseEvidence
31
31
  } from "./chunk-b9rjx4qc.js";
@@ -925,11 +925,19 @@ function evidenceCount(expert) {
925
925
  // src/core/experts/roleExperts.ts
926
926
  import { join as join5 } from "node:path";
927
927
  var ROLE_TEMPLATES_DIR = join5(TEMPLATES_DIR, "experts");
928
+ // src/core/experts/packSections.ts
929
+ var PACK_MAX_BYTES = 24 * 1024;
928
930
  // src/core/experts/domainRank.ts
929
931
  var MAX_GRAPH_BYTES = 64 * 1024 * 1024;
932
+ // src/core/experts/packTemplates.ts
933
+ import { join as join6 } from "node:path";
934
+ var PACK_TEMPLATES_DIR = join6(TEMPLATES_DIR, "experts", "stack");
935
+ var OVERLAY_TEMPLATES_DIR = join6(PACK_TEMPLATES_DIR, "overlays");
936
+ var PACK_BODY_MAX_BYTES = 8 * 1024;
937
+ var OVERLAY_MAX_BYTES = 6 * 1024;
930
938
  // src/core/training/mineRuns.ts
931
939
  import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
932
- import { join as join6, relative as relative2 } from "node:path";
940
+ import { join as join7, relative as relative2 } from "node:path";
933
941
  var MAX_RUN_BYTES = 64 * 1024;
934
942
  var MINED_NAMES = ["handoff.md", "retro.md"];
935
943
  var MAX_DEPTH = 4;
@@ -953,7 +961,7 @@ function findMined(dir, depth) {
953
961
  for (const entry of [...entries].sort()) {
954
962
  if (entry.startsWith("."))
955
963
  continue;
956
- const abs = join6(dir, entry);
964
+ const abs = join7(dir, entry);
957
965
  let isDir = false;
958
966
  try {
959
967
  isDir = statSync4(abs).isDirectory();
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  bar,
4
4
  runSnapshot
5
- } from "./chunk-pf9d8ag8.js";
6
- import"./chunk-md1c7z7q.js";
5
+ } from "./chunk-6ytv36jd.js";
6
+ import"./chunk-c4p5x1b4.js";
7
7
  import"./chunk-b9rjx4qc.js";
8
8
  import"./chunk-rrkdfk7s.js";
9
9
  import"./chunk-5w5eg00k.js";