refactor!: converge every package on the tutorial contract; numbered tutorial replaces the doc set - #1
Open
marcosluizfp wants to merge 36 commits into
Open
refactor!: converge every package on the tutorial contract; numbered tutorial replaces the doc set#1marcosluizfp wants to merge 36 commits into
marcosluizfp wants to merge 36 commits into
Conversation
Enumerates all 265 symbols exported from the index.ts of core, mastra, models, eval, server and vercel, and classifies each as public / internal / delete based on an import-aware scan of every consumer (examples, skills, scripts, governance, looprun-bench, agentspec, yntelli) plus the two shipped CLI bins and the agentspec guard catalog. Result: 77 public, 35 internal, 153 with no consumer outside the defining package. Re-verifies the 16 pre-seeded zero-usage claims (all confirmed) and records six spot-checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cope Review round 1 fixes: - core.validateSpec internal -> public. mastra's barrel ends with 'export * from @looprun-ai/core', so yntelli reaches a CORE symbol through a MASTRA specifier and the import scan dropped it. Added a pass-C closure check resolving every consumer binding against the owner of the name: validateSpec is the only such case in the corpus. - eval.agentForCase and eval.stripGovernance delete -> public, reached by agentspec's synth-fork.mjs through a computed importFromCwd(). - core.renderTurnPrompt internal -> public, same mechanism in synth-fork.mjs and extract-fork.mjs. Found in re-verification. - models.localModel and its three companion types public -> internal: the earlier promotion rested on README evidence, and verdicts are usage-based. The docs/exports conflict is recorded for Task 12. Corrections without verdict impact: fixed the false claim that foldRow, isSingleClause and DEFAULT_POLARITY_LEXICON are unreferenced (all three are used inside coherence.ts; only findUnassessableLines is genuinely dead); defined what the same-package column measures and warned that a dash never authorizes erasing an implementation; widened the doc sweep to READMEs, GUARDS.md, governance/ and CHANGELOGs, attaching Task 12 notes instead of promoting on doc mentions; disclosed the 20 non-barrel src exports and the 28 ./testing symbols as out of scope; flagged the stale vendored guard-catalog snapshots and the three consumer files that import symbols which do not exist. Totals: 77 public / 37 internal / 151 delete (was 77 / 35 / 153). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
packages/looprun/bin/looprun.mjs is a published bin (package.json "bin" + "files": ["dist","bin"]) and reaches resolveAlias (lines 42, 66, 88, 102) and LlamaCppRuntime (lines 68, 94, 103) by dynamic import — the same mechanism that promoted 11 eval symbols via looprun-eval.mjs and localModelStatus via run-canary.mjs. Round 1 promoted the eval side and left the models side internal, which was inconsistent. Both flip internal -> public. §3 now states the rule explicitly rather than leaving it implicit in the per-file table, so it cannot be applied unevenly again: a symbol a published bin calls is user-facing regardless of which package owns the bin. models: 2/6/16 -> 4/4/16. Totals: 77/37/151 -> 79/35/151. Updated the §1 chart and bar chart, the §7.3 heading, findings 1 and 4 in §6, and the §9 revision log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Places every inventory-public symbol in exactly one of the six tutorial chapters. The union of the per-chapter lists is the target public API that tasks 3-7 converge index.ts onto. Resolves the localModel conflict the inventory flagged (finding 4): chapter 06 teaches localModel, so it and its three companion types flip back to public — on tutorial-contract grounds, not on a doc mention. Inventory totals 79/35/151 -> 83/31/151, recorded as revision round 3. No symbol was orphaned, so no "no tutorial home" downgrades.
Round 3 passed a mechanical 83-vs-83 completeness diff while three chapters were unwritable from the surface they claimed. Fixes, each verified against the named source line: - drop "bring your own loop" (06.4): closing that loop needs recordToolResult, resultOk and isTerminal, all internal, so a taught version would leave the ledger empty and every history-keyed guard silently never firing. Its ten symbols demote to @looprun-ai/core/internal, reason "no tutorial home" - promote the types the reader must author but could not name: Dim, Hook, ToolTarget, AgentScope, TerminalPolicy (core spec/guards), TurnInput, RunResult, TurnRecord, RuntimeDeps (runSpecConversation), StateView, and the eval subject-directory types (Subject, SubjectCase, CaseTurn, CaseInvariants, ReqCall, RubricItem) - state that the scheduler artifacts are authored in docs/tutorial/snippets/ by tasks 8-9; examples/calendar is a seed with no code - correct worldFromTools (native-tools mode, exec throws); 03 teaches hand-writing AgentWorld as the certified path - rescope 06 to server + local models; decoding trio moves to 05 - add per-chapter import specifiers; vercel excluded as a non-functional stub - GUARD_CATALOG ships on /internal, not the public barrel (plan amendment) Two rules now decide types (annotation rule, taught-field rule) so the promotion set is bounded and reproducible. Inventory totals 83/31/151 -> 89/38/138, recorded as revision round 4.
Three places still read as certifying symbols that round 4 demoted: §3's published-bin box and the §5 spot-check rows for redriveMessage and renderTurnPrompt. Each now carries a "→ internal in round 4, seam preserved on /internal" note. The evidence itself was correct and is unchanged; only its standing is annotated. No verdict and no count changed. Also: Layer's union is 'minimal' | 'base' | 'full' | 'agent' (was enumerated with three members in two places); the outline's exclusion row now separates addGuard opts from AgentSpecConfig fields and lists MutatorBinding; and decision 4 records that GUARD_CATALOG has no inventory row, so it is the only place Task 4's reviewer can check exports against.
…ooprun-ai/core/internal `packages/core/src/index.ts` now exports exactly the 51 core symbols the tutorial contract claims (docs/tutorial/00-outline.md §4, chapters 03/04/05) — 97 runtime values + their types down to 51 total. Everything with an `internal` verdict in the symbol inventory (§7.1, 37 symbols) moves to the new `@looprun-ai/core/internal` subpath: the guard-catalog tables, spec binding resolution, the trunk renderer, model settings, and the whole governed-turn seam (ledger + terminal protocol + prompt renderer + turn machine). That subpath carries no compatibility promise. `delete`-verdict symbols only leave the barrel — no implementation is erased; the inventory's §2 rule holds (every one of them still has an internal or test caller). In-repo consumers (mastra, eval) and the core/mastra tests now import internals from the subpath or from the owning module file; no test was weakened or dropped. Verified: `pnpm -r build` (7/7) · `pnpm test` (773 tests, 0 failures) · `pnpm typecheck` (8/8, incl. examples/hermes-sim). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xecutionError, surface lock Four review findings on the Task 3 cut. 1. TYPE-CLOSURE RIDER. Cutting the barrel to the taught 51 made public signatures uncompilable downstream: a consumer building with `declaration: true` hits TS4023/TS2742 the moment it writes `export const w = validateSpec(spec)` or subclasses AgentSpecBase, because SpecWarning / AgentControls / ChainSpec / StateDirective / GuardBinding / MutatorBinding / Layer / SpatialEdge / ReplyMutator / SamplingSettings / TokenUsage are named by no entry point. Both barrels now export the transitive type closure of their own signatures as type-only riders (11 on `.`, 8 on `/internal`), derived with the TS compiler API rather than hand-picked. Riders are not taught and are not counted in the 89 — recorded as outline §7 and inventory §9 round 5. New proof `test/proofs/declaration-emit.test.ts` compiles two real consumer modules from a temp dir whose node_modules/@looprun-ai/core symlinks to this package, so resolution goes through the real `exports` map — which also makes it the first test covering the `./internal` condition. It carries a self-test that TS4023/TS2742 are in fact reported. 2. The src/index.ts header claimed everything cut was "still available from /internal". False: ~58 former exports (renderTrunkBlocks, chainOrder, resolveBindings, TERMINAL_TOOLS, the coherence set, …) are on neither barrel. Reworded to name all three destinations, module-local included, and to say Tasks 5/6 own their fate. 3. GuardExecutionError is thrown across the package boundary at guard authors but was reachable from no entry point, so `e instanceof GuardExecutionError` was unwritable downstream. Exported from /internal; whether chapter 04 teaches it (promoting it public) is deferred to Task 10 as outline §6 decision 8. 4. SURFACE LOCK. Tasks 4-7 all edit this barrel and nothing failed if it drifted. `test/proofs/surface-lock.test.ts` asserts the exact export-name sets of both entry points against three hardcoded arrays (51 taught by chapter, 11 riders, 46 internal), via the TS compiler API over src so types count and no build is needed. A deliberate surface change must edit the arrays. Verified: pnpm -r build 7/7 · pnpm typecheck 8/8 · pnpm test 781 passed, 0 failed (was 773; +8 from the two new proofs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…OG data The 1427-line guards.ts becomes packages/core/src/guards/, one file per tutorial category — flow, args, world, confirmation, honesty, reply, custom — plus shared.ts (module-local helpers), catalog.ts (the vocabulary as data) and index.ts (the single import site). Every factory body moves verbatim: the only edits inside the moved code are the `export` prefix on the shared helpers. catalog.ts adds GUARD_CATALOG: one GuardCatalogEntry per exported factory (summary / whenToUse / example), the data Task 10's chapter-04 generator reads. It ships from @looprun-ai/core/internal, not the public barrel (outline §6, decision 4) — documentation infrastructure, not authoring vocabulary. The three kind-classification registries (DENY_ONLY_PROSE_KINDS, CONFIRM_CLASS_KINDS, ARMED_SEAMS) move there with it and keep their /internal export. guard-catalog-parity now scans the directory and gains a second lane: every exported factory has exactly one catalog entry and vice versa, every example calls its own factory, every entry sits in the file that exports it. The surface lock's INTERNAL array grows by the two new seam symbols; TAUGHT and RIDERS are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…arden the gates Review follow-up on 08fecd0. - GuardCatalogEntry gains `hook` ('preTool' | 'postTool' | 'onReply' | 'onReplyMutate'), set per entry from the factory's real installation phase via spec.ts#DIM_HOOKS. `category` stays file-derived; the two axes disagree exactly where it matters (noInstructionFromData lives in reply.ts but gates a call). Task 10's chapter groups by phase, as the agentspec reference does. - guard-catalog-parity gains two lanes: the hook must be one of the four, and the tricky rows are spot-asserted (noInstructionFromData=preTool, jargonScrub=onReplyMutate, resultInvariant=postTool, pendingConfirmMustAsk=onReply). - Gate hardening: both source-scanning tests drop the `f !== 'shared.ts'` filename skip. They already discriminate by return type, and the skip would let a Guard-returning factory dropped into shared.ts escape the ratchet. - whenToUse rewritten for confirmFirst, noFalseFailureClaim and degenerationGuard — the discriminating situation first, configuration second. confirmFirst's example is now the shipping string overload confirmFirst('confirmed'), and its summary records the construction-time throw on confirmFirst('prior-ask'). - Outline amended: chapter 04 is 30 catalog rows + canonArgs taught in prose (it returns a string, not a Guard — §3 and §6 decision 2); §6 decision 4 records the hook field. The §4 row and every symbol count are unchanged. - Stale path comments: test/proofs/catalog.ts, ratchet.test.ts; ledger.ts now imports canonArgs via ./guards/index.js like spec.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ule-local `coherence.ts` (428 lines, 33 exports) shipped two things: the trunk's attributed table + fold, which `renderScopedSpecTrunk` needs, and the coherence QUERIES (contradiction / duplication / single-owner / lint censuses), which nothing needs. Task 3 removed every one of them from the barrel; this removes the implementations whose only remaining callers were each other and the proof cases written for them. - new `src/trunk-fold.ts` (184 lines, module-local — reachable from no entry point): the table types, `GUARD_KIND_SUBJECT`, `derivePolarity`, `deriveSubject`, `foldTrunk`. `foldRow` is now private to it. - erased: findContradictions, findDuplications, findMultiOwnerSubjects, findSubjectlessLines, findUnassessableLines, isSingleClause, mutatorLines, trunkLines, withPolarityLexicon, DEFAULT_POLARITY_LEXICON, groupBySubject and the NormativeLine / MutatorBindingLike / PolarityLexicon / *Finding types. The polarity lexicon was injectable for the census alone, so `derivePolarity(text)` loses its second parameter; the `jargonScrub` subject entry went with `mutatorLines` (a mutator has no prose and never reaches `deriveSubject`). - `trunk-provenance.test.ts`: the cases whose subjects were erased are deleted with them (15 census cases); everything about the render → table → fold mechanism is kept, including the byte-identity invariant. `trunkLines` survives as a three-line test-local helper. Public and `/internal` export sets are unchanged (surface-lock green). Guard behavior is untouched: proofs 495/495, coverage still 29/29 kinds. Proof record: governance/proofs/2026-07-29-trunk-fold-coherence-cut.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
… edit, close the record gap Review follow-up on 55b8ac5. Its message says "15 census cases" and the first cut of the record said 14; both double-counted. MEASURED (vitest, and grep -c on the file at 4b59968): trunk-provenance.test.ts goes 36 cases -> 24, i.e. 12 deleted outright query (a) 4 · query (b) 2 · query (c) 2 · B4 2 of 3 · I7 2 of 3 2 rewritten the mutator adds no bytes · the polarity markers are English-only 1 assertion dropped findSubjectlessLines, inside a surviving case The by-describe axis (4/2/2/2/2) and the by-symbol axis (findMultiOwnerSubjects 3, mutatorLines 2, …) count the same 12 cases; they differ only because the mutator-as-owner case calls both symbols. Report and record now print both. (55b8ac5's own message could not be amended — history rewriting is blocked in this environment — so this commit is the correction of record.) Also in the Task-5 record: - Disclose that GUARD_KIND_SUBJECT lost its `jargonScrub: 'term-substitution'` entry, with the inertness argument: the table is read only by deriveSubject <- trunk.ts#line() <- rendering a guard's prose(); a ReplyMutator has no prose(), so no TrunkLine can carry that kind. - Fix the malformed table (header/separator inverted). - Fix the arbiter claim: foldTrunk(renderTrunkBlocks(…)) === renderScopedSpecTrunk(…) is INTRA-COMMIT self-consistency; the repo pins no cross-commit trunk snapshot. Cross-commit identity is cited as REVIEW evidence — sha256 f695126…, 6999 bytes, identical at 4b59968 and 55b8ac5 over a three-trunk fixture incl. installed jargonScrub — not as an in-repo pin. Governance gap: `check-record-required --base main` was passing VACUOUSLY, the Task-5 record silently covering the 24 governed paths of Tasks 3 and 4 while its body says no guard was touched (true of Task 5, false for the branch). Two retroactive records fill it, both generated by `pnpm proofs:record` with fresh tallies and both disclosing that they attest branch state, not an at-the-time run: - 2026-07-29-core-internal-subpath.md — Task 3 (5b5e30f, e6860ec) - 2026-07-29-guards-split-catalog.md — Task 4 (08fecd0, 4b59968) MATRIX regenerated (6 records). core/src/index.ts drops the planning-internal "Task 5" reference for timeless prose. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
Task 6 is a verification pass: Task 3 already moved the governed-turn machine to @looprun-ai/core/internal, so the public barrel already carries only the contract's result/record types and the backends already import the machinery from /internal. The one residual was half-open visibility. Seven symbols in runtime/*.ts carried `export` while no barrel, no sibling package and no test imported them — TERMINAL_TOOLS, TERMINAL_PROTOCOL, TERMINAL_PROTOCOL_REPLY_ONLY (terminal.ts), uploadDisplayLabels, isReplyOnly (prompt.ts), applyMutators, checkReply (turn.ts). Each has an in-file caller, so only the keyword goes; the implementations are byte-identical. RuntimeTurnInput (types.ts) is erased: a `= TurnInput` alias with zero references anywhere. RuntimeTurnRecord stays — /internal names it. recordVeto, VETO_STORM_LIMIT and shouldFireChain KEEP their exports: core's own tests import them by module path, so they are not dead. Proof record: governance/proofs/2026-07-29-runtime-dead-export-cut.md (PASS, 495/495, ratchet 29/29 kinds — unchanged, no guard touched). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
BREAKING CHANGE: `@looprun-ai/mastra` now exports only the 7 taught symbols of the tutorial contract (docs/tutorial/00-outline.md §4) plus the `@looprun-ai/core` re-export: LoopRunAgent, LoopRunAgentConfig, LoopRunOptions, worldFromTools, StateView, runSpecConversation, RuntimeDeps. Off the barrel (inventory §7.2 internal/delete verdicts, implementations kept and module-local — mastra has no /internal subpath): SessionStore, LoopRunSession, WorldFactory, compileSpec, CompiledSpec, buildWorldTools, buildTerminalTools, makeGuardHooks, makeInputProcessors, repeatedToolCallStop, GuardHooks, jsonSchemaToZodObject, jsonTypeToZod, surfaceFingerprint, DEFAULT_MAX_STEPS, DEFAULT_REDRIVES, LoopRunResultMeta. `createLoopRunAgent` is deleted (zero callers). - split agent.ts 551 → 448: construction (validation, world/surface resolution, tool build, drift gate, static instructions, Agent pass-through) moves verbatim to src/agent-construction.ts; agent.ts keeps the governed turn only. - add packages/mastra/test/surface-lock.test.ts — the barrel is data, not an emergent property (compiler-API mechanism copied from core's lock). - @looprun-ai/server declares the governed-turn meta shape it reads, since LoopRunResultMeta is internal to mastra and unreachable across the boundary. - governance record: governance/proofs/2026-07-29-mastra-facade-trim.md (PASS, 495/495 proofs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
…cord Review follow-up on 199c012. - packages/server/test/meta-mirror.test.ts: restores the compile-time tripwire the duplicated meta shape removed. Type-only deep import of mastra's declaration + MUTUAL assignability, enforced by `tsc --noEmit` (the build config excludes test/, so it never ships). Verified: renaming a field in mastra fails the server typecheck in 3 places. - reciprocal MIRROR comments above both declarations, each naming the other file and the pin. - src/index.ts docstring: the lock is packages/mastra/test/surface-lock.test.ts, not test/proofs/ (it is deliberately outside the guard-proof lane). - governance record: both construction reorders now disclosed with the inertness argument — makeGuardHooks (pure closure factory) and new SessionStore (typeof test + field assignments). Body-only edit; `pnpm proofs:matrix` regenerates MATRIX.md byte-identical, so no frontmatter/matrix change was needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
…act + surface locks The last three package barrels that still exported more than the Task-2 contract now export exactly the outline §4 rows for their package, plus the type-closure riders that make those signatures nameable downstream: models 24 → 8 taught (+2 riders) eval 52 → 19 taught (+9) server 13 → 4 (+3) Every `delete`-verdict symbol (inventory §7.3/§7.4/§7.5) stops being exported and stays module-local — none of these packages has an `/internal` subpath — so in-package code and the packages' own tests import the module files directly. No implementation with a live caller was erased; the one exception, `models.localModelClient`, had zero references in this repo and in looprun-bench / agentspec / yntelli, and lived inside the barrel file. The published bins keep their contract: `looprun-eval` reaches 11 eval symbols and `bin/looprun.mjs` reaches 3 models symbols through a dynamic PACKAGE import, so those are public by that fact — each lock asserts them separately (eval's reads the bin's actual `api.<name>` accesses rather than a maintained list). `TurnEvent.meta` decision (left open by the Task 7 review): the server's `LoopRunResultMeta` mirror is exported as a rider rather than inlined structurally, so the mirror keeps exactly one name and `meta-mirror.test.ts` keeps pinning the type `TurnEvent` really uses. Recorded in the outline §7 and inventory §9 round 6. Three new surface locks (copying mastra's compiler-API mechanism) hold all of it; each was sabotage-verified to fail on drift. Rider sufficiency was checked by compiling a synthetic consumer with `declaration: true` against the built packages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
docs/tutorial/snippets is a private workspace package that depends on the published `looprun` facade (workspace:*), so every tutorial code block is compiled against exactly what an npm reader installs — never @looprun-ai/*. Ships the running example's shared modules (outline §2): the scheduler spec (AgentSpecBase subclass, scope, terminal, contract, a custom no-double-book gate, destructiveTools ⇒ auto confirmFirst on cancelEvent), its hand-written AgentWorld, its ToolDef surface, plus the seeded 02-hello-world snippet. Gates: `pnpm -r --if-present typecheck` picks up the package, and a two-test vitest smoke run keeps the world honest at runtime, not only at type level. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
… model id Dual-review fixes for the Task 8 snippet package. CRITICAL: the chapter-02 cut passed toolDefs:[listEvents] over the three-tool scheduler spec, so the rendered trunk advertised addEvent/cancelEvent rules and activeTools named unregistered tools. Adds scheduler/hello-spec.ts — one tool, plain config, no addGuard — and 02-hello-world.ts uses it. Also: terminal policy no longer contradicts a behavior bullet (the bullet is now about disambiguation, which is what askUser is for here); argFormat on addEvent.start/end plus world-side DATETIME_RE + past-date checks, so the lexicographic clash compare can no longer be fed 'next Tuesday'; model id is the repo's pinned google/gemini-3.1-flash-lite with its env-var noted; cancelEvent's description says the confirm lands in a LATER turn; 02 shows LoopRunOptions; ingestAttachment returns the url; REFERENCE_NOW + the datetime pattern live once in scheduler/contract.ts; outline's `agent.run` → `agent.generate`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
A world instance plus loopRun.sessionId is rejected by the mastra SessionStore
("pass a world FACTORY"), so 02-hello-world.ts threw before it ever reached the
model — introduced by the LoopRunOptions example in the previous commit.
Also: spec.ts's header described two guards while the constructor installs three
(and confirm-first is auto-installed, not written there); addEvent's start/end
schema now carries the same DATETIME_PATTERN the argFormat guards enforce, so
the model sees the constraint; the smoke test covers the world's backwards-window
and past-date rejections.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
01-concepts (code-free): the ungoverned-loop problem, the action/language split, the three nouns (spec = map, guards = safety kit, agent = GPS), and a full annotated turn showing a preTool veto returning as the tool result. 02-hello-world: teaches LoopRunAgent / LoopRunAgentConfig / LoopRunOptions only, on snippets/02-hello-world.ts verbatim. States the GOOGLE_GENERATIVE_AI_API_KEY requirement (the provider reads it, not looprun), why the model id is pinned, and why `world` is a FACTORY (an instance + a sessionId throws in the session store). Closes with the borrow list from outline §3, unexpanded. 03-agent-anatomy: all 13 taught symbols against the shared scheduler modules, plus a new snippets/03-agent-anatomy.ts for validateSpec, the two world-read casts and the native-tools StateView. Teaches the forced [k: string]: any index signature and its cost (a typo typechecks — proved in the CI-typechecked snippet), the nominal vs structural cast trade-off, and worldFromTools as the native-tools adapter whose exec() throws. States explicitly that the smoke test covers the world's half of the two-step cancel, not the confirmFirst guard. Every fenced TS block is verbatim from docs/tutorial/snippets/ or marked as a library signature / illustrative. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
…iew 03 -> 06
Accuracy (all verified against source before editing):
- 03: the confirmed-flag cross-check does NOT throw at construction.
assertDestructiveConfirmable has exactly one caller, run-conversation.ts:101
(the chapter-05 runner); LoopRunAgent's resolveConstruction never calls it.
Reworded to say the eval runner throws at run start and LoopRunAgent does
not check it today. No code changed.
- 03: system-prompt order redrawn from trunk.ts:293-337 — voice, scope, core
rules, flow, tool rules, governance, persona+behavior, languageClause LAST.
The old diagram put scope below core rules and languageClause in the head.
- 03: mcp.getTools() -> listTools(), plus the namespacing (serverName_toolName)
and deny-by-default surface intersection (agent-construction.ts:80-97).
Applied in the parked chapter-06 text, since the section moved.
- 01 + 03: the veto is the tagged governance envelope (turn.ts:33-73), not a
bare { success:false, error }. 01 now teaches the world-refusal vs
guard-correction distinction, which is the point of the discriminator.
- 02: hello world was not runnable as printed — added the copy-the-scheduler
step, type=module and npx tsx notes, and scoped "twenty lines" to the agent
file.
- 02: model equivalence downgraded to same family (the harness pins the
thinking-off variant); "wording constrained by machine checks" replaced —
the language layer is never gated, it is measured.
- 03: auto-install box scoped (lexicon.falseFailureClaimRe adds a sixth guard;
confirmMechanism selects arg vs prior-ask), and `mode` documented as a
required, near-vestigial field.
Contract amendment (controller ruling): worldFromTools + StateView move from
chapter 03 to chapter 06 — 03 13->11, 06 11->13, total still 89, no verdict
changed. Recorded in 00-outline.md §7 and symbol-inventory §9 round 7;
mcp-tools.md now absorbed by 06. Chapter 03 keeps a 3-line forward reference,
and the section text is parked (with the listTools/namespacing/refresh-is-
fire-and-forget fixes applied) for Task 11.
Also: 03 §7's index-signature essay condensed to a callout, class diagram
corrected, addGuard's elided `layer` field marked, scope's all-or-nothing
rendering stated, and the chapter's "every block is quoted" umbrella replaced
with the honest excerpt-vs-signature split.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
cp -r copies five files, not four, and the claim that chapter 03 writes all of them was wrong twice: hello-spec.ts is chapter 02's own, and spec.ts is 03's full three-tool spec that comes along unused. Replaced the sentence with a per-file table naming what each one is and whether this chapter uses it. Also tightened 03 §10's forward reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
The catalog half of docs/tutorial/04-guards.md is rendered by scripts/gen-guards-chapter.mjs from GUARD_CATALOG on the BUILT @looprun-ai/core/internal, grouped by `hook` (the enforcement phase) with `custom` in its own escape-hatch group. summary/whenToUse/example are emitted verbatim — a wording fix belongs in src/guards/catalog.ts. The hand-written half teaches the four vocabulary types (Guard, GuardCtx, ObservedCall, Dim), canonArgs as a helper in prose, cross-references addGuard to chapter 03, and closes with writing a custom guard. GuardExecutionError stays on /internal (outline §6 decision 8): §6 MENTIONS it as the diagnostic a throwing check produces, but does not teach its API — the intended reaction is to fix the guard, not catch the error. Drift gate: `node scripts/gen-guards-chapter.mjs --check` exits 1 on drift, and runs both at the end of the root `pnpm test` chain and as its own CI step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
…, routing table CRITICAL: maxCalls does NOT use canonArgs (flow.ts counts by tool name, args ignored). Four sites taught the opposite; §4 now contrasts the two — a rephrased retry escapes noDuplicateCall and still burns maxCalls budget. The "the catalog examples typecheck" claim is now a fact: the generator also emits docs/tutorial/snippets/04-guards-examples.generated.ts (all 30 examples, committed + drift-checked), which the snippets package typechecks in CI. Catalog prose (governed path, proof record 2026-07-29-guard-catalog-summaries-detaxonomized, 495/495 unchanged): risk-family prefixes stripped from six summaries; forbidThisTurn's scope corrected to the binding's lifetime; custom's hook caveat de-dangled. Chapter: symptom→guard routing table and the honesty cluster added to §4, section index in the intro, §3's five+conditional-sixth count fixed, addGuard convention-vs-rule and addReplyCheck payoff, onInput noted in §5's intro. Outline: Task 10 amendment recording the three departures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
Chapter 05 (running & eval) teaches its 27 outline symbols: runSpecConversation with TurnInput/RuntimeDeps/RunResult/TurnRecord, the decoding trio, the subject directory contract with its five authored case types, and the twelve looprun-eval entry points taught CLI-first. It absorbs docs/guides/eval-config.md and docs/guides/measured-loop.md (both die in Task 12). Chapter 06 (advanced) teaches its 13: the OpenAI-compatible server (4), the local-model seven, and worldFromTools/StateView moved here by the outline §7 amendment. It absorbs docs/guides/local-models.md, packages/server/README.md and docs/guides/mcp-tools.md. @looprun-ai/vercel gets one honest line: a reserved stub whose factory throws. New snippets: 05-running-and-eval.ts, 06-advanced.ts, and a real eval subject (scheduler-subject/) over the shared scheduler — norms/gen/evals/ask plus the judge prompt. Its smoke test runs a scripted governed conversation (the clash gate really vetoes) and asserts the five preflight lints are clean. The scheduler modules' relative imports now carry the .ts extension: the subject is imported by node through loadSubject, where a .js specifier does not resolve to a .ts file. worldFromTools/StateView moved out of the 03 snippet with their chapter; the mastra surface lock's comment labels follow. Terminal transcripts are labelled: help, lint (clean and failing) and the three seal commands are real runs on this repo; run/fold/cert need a live model and are marked as illustrative shapes.
CRITICAL: cloudValidationDeps assigned geminiFlashLiteThinkOff()'s modelParams straight onto the deps, replacing pinnedDecoding() and leaving temperature on the provider default — inside the section about pinning. It now spreads over pinnedDecoding(), the way provider.ts does, and §3 says the helper carries the thinking-off options only. The seal-void transcript's "on disk" hash was produced by a different tamper than the one the chapter prints. Re-ran the exact four-command sequence twice: both hashes now reproduce byte-identical (restore → VALID a2c96e1f…, re-append → VOID 6ab73afa…), and the caption discloses the node-bin invocation. Accuracy: the trunk-static gate is conditional on ≥2 declared presets (plus the <200-byte shared-head rule for multi-agent subjects); forbiddenToolCalls is evaluated over calls that REACHED the world, so a vetoed call is not a violation; rubric `critical` is an unenforced hint for the judge prompt; cases.jsonl is not gitignored by anything, so that is now a recommendation; lintSubject exempts the minimal: layer from GUARD-NEVER-TARGETED; the ungoverned strip also empties controls.exhaustionReply and keeps flow; the systemPrompt escape hatch and the seal --target model:rate:reps triple are stated. Chapter 06: the wire envelope carries five fields, NOT the observed ledger — only onTurn sees it; §1 gains a real curl transcript against a server started with a scripted model (no live model), and a test pins that envelope's field set on an ephemeral port; turn 0 renders on a never-refreshed StateView; the GPU line no longer implies CPU-only is impossible; two captions now admit elided doc comments and worldFromTools' signature shows its real default parameter. mcp.listTools() stands: Mastra v1 deprecated getTools() and renamed it.
§5.6 still carried the pre-fix vocabulary ("fails on the ATTEMPT"), contradicting
§4: forbiddenToolCalls fails on the call REACHING the world, and the arms differ
by how far the write travels before something stops it.
The pinnedDecoding row implied looprun drops a flat temperature. It does not —
runSpecConversation and LoopRunAgent both normalize flat call settings into
modelSettings before calling Mastra; the drop is Mastra's own generate(). Row
rescoped, and the snippet comment no longer cites provider.ts as the reason.
The /v1/models body is a list envelope, not the model object.
The six tutorial chapters now carry the whole story, so the nine documents
they absorbed are removed: overview, getting-started, illustrated-guide,
examples, references, and all four guides/.
Before deleting, the six sections a prior review flagged as having no chapter
home were adjudicated individually:
illustrated-guide 3.5 zero business strings DROP - library-internal CI
invariant; the practical
consequence is in ch03 §2
illustrated-guide 3.6 the debate gate DROP - duplicated in README
illustrated-guide 4 skill install + pipeline DROP - agentspec-skill
methodology, owned by that
repo; install is in README
illustrated-guide 4.4 the fail taxonomy CARRIED - 7 of 8 rows are
already ch05 §5.7; row 8
(near-tie action coin) was
genuinely absent and is now
added there
illustrated-guide 6 contributing DROP - CONTRIBUTING.md and
governance/ are authoritative
getting-started 2 agentspec pipeline DROP - strict subset of the
README section
00-outline.md is NOT deleted. It is the standing record of the public-API
contract and is cited by path from all six src/index.ts barrels and their
surface-lock tests, so it moves to docs/superpowers/specs/ alongside the
inventory and the design. Every live citation is rewritten; the dated plan
and the governance proofs keep the old path, as records of what was true
when they were written.
Link integrity: docs/benchmarks.md repointed at the chapters; zero dead
relative links across README, docs/** and packages/*/README.md.
README is now what it should have been: what looprun is, install, a runnable
hello world, the six-chapter table, certify, local models, packages,
benchmarks. The agentspec pipeline shrinks to a bullet (private-beta skill,
own repo) and the hand-written path points at chapter 03.
Stale symbols corrected against the post-refactor barrels:
compileSpec README packages table -> runSpecConversation,
worldFromTools (compileSpec
is internal to mastra)
createOpenAiHandler server README "API" -> reachable as
createModelServer(...).handler
(verified on ModelServer)
CaseDump eval README, ch05 -> "one case dump per line";
the type is module-local
getTools() agent.ts JSDoc, -> listTools(); getTools is
hermes-sim README deprecated in Mastra v1
(codemod v1/mcp-get-tools).
Chapter 06 already taught
listTools; these were the
last stale copies
core/internal seam vercel README -> names the subpath the backend
primitives actually live on,
and its lack of a promise
localModel stays headlined: the outline resolved it to public on
tutorial-contract grounds, so the docs and the barrel now agree.
Also corrects two comments that overstated what they pin: the
LoopRunResultMeta mirror does not catch an OPTIONAL field added to one side,
and canonArgs is keyed on by noDuplicateCall and pendingConfirmMustAsk
specifically, not by an unnamed set of "repetition kinds".
Governed-path edits here are comment-only; see the Task 12 proof record.
The changeset bumps all seven packages major (the config links them), with
the before/after barrel counts derived from the actual barrels on main
rather than from memory: core 107 named + 2 wildcards -> 51 + 11 riders,
mastra 25 + 1 -> 7, models 24 -> 8, eval 52 -> 19, server 13 -> 4.
The migration notes give external consumers the old-symbol -> new-location
table, per repo:
agentspec renderTurnPrompt -> core/internal; the eval symbols
synth-fork computes are untouched. Three reference-catalog
corrections are owed there: forbidThisTurn is a
binding-lifetime ban and not a repeat detector;
lint-guard-catalog.mjs still scans the deleted
packages/core/src/guards.ts and SKIPs silently rather than
failing; a stale vendored dist/guards.d.ts will mask it
looprun-bench runSpecConversation intact; nine runtime primitives move to
core/internal; the shim's TrunkTheme/EvalCase/EvalConfig
imports are phantom and never typechecked
yntelli no action - LoopRunAgent, validateSpec and worldFromTools
are all on the taught surface
TWO OPEN USER DECISIONS are carried verbatim out of the outline's §6 so
they survive its move, and are NOT decided here:
5 add looprun/eval + looprun/server facade subpaths, or keep the direct
package names the tutorial uses today?
6 @looprun-ai/vercel - ship the stub, implement it, or drop it?
Task 12 left both untouched. vercel still ships as a reserved stub.
The proof record covers the comment-only edits on packages/core/src and
packages/mastra/src. The claim is mechanical, not asserted: filtering the
governed diff for lines that do not start with a comment marker returns
nothing, so a behavior change is not expressible by it. Suite green anyway
at 495/495.
The bench and agentspec sections were written from the ledger's prose rather
than from the repos. Both are on disk; every claim below is now verified
against them.
MIGRATION NOTES, looprun-bench. The seam list was wrong in both directions.
The real seam file is
benchmarks/tau2-telecom/harness/shim/src/step-handler.ts, whose single
import block is the whole story:
removed (0 occurrences anywhere in the bench):
renderTurnPrompt, recordToolResult, isTerminal, terminalProtocol,
forcedTerminalPrompt
added (imported, and now /internal-only):
enforcePostTool, redriveMessage, renderScopedSpecTrunk, resolveGuards,
ReplyViolation
marked unchanged-public:
Guard, GuardCtx (taught by ch04), ObservedCall (shim/src/transcript.ts)
TrunkTheme/EvalConfig/EvalCase were misattributed to the shim, which is
clean. The "never existed" verdict holds - neither barrel has ever exported
them - but the real sites matter much more: TrunkTheme is 94 occurrences
across 40 files, essentially the entire Atlas v0.6.0 spec set, which is the
code behind the README's governed-96.5 headline. That spec set does not
typecheck against any published looprun and did not before this release
either; each theme.ts needs the type defined locally. Stated plainly in the
notes rather than buried, since a maintainer mid-migration is the right
person to learn it.
MIGRATION NOTES, agentspec. renderTurnPrompt has TWO call sites in two
scripts - synth-fork.mjs (:105 import, :178/:190 calls) and
extract-fork.mjs (:184 import, :210/:222 calls). The table pointed at one,
so a maintainer following it would fix half the problem. Both resolve the
package from the user's cwd at runtime, so neither fails at build time.
The lint-guard-catalog.mjs correction described a mechanism that does not
exist. It never scans repo sources: it resolves @looprun-ai/core from
node_modules and reads guards.d.ts next to the BUILT barrel. Post-split the
build emits dist/guards/index.d.ts, so the read throws and the catch prints
"engine not installed" - a false diagnosis that silently disables parity
checking. Rewritten around the actual mechanism, folding in the stale-dist
point, and prescribing the catch-message fix as part of it.
Also: the §7.1-not-discharged statement (recordVeto, VETO_STORM_LIMIT,
shouldFireChain remain test-only module exports by design, reachable from
no barrier) so an auditor does not read them as missed work.
SWEEP GAP. pnpm-workspace.yaml:4 - a live config file - still cited the
deleted docs/tutorial/00-outline.md. My first sweep missed it because the
grep filtered on --include=*.md,*.ts,*.mjs,*.json and never looked at YAML.
Re-run with no filter at all: only the two dated governance receipts remain,
which stay by design.
Also corrected: "all six src/index.ts barrels" cite the outline - it is five
(vercel has no taught surface); ch04's "four confusable clusters" heading
and "the four groups" body, which then present a fifth (honesty).
MINORS. Changeset now explains the looprun and vercel majors (the umbrella
re-exports core so the break reaches consumers through the facade; vercel
has no API change and rides the linked version group). README: seal added to
the eval verb list (verified as a real CLI verb), looprun/vercel subpath
named, core row scoped so the trunk renderer and turn machine are not
presented as public. The models-pull size string 3-21 GB -> 2.5-17 GB in all
five places, not one: ch06:202 is a verbatim --help transcript and had to
move with the CLI or stop matching it. Two RuntimeTurnInput test
doc-comments -> TurnInput. Outline's present-tense "Absorbs docs/guides/..."
-> past tense, since those files are now deleted.
Gate: build, 848 tests, typecheck, proofs 495/495, matrix in sync,
record-required OK, docs:guards idempotent, links 18/18 clean twice. No
governed path touched this round, so no new proof record is owed.
`tsc` only ever adds to `dist/` — it never removes an output whose source is gone. After the guards.ts split and the coherence cut, a workspace built across both layouts kept a pre-split `dist/guards.js` (80.8 kB) and `coherence.*` maps, and `npm pack --dry-run` in packages/core listed all of them: 111 files shipped where 106 exist. Every publishable package gets a `clean` script, the root gets a recursive one, and the release ritual's gate now cleans before it builds — the tarball is built from whatever sits in `dist/`, so the clean belongs in the gate, not in a maintainer's habits. Verified: clean + build → `npm pack --dry-run` lists no `guards.js` and no `coherence.*` at the dist root, and `dist/guards/` (the split) still ships. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
…cabulary of record GUARDS.md called itself "the guard reference (source of truth)" and carried a hand-maintained kind table headed "The 29 guard kinds" — while src/guards/ ships 30 — plus a "SIX RISK-FAMILY kinds" section built on the taxonomy this branch already removed from the catalog summaries. CONTRIBUTING step 3 pointed every new-guard PR at that table, so the drift was structural, not accidental. Ruling applied: demote it. The sections that exist nowhere else stay verbatim — the GuardCtx firewall and purity law, hook semantics, the prose-rendering and prose≠reason laws with the parity proof, what AgentSpecBase auto-installs, the reader-of-record traps, controls, P8a, the pair doctrine, behavior[]. The kind list is replaced by a pointer to docs/tutorial/04-guards.md §5 (generated from GUARD_CATALOG, examples compiled in CI) and to catalog.ts itself. The risk-family section is gone. Consequences: the parity test drops its markdown lane — it gated names in a file that no longer lists any, and the GUARD_CATALOG lane is strictly stronger (an entry must carry a summary, a when-to-use, an example that calls its own factory, and the right category file). CONTRIBUTING step 3, the PR template and the governance skill now say "add the GUARD_CATALOG entry, then pnpm docs:guards"; they also stop pointing at skills/agentspec/references/guard-catalog.md, a path this repo does not have. Also: catalog.ts backticks its identifier cross-references in summary/whenToUse, so chapter 04's generated region stops reading as a second author beside its own backticked prose; and chapter 04 §1 states the factory → Guard → kind mapping once, since the audit trail shows kinds and the catalog is indexed by factory. Proof record: governance/proofs/2026-07-29-guards-md-demoted-to-internals.md (text-only on two governed paths; proofs 495/495 unchanged). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
Chapter 02 sets the reader up OUTSIDE the repo — they clone looprun, copy ./scheduler and ./hello.ts out of it, and work there. Every command in chapter 05 was then repo-relative (docs/tutorial/snippets/scheduler-subject), so nothing in the chapter ran where the reader stood. §4 now copies the subject out the same way 02 copies the modules, and says why the two directories must sit side by side (norms/index.ts imports ../../scheduler rather than re-declaring the spec). Every command switches to ./scheduler-subject and ./scheduler. The three transcripts marked **real** were re-run from that layout, not merely re-pathed: lint (clean, exit 0), the CASE-WITHOUT-TARGET finding (exit 1), and the four-command seal sequence. Both seal hashes come back byte-identical — mintSeal hashes subject-RELATIVE paths, so the layout cannot move them — and the caption now says so. The one **illustrative shape** stays labelled as such. Also: 05 §1 tells the reader the §2 smoke test needs `npm i -D @looprun-ai/mastra`, since scriptedModel lives on @looprun-ai/mastra/testing and on no looprun/* subpath; 02 §1 mentions `npx looprun init` as the optional environment check the root README already advertises; both chapters drop the "outline decision 5" citations, which pointed the reader at an internal planning artifact, and state the fact plainly instead; 06 surfaces examples/hermes-sim and ends with a short "where next". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
Four packages published with no README at all — looprun, @looprun-ai/core, @looprun-ai/mastra, @looprun-ai/models — so their npm pages showed only the manifest description. Each gets a short one: what it is, the install line, what is on the barrel, and a link to the tutorial. The link is an absolute GitHub URL because npm pages cannot resolve repo-relative paths; the three existing READMEs (server, eval, vercel) get the same line. `files` needed no change — npm always ships README, verified with npm pack --dry-run. Corrections: - the changeset claimed "each barrel is now pinned by a surface-lock test, so it cannot drift back". Not literally true: the published ./testing subpaths of core and mastra re-export by wildcard (~27 symbols) and carry no lock. The claim now names that gap as a follow-up. - packages/mastra/test/surface-lock.test.ts said its core lane resolved through the package specifier and therefore proved the built barrel — it reads core/src/index.ts by path and says nothing about dist. - migration-notes said TrunkTheme occurs in 40 files; it is 39. - the root README lost "a birth certificate, not vibes" and "first-class", which are marketing where a statement of what the artifact contains does the job, and gained a three-line note for returning readers whose old doc set was retired. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Breaking major that executes the approved simplification design (
docs/superpowers/specs/2026-07-28-looprun-simplification-design.md): the public API of every package converges onto what the numbered tutorial teaches, everything else moves to@looprun-ai/core/internal, becomes module-local, or is deleted with evidence.@looprun-ai/core/internal(46+2, locked)guards/by category +GUARD_CATALOGdata (30 entries,hookaxis)docs/tutorial/01–06— every TS block compiled in CI, chapter 04 generated fromGUARD_CATALOGwith a drift gate, terminal transcripts labelled real/illustrativeEvidence chain: symbol inventory (265 symbols, per-symbol verdicts) → tutorial outline as API contract → per-concept refactor with dual independent review per task → final whole-branch dual review. Ledgered in
docs/superpowers/specs/.Migration notes for external consumers (looprun-bench, agentspec skill, yntelli):
docs/superpowers/specs/2026-07-28-migration-notes.md— includes two OPEN decisions that need a maintainer call (addlooprun/eval/looprun/serverfacades? fate of the@looprun-ai/vercelstub?).Type of change
turn.ts, both bareexportremovals)Governance checklist
pnpm test:proofsgreen (495/495, coverage 29/29 kinds)verdict: PASS)governance/MATRIX.mdregenerated and in sync (11 records)GUARD_CATALOGcurrent +pnpm docs:guardsidempotent (drift gate in CI)node tests/no-bench-drift.test.mjs)Paste the generated matrix row
See
governance/MATRIX.md— 7 new rows on this branch (2026-07-29-*), all PASS; representative:| 2026-07-29 | trunk-fold-coherence-cut | core | refactor | PASS | 495/495 · 29/29 |
🤖 Generated with Claude Code
https://claude.ai/code/session_01PRBVNBAvFrTNLq2yfMU3u9