agentfootprint 9.103.0 → 9.104.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/AGENTS.md +1 -1
- package/CHANGELOG.md +168 -88
- package/CLAUDE.md +3 -2
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/classify/index.js +20 -0
- package/dist/classify/index.js.map +1 -0
- package/dist/classify/mock.js +44 -0
- package/dist/classify/mock.js.map +1 -0
- package/dist/classify/types.js +47 -0
- package/dist/classify/types.js.map +1 -0
- package/dist/classify/typesafe.js +249 -0
- package/dist/classify/typesafe.js.map +1 -0
- package/dist/core/Agent.js +5 -0
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +14 -0
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/findings/judge.js +173 -0
- package/dist/core/agent/findings/judge.js.map +1 -0
- package/dist/core/agent/findings/ledger.js +50 -6
- package/dist/core/agent/findings/ledger.js.map +1 -1
- package/dist/core/agent/findings/types.js +8 -1
- package/dist/core/agent/findings/types.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +65 -0
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/doors/classify.js +52 -0
- package/dist/doors/classify.js.map +1 -0
- package/dist/doors/skill-graph.js +9 -1
- package/dist/doors/skill-graph.js.map +1 -1
- package/dist/esm/classify/index.d.ts +13 -0
- package/dist/esm/classify/index.js +14 -0
- package/dist/esm/classify/index.js.map +1 -0
- package/dist/esm/classify/mock.d.ts +26 -0
- package/dist/esm/classify/mock.js +40 -0
- package/dist/esm/classify/mock.js.map +1 -0
- package/dist/esm/classify/types.d.ts +111 -0
- package/dist/esm/classify/types.js +43 -0
- package/dist/esm/classify/types.js.map +1 -0
- package/dist/esm/classify/typesafe.d.ts +58 -0
- package/dist/esm/classify/typesafe.js +245 -0
- package/dist/esm/classify/typesafe.js.map +1 -0
- package/dist/esm/core/Agent.js +5 -0
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.js +14 -0
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/findings/judge.d.ts +84 -0
- package/dist/esm/core/agent/findings/judge.js +168 -0
- package/dist/esm/core/agent/findings/judge.js.map +1 -0
- package/dist/esm/core/agent/findings/ledger.d.ts +16 -6
- package/dist/esm/core/agent/findings/ledger.js +50 -6
- package/dist/esm/core/agent/findings/ledger.js.map +1 -1
- package/dist/esm/core/agent/findings/types.d.ts +72 -1
- package/dist/esm/core/agent/findings/types.js +7 -0
- package/dist/esm/core/agent/findings/types.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +15 -0
- package/dist/esm/core/agent/stages/toolCalls.js +42 -0
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +17 -0
- package/dist/esm/doors/classify.d.ts +35 -0
- package/dist/esm/doors/classify.js +36 -0
- package/dist/esm/doors/classify.js.map +1 -0
- package/dist/esm/doors/skill-graph.d.ts +2 -0
- package/dist/esm/doors/skill-graph.js +5 -0
- package/dist/esm/doors/skill-graph.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +50 -0
- package/dist/esm/events/registry.d.ts +5 -1
- package/dist/esm/events/registry.js +4 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/injection-engine/classifierScorer.d.ts +44 -0
- package/dist/esm/lib/injection-engine/classifierScorer.js +91 -0
- package/dist/esm/lib/injection-engine/classifierScorer.js.map +1 -0
- package/dist/esm/lib/injection-engine/index.d.ts +1 -0
- package/dist/esm/lib/injection-engine/index.js +1 -0
- package/dist/esm/lib/injection-engine/index.js.map +1 -1
- package/dist/events/registry.js +4 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/injection-engine/classifierScorer.js +95 -0
- package/dist/lib/injection-engine/classifierScorer.js.map +1 -0
- package/dist/lib/injection-engine/index.js +6 -2
- package/dist/lib/injection-engine/index.js.map +1 -1
- package/dist/types/classify/index.d.ts +14 -0
- package/dist/types/classify/index.d.ts.map +1 -0
- package/dist/types/classify/mock.d.ts +27 -0
- package/dist/types/classify/mock.d.ts.map +1 -0
- package/dist/types/classify/types.d.ts +112 -0
- package/dist/types/classify/types.d.ts.map +1 -0
- package/dist/types/classify/typesafe.d.ts +59 -0
- package/dist/types/classify/typesafe.d.ts.map +1 -0
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/findings/judge.d.ts +85 -0
- package/dist/types/core/agent/findings/judge.d.ts.map +1 -0
- package/dist/types/core/agent/findings/ledger.d.ts +16 -6
- package/dist/types/core/agent/findings/ledger.d.ts.map +1 -1
- package/dist/types/core/agent/findings/types.d.ts +72 -1
- package/dist/types/core/agent/findings/types.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +15 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +17 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/doors/classify.d.ts +36 -0
- package/dist/types/doors/classify.d.ts.map +1 -0
- package/dist/types/doors/skill-graph.d.ts +2 -0
- package/dist/types/doors/skill-graph.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +50 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +5 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/classifierScorer.d.ts +45 -0
- package/dist/types/lib/injection-engine/classifierScorer.d.ts.map +1 -0
- package/dist/types/lib/injection-engine/index.d.ts +1 -0
- package/dist/types/lib/injection-engine/index.d.ts.map +1 -1
- package/package.json +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,80 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [9.104.0] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Added — a calibrated judge beside the model's own standings
|
|
11
|
+
|
|
12
|
+
- **`agentfootprint/classify`** — a new door for a classifier that SCORES
|
|
13
|
+
declared candidates instead of generating text: the `Classifier` port
|
|
14
|
+
(`{ name; classify(request, signal?) }`), the request shapes (`choice` over
|
|
15
|
+
declared options, `noul` as a probability, `score` on a declared scale),
|
|
16
|
+
the answer shapes (the pick, its confidence and the WHOLE distribution),
|
|
17
|
+
`ClassifierError` (`status?`, `retryable`), and two adapters —
|
|
18
|
+
`typesafe()` (TypeSafe "System One", model `jev-latest`; key from
|
|
19
|
+
`TYPESAFE_API_KEY` or `apiKey`, refused at construction when missing; one
|
|
20
|
+
`fetch`, no SDK; exponential backoff on 429/529 up to `maxRetries`;
|
|
21
|
+
`latencyMs` measured around the whole call; the wire mapped field for
|
|
22
|
+
field, probabilities never renormalised, the key never in an error) and
|
|
23
|
+
`mockClassifier(script)` for tests and the bench (every request on
|
|
24
|
+
`calls`). Verified 2026-09-17 by one real call; the suite maps that probe
|
|
25
|
+
against a stubbed `fetch` and never calls the hosted classifier.
|
|
26
|
+
- **`.findings({ judge })`** — a SECOND SOURCE on the findings ledger. After
|
|
27
|
+
every tool result lands and before the next model call, the judge is
|
|
28
|
+
asked what the RESULT is worth for the proposition the model declared on
|
|
29
|
+
the call, or for the user's question when none was declared (never why
|
|
30
|
+
the tool was called), and its answer is filed as a `JudgmentRow` beside
|
|
31
|
+
the model's own `StandingRow`: `source: 'judge'`, the judge's name and
|
|
32
|
+
the provider's model string, `against: 'proposition' | 'question'`, the
|
|
33
|
+
standing, the distribution, the confidence, `testsSubject`, usage,
|
|
34
|
+
latency, `clipped` when the result was cut at 4000 characters, the
|
|
35
|
+
iteration. A failed call is a `JudgmentErrorRow` (status, the provider's
|
|
36
|
+
message, latency) — never a guessed standing — and the run continues.
|
|
37
|
+
`foldLedger` gains `judgments` (last wins) and leaves `standingOf` the
|
|
38
|
+
model's; nothing is served from a judgment in this release (policy A —
|
|
39
|
+
the design page names B–D and leaves them to the bench). The probe's
|
|
40
|
+
disagreement — the model said `ruled-out`, the judge said `noise` at
|
|
41
|
+
0.69 — is the first recorded fact of the second source. Root exports
|
|
42
|
+
`JudgmentRow` / `JudgmentErrorRow`; `agent.findings()` returns the rows
|
|
43
|
+
as before.
|
|
44
|
+
- **Events** `agentfootprint.findings.judged` (`toolCallId`, `toolName`,
|
|
45
|
+
`iteration`, `against`, `standing`, `confidence`, `latencyMs`,
|
|
46
|
+
`inputTokens?`, `outputTokens?`) and `agentfootprint.findings.judge_failed`
|
|
47
|
+
(`toolCallId`, `toolName`, `iteration`, `status?`, `latencyMs`) —
|
|
48
|
+
identities, enums and numbers, never the state; and
|
|
49
|
+
`agentfootprint.findings.standing` gains `agrees?: boolean`, the model's
|
|
50
|
+
standing against the judge's current judgment of the same result, present
|
|
51
|
+
exactly when a judgment row exists (the judge files before the model call
|
|
52
|
+
that declares, so the standing event is where both readings exist). 115
|
|
53
|
+
typed events across 25 domains.
|
|
54
|
+
- Only a result the tool PRODUCED is judged: a permission denial, a halt, a
|
|
55
|
+
fail-closed refusal or a declined check-in files no judgment row and
|
|
56
|
+
spends no classifier call — a denial text is not a tool result.
|
|
57
|
+
- `typesafe()` refuses a reply without the provider's `model` string or an
|
|
58
|
+
answer whose numeric field (`confidence`, `score`, `noul`) is missing or
|
|
59
|
+
not a finite number, as a `ClassifierError` — never `'unknown'` or `NaN`
|
|
60
|
+
on the record; a per-attempt `timeout` ends the call after one attempt,
|
|
61
|
+
not retryable.
|
|
62
|
+
- **`classifierScorer(classifier)`** on `agentfootprint/skill-graph` — an
|
|
63
|
+
`EntryScorer` whose ranking IS the provider's distribution (the scored
|
|
64
|
+
choice, design step 2): one `choice` question over the entry candidates
|
|
65
|
+
with `{ id: description }` as criteria, `score` and `relevance` both the
|
|
66
|
+
probability as sent (a candidate the provider did not score is absent
|
|
67
|
+
from the ranking, never a padded 0), `chosen` the provider's own pick,
|
|
68
|
+
`scorer: 'classifier:<model>'`. A provider failure returns `scorer:
|
|
69
|
+
'classifier:unavailable'` with an empty ranking and no `chosen`, and the
|
|
70
|
+
existing cold-start entry pick takes over — never a guess.
|
|
71
|
+
- **Bench** `bench/findings-shuffle.mjs`: `AF_SHUFFLE_JUDGE=mock|typesafe`
|
|
72
|
+
arms every armed condition with a judge and adds `judge-accuracy`,
|
|
73
|
+
`judge-agrees`, `judge-tokens` and `judge-latency-ms`, with the classifier
|
|
74
|
+
calls stated on the cost line before the first is made; the mock judge
|
|
75
|
+
scripts the planted truth at a fixed confidence and the smoke laws pin
|
|
76
|
+
it. Unset, the bench prints the 9.103.0 table byte for byte.
|
|
77
|
+
- Byte identity: the 17 references pass untouched; `agent-findings-judge`
|
|
78
|
+
is the one new reference, generated alone with a scripted judge — its
|
|
79
|
+
delta over `agent-findings` is the two judgment rows and the declared
|
|
80
|
+
proposition, and nothing under `served.*`.
|
|
81
|
+
|
|
8
82
|
## [9.103.0] - 2026-09-17
|
|
9
83
|
|
|
10
84
|
### Added — an answer-turn ask on the served ledger piece, as a dial
|
|
@@ -70,7 +144,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
70
144
|
the refusal engine saw a declared fact and a declared noise result as the
|
|
71
145
|
same bytes. By the model's claim only: a turn is the removal unit and its
|
|
72
146
|
standing is its most valuable result's (`fact > open > undeclared >
|
|
73
|
-
|
|
147
|
+
ruled-out > noise`); a result the model never named is undeclared and is not
|
|
74
148
|
held; the library reads no result's text to decide otherwise.
|
|
75
149
|
- A fact hold never exists without its ceiling and its stand-down. The
|
|
76
150
|
ceiling is spent newest first and a held turn already inside
|
|
@@ -138,7 +212,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
138
212
|
So from the second call on, the reserved `_findings` property on every
|
|
139
213
|
served schema binds the ids the model may name: `previous[].toolCallId`
|
|
140
214
|
carries `enum: <the tool results on the wire with no standing yet, newest
|
|
141
|
-
|
|
215
|
+
first, at most 32>` and says "one of the ids listed; a result not listed
|
|
142
216
|
cannot be named here"; a clipped list states the cap. The list is what the
|
|
143
217
|
model can still READ: a result with no standing, a `fact` (stood on in the
|
|
144
218
|
piece, served verbatim under the default mode) and an `open` result (served
|
|
@@ -161,7 +235,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
161
235
|
`receipt.tools.schemaHashes` moves when the offer does. The law holds at
|
|
162
236
|
the schema AND at the row: the offer is what the model may COPY, never what
|
|
163
237
|
the library resolves — an id outside it still files as written, `unknownId:
|
|
164
|
-
|
|
238
|
+
true`, never mapped to a position or a tool name — and every id INSIDE it
|
|
165
239
|
resolves, because a standing is identified against the same served history
|
|
166
240
|
the offer was read from (`findings/offer.ts · knownResults`: the served
|
|
167
241
|
`role: 'tool'` messages plus the previous batch), so an id copied from the
|
|
@@ -214,7 +288,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
214
288
|
|
|
215
289
|
- Since 9.101.0, `withoutFindingsArgument` recognised the library's
|
|
216
290
|
decoration by REFERENCE only (`properties._findings ===
|
|
217
|
-
|
|
291
|
+
FINDINGS_ARGUMENT_SCHEMA`), and the reference never holds on the live path:
|
|
218
292
|
the served list `callLLM` reads is the committed `dynamicToolSchemas`, a
|
|
219
293
|
`structuredClone` of what the slot planted. So the choice seam's enum fence
|
|
220
294
|
(`declaredEnumValuesOf(withoutFindingsArgument(schema))`, the
|
|
@@ -229,7 +303,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
229
303
|
written. An armed agent may therefore file an `unsupported-argument`
|
|
230
304
|
finding it previously suppressed. Its unit test had passed because it
|
|
231
305
|
handed the function the live reference; `test/core/agent/findings/
|
|
232
|
-
|
|
306
|
+
reserved.test.ts` now pins the clone.
|
|
233
307
|
|
|
234
308
|
### Unchanged — the unarmed agent, and the two armed references regenerated alone
|
|
235
309
|
|
|
@@ -326,7 +400,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
326
400
|
`open`. Bounded, every overflow stated, an empty bucket omitted. On the
|
|
327
401
|
wire, and only on the wire, a tool result the model judged `noise` or
|
|
328
402
|
`ruled-out` is served as a ticket, `{"collapsed":true,"standing":…,
|
|
329
|
-
|
|
403
|
+
"toolCallId":…}`, in place of its content; `open` and undeclared results
|
|
330
404
|
stay verbatim; nothing is dropped or reordered, and `toolName` /
|
|
331
405
|
`toolCallId` are untouched, so the tool_use/tool_result pair stays
|
|
332
406
|
wire-valid. `history` never changes — the window stage stays its only
|
|
@@ -620,7 +694,7 @@ footprintjs the range admits (`^9.21.1`).
|
|
|
620
694
|
touched before); `cache-transform`'s "that gap is the stronger claim" is
|
|
621
695
|
measured for every (gap, shared field) pairing that occurs beside it on
|
|
622
696
|
every fixture view, intact and damaged, against that gap's own claim (an
|
|
623
|
-
unmeasured kind fails by name; "stronger" is
|
|
697
|
+
unmeasured kind fails by name; "stronger" is _may be short_, ≤ — on a fresh
|
|
624
698
|
run with its base stripped the rebuild recovers everything);
|
|
625
699
|
`forced-tool-schema`'s "the tool list is complete" runs on both chart
|
|
626
700
|
shapes. The design page marks the three built; the general statement — a
|
|
@@ -677,7 +751,7 @@ footprintjs the range admits (`^9.21.1`).
|
|
|
677
751
|
(`servedView.ts` · `readReceipt`), which checks `basis.epoch` and nothing
|
|
678
752
|
else. A stored receipt with a `basis` and no `cache` container therefore
|
|
679
753
|
made `servedAt(snapshot, k)` throw `Cannot read properties of undefined
|
|
680
|
-
|
|
754
|
+
(reading 'strategy')` where 9.92 built a view. WHY it matters: a recording is
|
|
681
755
|
older than the reader that opens it, and every recording a consumer already
|
|
682
756
|
held was minted by the release that minted it — the lens found this the day
|
|
683
757
|
after 9.93.0 shipped, on a recording it already had. That breaks the family's
|
|
@@ -688,7 +762,7 @@ footprintjs the range admits (`^9.21.1`).
|
|
|
688
762
|
the check does not verify declared optional (`cache?`, and `strategy?`
|
|
689
763
|
within it) — and `receiptAt` answers that type, so the compiler holds every
|
|
690
764
|
reader to it; `viewOf` reads the strategy through it (`receipt?.cache?.
|
|
691
|
-
|
|
765
|
+
strategy !== null`). A receipt with no `cache` yields a view, its
|
|
692
766
|
`cache-transform` gap IS raised (the 9.93 rule: absence still raises; only a
|
|
693
767
|
receipt SAYING `null` lifts it), and its `basis` and every hash row it
|
|
694
768
|
carries still verify. Nothing is repaired: no `cache: {}` is fabricated and
|
|
@@ -698,9 +772,9 @@ footprintjs the range admits (`^9.21.1`).
|
|
|
698
772
|
`omittedForAttention`) sits behind the same narrowing and the same type; no
|
|
699
773
|
second reader existed in the library. `Receipt` itself is unchanged — it is
|
|
700
774
|
the MINTED shape and a mint still writes every container.
|
|
701
|
-
THE VINTAGE LAW, now in `src/lib/time-travel/README.md`:
|
|
775
|
+
THE VINTAGE LAW, now in `src/lib/time-travel/README.md`: _a reader reads
|
|
702
776
|
the receipt it is handed; a missing container is a fact about the vintage,
|
|
703
|
-
never a throw
|
|
777
|
+
never a throw._ `test/lib/time-travel/receipt-vintage.test.ts` ages real
|
|
704
778
|
recordings three ways — no `cache` (the shape the lens hit), `cache` without
|
|
705
779
|
`strategy` (the 9.88.0–9.92.1 mint, byte for byte), `strategy: null` (the
|
|
706
780
|
9.93.0 mint on a chart with no strategy) — and requires a view, the gap, the
|
|
@@ -711,8 +785,8 @@ footprintjs the range admits (`^9.21.1`).
|
|
|
711
785
|
`receiptAt(s, k)!.cache.transform` unguarded must guard the container — the
|
|
712
786
|
compiler now says on the page what the runtime said with a throw.
|
|
713
787
|
```ts
|
|
714
|
-
const receipt = receiptAt(olderRecording, 1)!;
|
|
715
|
-
receipt.cache?.strategy;
|
|
788
|
+
const receipt = receiptAt(olderRecording, 1)!; // minted by 9.92
|
|
789
|
+
receipt.cache?.strategy; // undefined — not null, not repaired
|
|
716
790
|
servedAt(olderRecording, 1)!.gaps.map((g) => g.gap); // ['cache-transform', 'provider-defaults']
|
|
717
791
|
```
|
|
718
792
|
|
|
@@ -759,7 +833,7 @@ one iteration (below), and because every bundled consumer's output changes.
|
|
|
759
833
|
catalogs and answers.
|
|
760
834
|
```ts
|
|
761
835
|
const agent = Agent.create({ provider, model }).tool(lookup).selfExplain().build();
|
|
762
|
-
await agent.run({ message: 'Refund order A-1001?' });
|
|
836
|
+
await agent.run({ message: 'Refund order A-1001?' }); // pack not loaded
|
|
763
837
|
await agent.run({ message: 'Why did you approve it?' }); // loads on the iteration read_skill opened it
|
|
764
838
|
```
|
|
765
839
|
- **The ESM build now carries `sideEffects`.** A bundler reads that flag from
|
|
@@ -827,7 +901,7 @@ window and no cache strategy records a different byte except the one new key.
|
|
|
827
901
|
release has no key, and a reader treats that as "cannot say", never as
|
|
828
902
|
`null`.
|
|
829
903
|
```ts
|
|
830
|
-
receiptAt(agent.getSnapshot()!, 1)!.cache.strategy;
|
|
904
|
+
receiptAt(agent.getSnapshot()!, 1)!.cache.strategy; // '*' — an agent on the mock provider
|
|
831
905
|
receiptAt(llmCall.getSnapshot()!, 1)!.cache.strategy; // null — nothing between assembly and the port
|
|
832
906
|
```
|
|
833
907
|
- **`Receipt.omittedForAttention` is written** — by the agent chart's window
|
|
@@ -847,8 +921,11 @@ window and no cache strategy records a different byte except the one new key.
|
|
|
847
921
|
`no-receipt-on-chart`, the one gap that can lose it. An agent without a
|
|
848
922
|
window hands both stages the deps they always had.
|
|
849
923
|
```ts
|
|
850
|
-
const agent = Agent.create({ provider, model })
|
|
851
|
-
.
|
|
924
|
+
const agent = Agent.create({ provider, model })
|
|
925
|
+
.system('bot')
|
|
926
|
+
.tool(lookup)
|
|
927
|
+
.window(slidingWindow({ keepRecentTurns: 1 }))
|
|
928
|
+
.build();
|
|
852
929
|
await agent.run({ message: 'go' });
|
|
853
930
|
receiptAt(agent.getSnapshot()!, 3)!.omittedForAttention; // { count: 2, hashes: [...] } — the pair that left at this head
|
|
854
931
|
```
|
|
@@ -966,7 +1043,7 @@ consequences, one example each:
|
|
|
966
1043
|
(`ServedToolParties.lastServed`) or the name's only holder when it was never
|
|
967
1044
|
served; every such dispatch is on the record as the new
|
|
968
1045
|
`agentfootprint.tools.answered_off_wire` (`{ toolName, toolCallId,
|
|
969
|
-
|
|
1046
|
+
iteration, answeredBy, answeredById? }`). A pause that re-dispatches on
|
|
970
1047
|
resume (a middleware ask, a check-in, a credential consent) carries the
|
|
971
1048
|
served party on its checkpoint (`pausedToolParty`, pause-path-only), so a
|
|
972
1049
|
resume in a FRESH Agent instance — empty closure, no Compose — cannot fall
|
|
@@ -980,7 +1057,7 @@ consequences, one example each:
|
|
|
980
1057
|
is named as itself.
|
|
981
1058
|
- **A dead claim is reported.** New `agentfootprint.tools.claim_swallowed`
|
|
982
1059
|
(`ToolsClaimSwallowedPayload`: `{ toolName, iteration, lostBy, lostById?,
|
|
983
|
-
|
|
1060
|
+
wonBy, wonById? }`, names only) fires once per iteration for every party
|
|
984
1061
|
whose claim to a name is held by somebody else — whether its contract
|
|
985
1062
|
competed (an active skill against a provider) or never reached the merge
|
|
986
1063
|
(the same skill while inactive; a provider tool whose name a static
|
|
@@ -1050,8 +1127,8 @@ Two reasons were on the record for that. **Neither survived.** The first — tha
|
|
|
1050
1127
|
`Receipt.cache.transform` has no honest value on a chart running no cache
|
|
1051
1128
|
strategy — was already refuted in 9.88.0 and left refuted in the source: an
|
|
1052
1129
|
`Agent` with a pass-through strategy records `'unchanged'` today, and the value
|
|
1053
|
-
is true for the same reason where no strategy exists at all, because
|
|
1054
|
-
request that went out IS the request that was
|
|
1130
|
+
is true for the same reason where no strategy exists at all, because _the
|
|
1131
|
+
request that went out IS the request that was assembled_. **No fourth enum value
|
|
1055
1132
|
was added.** The second — the salt — never applied to `LLMCall`, which owns its
|
|
1056
1133
|
executor and mints a run id exactly as `Agent` does. It DOES apply to the two
|
|
1057
1134
|
messageAPI charts, and that was verified rather than assumed: footprintjs stamps
|
|
@@ -1094,8 +1171,8 @@ than salting every hash with an empty string.
|
|
|
1094
1171
|
unnoticed for as long as no receipt existed to contradict it. The read is now
|
|
1095
1172
|
a fallback chain (the agent key first, so no agent recording changes), the
|
|
1096
1173
|
same shape the conversation has had since 9.88.0. Red before, on
|
|
1097
|
-
`test/lib/time-travel/receipt-conformance.test.ts` ·
|
|
1098
|
-
served is hashed by the receipt AND rebuilt from the
|
|
1174
|
+
`test/lib/time-travel/receipt-conformance.test.ts` · _the tool the model was
|
|
1175
|
+
served is hashed by the receipt AND rebuilt from the log_.
|
|
1099
1176
|
|
|
1100
1177
|
### Changed
|
|
1101
1178
|
|
|
@@ -1103,7 +1180,7 @@ than salting every hash with an empty string.
|
|
|
1103
1180
|
printed sentence never named a chart, so nothing in it went false; the
|
|
1104
1181
|
MECHANISM comment beside it now names the shapes that still reach it — a
|
|
1105
1182
|
messageAPI chart handed no run id, a run that declined with `recordReceipt:
|
|
1106
|
-
|
|
1183
|
+
false`, a recording made before 9.88.0, and a consumer's own `call-llm` stage,
|
|
1107
1184
|
which this library does not mint for. The cause `'no-receipt-committed'` is
|
|
1108
1185
|
still driven by a REAL run in both walks; the driver moved from an `LLMCall`
|
|
1109
1186
|
(which now mints) to a messageAPI chart with no run id.
|
|
@@ -1193,12 +1270,12 @@ import { milestoneTag, milestoneStopsStrategy } from 'agentfootprint';
|
|
|
1193
1270
|
|
|
1194
1271
|
// A reader with NO agent id conventions — footprintjs's own strategy, our word:
|
|
1195
1272
|
const turns = timeTravel(agent.getSnapshot()!, { strategy: tagStops([milestoneTag('llm-turn')]) });
|
|
1196
|
-
turns.stops.map((s) => s.label);
|
|
1197
|
-
turns.stops[1].meta;
|
|
1273
|
+
turns.stops.map((s) => s.label); // ['Run start', 'CallLLM', 'CallLLM', 'Run end']
|
|
1274
|
+
turns.stops[1].meta; // ['milestone:llm-turn', 'milestone-label:LLM turn']
|
|
1198
1275
|
|
|
1199
1276
|
// The agent's own axis, unchanged in shape — tag first, id fallback:
|
|
1200
1277
|
const all = timeTravel(agent.getSnapshot()!, { strategy: milestoneStopsStrategy });
|
|
1201
|
-
all.stops[5].meta;
|
|
1278
|
+
all.stops[5].meta; // { kind: 'llm-turn', label: 'LLM turn' } — read off the bundle
|
|
1202
1279
|
```
|
|
1203
1280
|
|
|
1204
1281
|
## [9.89.3] - 2026-09-10
|
|
@@ -1283,13 +1360,13 @@ for byte as before.
|
|
|
1283
1360
|
const tool = flowchartAsTool({
|
|
1284
1361
|
name: 'seeded',
|
|
1285
1362
|
description: 'Seeds a subflow with the key and reads it there.',
|
|
1286
|
-
flowchart: chart,
|
|
1363
|
+
flowchart: chart, // parent writes apiKey; the subflow is seeded with it
|
|
1287
1364
|
keepRecord: true,
|
|
1288
1365
|
redact: { keys: ['apiKey'] },
|
|
1289
1366
|
});
|
|
1290
|
-
await tool.execute({}, ctx);
|
|
1367
|
+
await tool.execute({}, ctx); // → '{"apiKey":"REDACTED","seen":26}' — the subflow computed on the real 26-byte key
|
|
1291
1368
|
const record = JSON.stringify(innerRunsOf(tool)!.get(ctx.toolCallId)!);
|
|
1292
|
-
record.includes('sk-live-');
|
|
1369
|
+
record.includes('sk-live-'); // false — result, log, mirror, stageReads, narrative Input: line, history[0], parent log
|
|
1293
1370
|
```
|
|
1294
1371
|
|
|
1295
1372
|
The law's other half is unchanged and now pinned in the same file: the live
|
|
@@ -1320,7 +1397,7 @@ changes, and a tool without `redact` behaves byte for byte as before.
|
|
|
1320
1397
|
policy-redacted key never entered the inner commit log — and the option's
|
|
1321
1398
|
own documentation drew the wrong conclusion from that. The live state view
|
|
1322
1399
|
is not a commit: it is the run's raw heap, and its scrubbed twin (the
|
|
1323
|
-
|
|
1400
|
+
_redacted mirror_ footprintjs maintains beside it) is served only by
|
|
1324
1401
|
`getSnapshot({ redact: true })`. Both tools called `getSnapshot()` bare, so
|
|
1325
1402
|
the string the model read (`JSON.stringify(snapshot.values)`, or whatever a
|
|
1326
1403
|
`resultMapper` built from it), the envelope's state, and a kept record's
|
|
@@ -1341,13 +1418,13 @@ changes, and a tool without `redact` behaves byte for byte as before.
|
|
|
1341
1418
|
const tool = flowchartAsTool({
|
|
1342
1419
|
name: 'weather_advice',
|
|
1343
1420
|
description: 'Forecast tomorrow and advise on biking.',
|
|
1344
|
-
flowchart: adviceChart,
|
|
1421
|
+
flowchart: adviceChart, // writes scope.apiKey = 'sk-…'
|
|
1345
1422
|
keepRecord: true,
|
|
1346
1423
|
redact: { keys: ['apiKey'] },
|
|
1347
1424
|
});
|
|
1348
|
-
await tool.execute({}, ctx);
|
|
1425
|
+
await tool.execute({}, ctx); // → '{"apiKey":"REDACTED","advice":"bike"}'
|
|
1349
1426
|
const { snapshot } = innerRunsOf(tool)!.get(ctx.toolCallId)!.recording!;
|
|
1350
|
-
snapshot.sharedState.apiKey;
|
|
1427
|
+
snapshot.sharedState.apiKey; // 'REDACTED' — was the plaintext
|
|
1351
1428
|
JSON.stringify(snapshot).includes('sk-'); // false — in every field the log scrubs
|
|
1352
1429
|
```
|
|
1353
1430
|
|
|
@@ -1385,7 +1462,8 @@ both additive: a 9.88.0 consumer compiles and behaves identically.
|
|
|
1385
1462
|
object a consumer already holds:
|
|
1386
1463
|
|
|
1387
1464
|
```ts
|
|
1388
|
-
receiptHash(runId, toolDigestInput(servedAt(k).tools.schemas[i])) ===
|
|
1465
|
+
receiptHash(runId, toolDigestInput(servedAt(k).tools.schemas[i])) ===
|
|
1466
|
+
receiptAt(k).tools.schemaHashes[name];
|
|
1389
1467
|
```
|
|
1390
1468
|
|
|
1391
1469
|
It takes an `LLMToolSchema` — the tool as handed to the provider port, which
|
|
@@ -1413,7 +1491,7 @@ both additive: a 9.88.0 consumer compiles and behaves identically.
|
|
|
1413
1491
|
all of it by hand against 9.17. This was one of them. The hand-rolled guard,
|
|
1414
1492
|
the re-partition loop and `milestoneOf`'s re-derivation are gone;
|
|
1415
1493
|
`milestoneStops` is now one expression, `filterStops(commitStops(log, tree),
|
|
1416
|
-
|
|
1494
|
+
keep)`, and the one owner of the `[start, …stages, end]` contract is the
|
|
1417
1495
|
library that returns it. Public names and signatures are unchanged
|
|
1418
1496
|
(`milestoneOf`, `milestoneStops`, `milestoneStopsStrategy`); the stops are
|
|
1419
1497
|
now typed `Stop<Milestone>`, so `cursor.at()?.meta?.kind` is typed, and
|
|
@@ -1543,14 +1621,14 @@ that file now exists. All of it is closed below, at the root, additively.
|
|
|
1543
1621
|
strategy actually applied, three scalars each (`field`, `boundaryIndex`,
|
|
1544
1622
|
`ttl`). `transformHash` is a digest over the whole prepared request: inside a
|
|
1545
1623
|
run it says only "something changed", and across epochs it is not comparable
|
|
1546
|
-
at all — so it could not answer
|
|
1547
|
-
call 4
|
|
1624
|
+
at all — so it could not answer _did the breakpoints move between call 3 and
|
|
1625
|
+
call 4?_, which is the question that decides an Anthropic bill. Two receipts'
|
|
1548
1626
|
`markersApplied` answer it by inspection.
|
|
1549
1627
|
- **`receipt.cache.transform`** — `'unchanged' | 'rewritten' | 'unknown'`.
|
|
1550
1628
|
Branch on this, never on `transformHash === null`.
|
|
1551
1629
|
- **`RECEIPT_BOUNDARY`** — the one sentence every receipt field is true at, so
|
|
1552
|
-
a renderer prints the library's own wording:
|
|
1553
|
-
as this library last saw it
|
|
1630
|
+
a renderer prints the library's own wording: _a receipt describes the request
|
|
1631
|
+
as this library last saw it._ Where that is — the provider port — is in the
|
|
1554
1632
|
comment beside the constant, because the sentence is printed and the sixth
|
|
1555
1633
|
round's rule is that a printed sentence names no mechanism.
|
|
1556
1634
|
- **`ServedView.basis`** — `{ model, provider, runId }`, read off the receipt.
|
|
@@ -1689,7 +1767,7 @@ that file now exists. All of it is closed below, at the root, additively.
|
|
|
1689
1767
|
terminal, in a diff and in review, and one `sed` away from being eaten. Same
|
|
1690
1768
|
bytes, same hashes; they can now be read.
|
|
1691
1769
|
- **`test/lib/time-travel/receipt-conformance.test.ts` · `describe('a redacted
|
|
1692
|
-
|
|
1770
|
+
run')` was a NO-OP, and two READMEs documented what it pretended to prove.**
|
|
1693
1771
|
It passed `redact: [...]` to `Agent.create`, which has no such option;
|
|
1694
1772
|
`tsconfig.json` excludes `test/`, so the unknown key was never typechecked and
|
|
1695
1773
|
was silently dropped. The run was not redacted, and the READMEs' "honest edge"
|
|
@@ -1862,6 +1940,7 @@ a sentence is TRUE, and three of them were not.
|
|
|
1862
1940
|
conditionally instead would mean inferring "no strategy ran" from a recording,
|
|
1863
1941
|
which is the absence-of-evidence reading this whole feature refuses; recorded
|
|
1864
1942
|
as entry 8 of `docs/design/2026-09-recorded-not-built.md`.
|
|
1943
|
+
|
|
1865
1944
|
#### The fifth and sixth rounds — a printed gap sentence stops describing code
|
|
1866
1945
|
|
|
1867
1946
|
Five review rounds, and each one found NEW false prose in the sentences the
|
|
@@ -1871,7 +1950,7 @@ composed once and read many times is a PREDICTION** — one surface over: compos
|
|
|
1871
1950
|
once, and read against every later version of the code it describes.
|
|
1872
1951
|
|
|
1873
1952
|
The reproduction is one shipped string. `SERVED_GAPS['no-receipt-on-chart'].why`
|
|
1874
|
-
said
|
|
1953
|
+
said _"THREE causes and none of them is a hole in this view: …"_. A fourth path
|
|
1875
1954
|
was then added — a value under the receipt key refused because it carries no
|
|
1876
1955
|
basis — and BOTH halves went false at once: four causes, and that one IS a hole.
|
|
1877
1956
|
Nobody edited the string. Nobody had to.
|
|
@@ -1883,12 +1962,12 @@ one, and the reason is the whole story of this release.
|
|
|
1883
1962
|
|
|
1884
1963
|
Five rounds tried to write TRUE mechanism sentences and the rate of new
|
|
1885
1964
|
falsehoods held constant. So the sixth put one question to all ten printed
|
|
1886
|
-
sentences —
|
|
1965
|
+
sentences — _could this become false without anyone editing it?_ — and NINE
|
|
1887
1966
|
could, two of them being false the day they shipped. Exactly one could not:
|
|
1888
1967
|
|
|
1889
|
-
> `UNGAPPED_FIELDS.gaps` —
|
|
1968
|
+
> `UNGAPPED_FIELDS.gaps` — _"The account itself rather than a fact about the
|
|
1890
1969
|
> request: a gap naming this list would be the account excusing its own
|
|
1891
|
-
> absence."
|
|
1970
|
+
> absence."_
|
|
1892
1971
|
|
|
1893
1972
|
It survives because it makes **no claim about code**. It says what the field
|
|
1894
1973
|
means inside the account, and nothing outside the sentence can falsify it. Every
|
|
@@ -1923,8 +2002,8 @@ of them still make a claim a code edit falsifies.** Exactly one does not —
|
|
|
1923
2002
|
`UNGAPPED_FIELDS.gaps` — and it does not because it is SELF-REFERENTIAL: it says
|
|
1924
2003
|
what its field is inside the account, not anything about the request. The other
|
|
1925
2004
|
ten cannot copy that shape, because a sentence that tells a reader something
|
|
1926
|
-
USEFUL —
|
|
1927
|
-
the schemas are one
|
|
2005
|
+
USEFUL — _may be SHORT_, _absent means unknown_, _the tool list is complete and
|
|
2006
|
+
the schemas are one short_ — is a claim about how the rebuild behaves, and the
|
|
1928
2007
|
rebuild is code. **The reduction changed the VOCABULARY of the claims, not their
|
|
1929
2008
|
CLASS.**
|
|
1930
2009
|
|
|
@@ -1968,20 +2047,20 @@ spot than six rounds of rewriting produced, and it is the whole of it.
|
|
|
1968
2047
|
a doc is FOR; the other three do not, because a cause count goes stale in a
|
|
1969
2048
|
doc exactly as it does in a constant.
|
|
1970
2049
|
- **Every printed gap sentence is one or two sentences and names nothing.**
|
|
1971
|
-
`no-receipt-on-chart` now reads in full:
|
|
2050
|
+
`no-receipt-on-chart` now reads in full: _"Nothing on this view has been
|
|
1972
2051
|
checked against what went out. Every field below is missing as a whole, and an
|
|
1973
2052
|
absence among them says nothing about the call — not even that a dial was left
|
|
1974
|
-
unset."
|
|
2053
|
+
unset."_ `forced-tool-schema` is 26 words. `callRuntimeStageId`'s excuse is 12.
|
|
1975
2054
|
- **TWO SENTENCES WERE FALSE THE DAY THEY SHIPPED, and both were mechanism
|
|
1976
2055
|
claims, so the rule deletes the category rather than the instances.**
|
|
1977
|
-
`cache-transform` said
|
|
2056
|
+
`cache-transform` said _"only its INPUTS are on the record"_ while three of
|
|
1978
2057
|
the fields it covers are OUTPUTS that are on the record — `cache.transform`
|
|
1979
2058
|
(the verdict of comparing what the strategy was given against what it handed
|
|
1980
2059
|
back), `cache.transformHash` (the digest of the result, when they differed)
|
|
1981
2060
|
and `cache.markersApplied` (the breakpoints actually applied, as against the
|
|
1982
2061
|
candidates in `scope.cacheMarkers`, which are the inputs). And
|
|
1983
|
-
`no-receipt-on-chart` opened
|
|
1984
|
-
closed
|
|
2062
|
+
`no-receipt-on-chart` opened _"No receipt was found for this epoch"_ and
|
|
2063
|
+
closed _"absent here means unrecorded"_ — both false under
|
|
1985
2064
|
`'receipt-shape-rejected'`, where a receipt WAS written and the read refused
|
|
1986
2065
|
it, which is to say the sentence asserted which cause applied and was wrong
|
|
1987
2066
|
for one of two. Neither can be written under the new rule. Both are corrected
|
|
@@ -1990,9 +2069,9 @@ spot than six rounds of rewriting produced, and it is the whole of it.
|
|
|
1990
2069
|
- **`RECEIPT_BOUNDARY` was the one printed sentence exempted from the rule, and
|
|
1991
2070
|
the exemption is gone.** It named `LLMProvider.complete` and `complete()` — a
|
|
1992
2071
|
module and a call, printed to a reader who cannot open either. It now reads
|
|
1993
|
-
|
|
2072
|
+
_"A receipt describes the request as this library last saw it. Whatever
|
|
1994
2073
|
handled it after that could have changed it, and nothing on the receipt would
|
|
1995
|
-
show that."
|
|
2074
|
+
show that."_ The port, the decorated provider, the vendor adapter and the
|
|
1996
2075
|
vendor's own defaults are in the comment above the constant. One string, one
|
|
1997
2076
|
rule set: the walk no longer strips it before judging, and asserts it on its
|
|
1998
2077
|
own as well as inside the two entries that quote it.
|
|
@@ -2027,8 +2106,8 @@ spot than six rounds of rewriting produced, and it is the whole of it.
|
|
|
2027
2106
|
measurement is unchanged and still re-taken by the walk on every run; it reads
|
|
2028
2107
|
the claim from the COMMENT beside the entry now, so a chart that starts
|
|
2029
2108
|
supplying one still fails the suite instead of aging the sentence. What a
|
|
2030
|
-
reader is shown is what the field means:
|
|
2031
|
-
drop, never that nothing was dropped."
|
|
2109
|
+
reader is shown is what the field means: _"Absent means nobody recorded a
|
|
2110
|
+
drop, never that nothing was dropped."_
|
|
2032
2111
|
- **`examples/observability/24-receipt-at-the-stop.ts` clipped a printed reason
|
|
2033
2112
|
mid-version-number.** It printed `reason.split('.')[0]`, so "measured on
|
|
2034
2113
|
9.88.0" reached a reader as "measured on 9." — a fragment that reads as a
|
|
@@ -2038,8 +2117,7 @@ spot than six rounds of rewriting produced, and it is the whole of it.
|
|
|
2038
2117
|
coverage.** The header claimed the vacuous-row guard was closed "by driving
|
|
2039
2118
|
each row on a run that reaches its own fields". Measured, that clause is
|
|
2040
2119
|
false: `no-fold-base` moves 7 of the 11 fields it names, `no-run-log` 1 of 4,
|
|
2041
|
-
`no-receipt-on-chart` 3 of 8, and only `no-conversation-on-record` reaches all
|
|
2042
|
-
3. The reasons are structural — a receipt-only field never appears on a view,
|
|
2120
|
+
`no-receipt-on-chart` 3 of 8, and only `no-conversation-on-record` reaches all 3. The reasons are structural — a receipt-only field never appears on a view,
|
|
2043
2121
|
so removing the receipt cannot MOVE it — so the header names the numbers per
|
|
2044
2122
|
row, a test pins them so the table cannot go stale, and it says plainly that a
|
|
2045
2123
|
field claim divergence does not reach is carried by the coverage half and by
|
|
@@ -2063,56 +2141,57 @@ each of them, which is how the library closes everything else, and it found a
|
|
|
2063
2141
|
different class of defect: not prose that names a mechanism, but prose that is
|
|
2064
2142
|
plain, short, rule-abiding and UNTRUE OF THE VIEW IT IS PRINTED BESIDE.
|
|
2065
2143
|
|
|
2066
|
-
- **`no-run-log` claimed a loss that a run says did not happen.** It read
|
|
2067
|
-
fields below could not be fully recovered here"
|
|
2144
|
+
- **`no-run-log` claimed a loss that a run says did not happen.** It read _"The
|
|
2145
|
+
fields below could not be fully recovered here"_ — an assertion that recovery
|
|
2068
2146
|
DID fail. Measured on the ordinary view that raises it, a
|
|
2069
2147
|
`'dynamic-grouped'` agent with one plain tool and its `commitLog` emptied:
|
|
2070
2148
|
`tools.names`, `tools.schemas`, `tools.forced` and `messages.requestOnly` all
|
|
2071
2149
|
come back BYTE-IDENTICAL to the intact view. The gap fires and costs nothing,
|
|
2072
2150
|
because that run has no forced tool name and no `wants` to lose. It now reads
|
|
2073
|
-
|
|
2151
|
+
_"The fields below may be SHORT: a name can be missing from the tool list, and
|
|
2074
2152
|
a line that went out with the request can be missing too. An absence below is
|
|
2075
2153
|
not evidence that there was nothing there — read the whole recording rather
|
|
2076
|
-
than a piece of it."
|
|
2154
|
+
than a piece of it."_ **The repair is the sentence and not the condition**,
|
|
2077
2155
|
and the reason is that the condition cannot be narrowed by anything the read
|
|
2078
2156
|
can see: whether the run had a constant to lose is recorded in the log whose
|
|
2079
2157
|
absence raises the gap. Both directions are asserted — emptying the run log
|
|
2080
2158
|
takes a forced-output run's tool list from one name to none and a staged-refs
|
|
2081
2159
|
run's request-only line to nothing, and takes nothing at all from the plain
|
|
2082
2160
|
run. Its second clause was loose as well: it said a line could go missing
|
|
2083
|
-
|
|
2161
|
+
_"from the conversation"_, and a request-only line is by construction in no
|
|
2084
2162
|
conversation — asserted now against the wire, which carries it as the last
|
|
2085
2163
|
message of the request while the rebuilt `messages.asSent` does not contain it.
|
|
2086
2164
|
- **Three sentences were true where they were composed and misleading where they
|
|
2087
2165
|
were PRINTED.** One sentence, several contexts: the library's own Honest
|
|
2088
2166
|
Sentence law says it has to hold in all of them.
|
|
2089
|
-
|
|
2090
|
-
|
|
2167
|
+
|
|
2168
|
+
- `cache-transform` ended by quoting `RECEIPT_BOUNDARY` — _"A receipt
|
|
2169
|
+
describes the request as this library last saw it"_ — and it is raised on
|
|
2091
2170
|
EVERY view, including a receipt-less one. Measured: an `LLMCall` view
|
|
2092
2171
|
carries exactly `no-receipt-on-chart` and `cache-transform`, so the reader
|
|
2093
2172
|
was told what a receipt describes beside a view that has none. The quote is
|
|
2094
2173
|
gone from it and the claim survives in the entry's own words, in the
|
|
2095
|
-
vocabulary of a view:
|
|
2174
|
+
vocabulary of a view: _"…and nothing on this view would show it."_
|
|
2096
2175
|
`provider-defaults` keeps the quote and is the only entry that may have it —
|
|
2097
2176
|
it is pushed inside `if (receipt !== undefined)`, so a view carrying it
|
|
2098
2177
|
always has a receipt for the sentence to be about. Asserted across every run
|
|
2099
2178
|
in the new file: a gap whose `why` includes the boundary appears only on a
|
|
2100
2179
|
view whose `basis` is defined.
|
|
2101
|
-
- `no-fold-base` said the view's number
|
|
2102
|
-
for this turn carries"
|
|
2180
|
+
- `no-fold-base` said the view's number _"may differ from the one the receipt
|
|
2181
|
+
for this turn carries"_ — printed on views that carry no receipt (a
|
|
2103
2182
|
base-less `LLMCall` recording raises both gaps at once). It now says what
|
|
2104
|
-
the NUMBER means:
|
|
2105
|
-
order rather than the number the run itself gave it."
|
|
2183
|
+
the NUMBER means: _"The turn number below may be this turn's place in run
|
|
2184
|
+
order rather than the number the run itself gave it."_ Asserted on the
|
|
2106
2185
|
resumed run whose base and `iteration` writes are gone: the view calls the
|
|
2107
2186
|
second turn 1 while the run's own count for it was 2.
|
|
2108
|
-
- `no-receipt-on-chart` closed
|
|
2109
|
-
never a call made without them"
|
|
2187
|
+
- `no-receipt-on-chart` closed _"their absence here is a gap in the record,
|
|
2188
|
+
never a call made without them"_. True of each field AS A WHOLE and false
|
|
2110
2189
|
one level down, which is the level a reader reads at: a receipt always
|
|
2111
2190
|
carries `params` and always carries a `cache.transform` verdict, and an
|
|
2112
2191
|
absence INSIDE `params` — measured `{}` on an agent that set no dials — IS a
|
|
2113
2192
|
call made without one. It now claims nothing about what is inside a field it
|
|
2114
|
-
cannot see:
|
|
2115
|
-
that a dial was left unset."
|
|
2193
|
+
cannot see: _"…an absence among them says nothing about the call — not even
|
|
2194
|
+
that a dial was left unset."_
|
|
2116
2195
|
|
|
2117
2196
|
- **`test/lib/time-travel/gap-sentences.test.ts` — the assertion, beside the
|
|
2118
2197
|
sentence.** One real run per catalogue entry and per `UNGAPPED_FIELDS` key,
|
|
@@ -2201,6 +2280,7 @@ plain, short, rule-abiding and UNTRUE OF THE VIEW IT IS PRINTED BESIDE.
|
|
|
2201
2280
|
hand-listed, so a gap whose damage nobody wrote down still gets only the
|
|
2202
2281
|
account half. Neither half reads a `why`. That is a person's job, and it is
|
|
2203
2282
|
where this release's last three defects came from.
|
|
2283
|
+
|
|
2204
2284
|
- `test/lib/time-travel/keyed-fold-equivalence.test.ts` — 9 tests: every key at
|
|
2205
2285
|
every commit of a real run, checked against `stateAt` itself, including a
|
|
2206
2286
|
merge with no `set` anchor (which cannot be folded at all without the base),
|
|
@@ -2250,9 +2330,9 @@ the examples it pulls in — a repair of its own, not a line item in this releas
|
|
|
2250
2330
|
The hole is closed instead in `test/type-regressions/`, which already compiles
|
|
2251
2331
|
under `npm run test:types`.
|
|
2252
2332
|
|
|
2253
|
-
`ServedGap.fields` carries TWO relations on one list. Most entries mean
|
|
2254
|
-
rebuild cannot produce this
|
|
2255
|
-
|
|
2333
|
+
`ServedGap.fields` carries TWO relations on one list. Most entries mean _the
|
|
2334
|
+
rebuild cannot produce this field_; `cache-transform`'s composition fields mean
|
|
2335
|
+
_it can, and both sides agree, but only up to the cache strategy_. A checker
|
|
2256
2336
|
that granted the second as an excuse would stop checking fields the record
|
|
2257
2337
|
proves perfectly well — which is exactly what happened when the composition
|
|
2258
2338
|
fields were added, and it silently disabled one clause of the law. The clause
|
|
@@ -2396,7 +2476,7 @@ Iteration → … → Run end.
|
|
|
2396
2476
|
becomes a one-line reader of it.
|
|
2397
2477
|
|
|
2398
2478
|
- **Which keys are visible where, in the grouped shape.** The settled skill
|
|
2399
|
-
cursor for turn
|
|
2479
|
+
cursor for turn _k_ is on the OUTER axis, at iteration _k_ (`currentSkillId`).
|
|
2400
2480
|
Inside the drill, `currentSkillId` is the value the turn STARTED from — it
|
|
2401
2481
|
crosses the mount as a read-only input — and the move the turn made is
|
|
2402
2482
|
`nextSkillCursor`, merged back out by the outputMapper. Both logs are truthful
|
|
@@ -2442,7 +2522,7 @@ Iteration → … → Run end.
|
|
|
2442
2522
|
- **17 tests over real runs**, not fixtures: the axis in both chart shapes, the
|
|
2443
2523
|
partition property (every commit belongs to exactly one stop), a jump that
|
|
2444
2524
|
lands and a miss that names a nearest without moving, the skill graph read
|
|
2445
|
-
along the commits with the wire as witness (the skill whose body rode turn
|
|
2525
|
+
along the commits with the wire as witness (the skill whose body rode turn _k_
|
|
2446
2526
|
is the one that iteration's own log settled on), marks that survive jumps and
|
|
2447
2527
|
never appear in the recording, what `'start'` folds on this axis versus the
|
|
2448
2528
|
port's, a resumed run's axis, and a log with no milestones in it. One more
|
|
@@ -2477,7 +2557,7 @@ Everything else here is one review of 9.86.0, taken finding by finding.
|
|
|
2477
2557
|
`TurnRoute.by`'s six values, so the clause was composed for the other four too:
|
|
2478
2558
|
false for `'continuity'` (the cursor was carried over from the previous turn and
|
|
2479
2559
|
nothing moved it — the verdict every follow-up produces under `{ strictness:
|
|
2480
|
-
|
|
2560
|
+
'guard', continuity: 'conversation' }`), false for `'menu'` resolved by the
|
|
2481
2561
|
model's own pick, and unprovable for `'none'`. The composer now takes
|
|
2482
2562
|
`turnStartedBy: TurnRoute['by']` and says one past fact per value — "the turn's
|
|
2483
2563
|
start had already been resolved decisively", "the cursor had been carried over
|
|
@@ -2570,7 +2650,7 @@ Everything else here is one review of 9.86.0, taken finding by finding.
|
|
|
2570
2650
|
The ten 9.86.0 rows cited `buildToolRegistry.ts` line ranges from the 9.85.0 tree
|
|
2571
2651
|
that the same release had moved by about twenty-nine lines, beside a
|
|
2572
2652
|
`buildToolsSlot.ts` line from HEAD. They name the checks now (`holders.includes
|
|
2573
|
-
|
|
2653
|
+
(PRESENT_TOOL_NAME)`, the `seenNames` loop, the `sharedSkillTools` backfill), and
|
|
2574
2654
|
the baseline's `note` says so. The `present-vs-mcp` row no longer claims the MCP
|
|
2575
2655
|
cell proves the blind spot is the provider channel: both claimants mount through
|
|
2576
2656
|
`staticTools()`, so the cell shows an MCP catalog inherits that seam unchanged and
|
|
@@ -2685,11 +2765,11 @@ the cursor?", and one scope key answers "which skill ids may this role see?".
|
|
|
2685
2765
|
export inside the tool-calls stage, not on any package door — _corrected in
|
|
2686
2766
|
9.86.1_), which
|
|
2687
2767
|
names the dispatch roster: `Unknown tool 'X' on that call. Tool names that
|
|
2688
|
-
|
|
2768
|
+
resolved to an implementation on that call: …`, or, with an empty roster, that
|
|
2689
2769
|
none did. The leading `Unknown tool` token is preserved, so every matcher on it
|
|
2690
2770
|
is untouched.
|
|
2691
2771
|
|
|
2692
|
-
It says
|
|
2772
|
+
It says _resolved_, not _could be dispatched_, because two gates sit between
|
|
2693
2773
|
resolution and a tool running — the `tool_call` permission check and the
|
|
2694
2774
|
middleware chain — and neither is asked to phrase an error. And the roster is
|
|
2695
2775
|
role-filtered before it is named: it used to read the dispatch map raw and could
|
|
@@ -2703,10 +2783,10 @@ the cursor?", and one scope key answers "which skill ids may this role see?".
|
|
|
2703
2783
|
Fold holds.** Three sentences branched on `length > 0` over an already-filtered
|
|
2704
2784
|
array, so "the graph held nothing" and "the role filter emptied it" composed the
|
|
2705
2785
|
same words. A cursor whose only declared hop was hidden answered `read_skill`
|
|
2706
|
-
with
|
|
2786
|
+
with _"No skill was reachable from 'alpha' when that call was made."_ while the
|
|
2707
2787
|
graph was routing `alpha`; a `'guard'` menu whose every id had been hidden since
|
|
2708
|
-
the turn started said
|
|
2709
|
-
Declared routes moved the cursor instead."
|
|
2788
|
+
the turn started said _"no menu was outstanding when that call was made.
|
|
2789
|
+
Declared routes moved the cursor instead."_ — two false clauses in one breath.
|
|
2710
2790
|
|
|
2711
2791
|
A model told the map is a dead end stops asking for the door it may not be
|
|
2712
2792
|
shown, and the checker cannot see it: every one of those sentences passes
|
|
@@ -2719,8 +2799,8 @@ the cursor?", and one scope key answers "which skill ids may this role see?".
|
|
|
2719
2799
|
|
|
2720
2800
|
The fourth sentence was the one the model reads to CHOOSE. `describeOffer`
|
|
2721
2801
|
computed its columns from an already-filtered catalog, so a cursor whose only
|
|
2722
|
-
declared hop is hidden was told
|
|
2723
|
-
the skill you are in, or finish."
|
|
2802
|
+
declared hop is hidden was told _"Nothing is reachable from here — answer with
|
|
2803
|
+
the skill you are in, or finish."_ while the graph held that edge. It classifies
|
|
2724
2804
|
the hop set over the unfiltered catalog now and drops the clause when the filter
|
|
2725
2805
|
is what emptied it; with nothing wired out at all the sentence still stands,
|
|
2726
2806
|
because that absence is one the description has evidence for. Reaching it meant
|