contextgc 0.4.2__tar.gz → 0.4.4__tar.gz
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.
- {contextgc-0.4.2 → contextgc-0.4.4}/PKG-INFO +295 -50
- {contextgc-0.4.2 → contextgc-0.4.4}/README.md +294 -49
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/schemas/coding.json +8 -7
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/schemas/travel.json +4 -4
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc.egg-info/PKG-INFO +295 -50
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc.egg-info/SOURCES.txt +1 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/pyproject.toml +1 -1
- {contextgc-0.4.2 → contextgc-0.4.4}/tests/test_benchmarks.py +227 -0
- contextgc-0.4.4/tests/test_readme_examples.py +123 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/tests/test_value_shapes.py +75 -19
- {contextgc-0.4.2 → contextgc-0.4.4}/tests/test_write_path_hardening.py +41 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/LICENSE +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/__init__.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/anchors.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/client.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/gc_engine.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/py.typed +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/sanitizer.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/schemas/logistics.json +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/schemas.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/state_dag.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/state_protocol.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/transcript.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/value_shapes.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc/vector_tier.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc.egg-info/dependency_links.txt +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc.egg-info/requires.txt +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/contextgc.egg-info/top_level.txt +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/setup.cfg +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/tests/test_agent_loop.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/tests/test_contextgc.py +0 -0
- {contextgc-0.4.2 → contextgc-0.4.4}/tests/test_state_protocol.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextgc
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Deterministic context compiler for AI agent transcripts. Retires superseded facts, compacts tool output, pins declared invariants. No dependencies, no model calls.
|
|
5
5
|
Author: Jay Gopal Tripathy
|
|
6
6
|
License: MIT
|
|
@@ -52,38 +52,65 @@ no network, no API key, no dependencies. Same input, same output, microseconds.
|
|
|
52
52
|
pip install contextgc
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
> **
|
|
56
|
-
>
|
|
57
|
-
>
|
|
58
|
-
>
|
|
59
|
-
>
|
|
55
|
+
> **On PyPI** as `contextgc`, published via Trusted Publishing with no stored
|
|
56
|
+
> credentials. `pip install contextgc` gets you the library and nothing else —
|
|
57
|
+
> it has **zero runtime dependencies** by design, and it is a library, not a
|
|
58
|
+
> CLI: there is no `contextgc` command. The entry points are
|
|
59
|
+
> `compile_messages`, `load_schema`, `patch_openai` and `StateDAG`.
|
|
60
60
|
>
|
|
61
|
-
>
|
|
62
|
-
>
|
|
63
|
-
>
|
|
61
|
+
> If you found an earlier version of this README saying "Not on PyPI yet", it
|
|
62
|
+
> was true when written and then stopped being true, which is how a README
|
|
63
|
+
> starts lying in the most load-bearing spot it has. Every number in this file
|
|
64
|
+
> is reproducible from the repository; see below for how to get it.
|
|
64
65
|
|
|
65
66
|
```python
|
|
66
|
-
from contextgc import compile_messages
|
|
67
|
+
from contextgc import compile_messages, load_schema
|
|
67
68
|
|
|
68
69
|
messages = [
|
|
69
|
-
{"role": "user", "content": "Deliver order ORD-1 to
|
|
70
|
-
{"role": "assistant", "content": "Confirmed,
|
|
71
|
-
{"role": "user", "content": "
|
|
72
|
-
{"role": "assistant", "content": "
|
|
73
|
-
{"role": "user", "content": "Actually reroute to Gate 2. Code 4921."},
|
|
74
|
-
{"role": "assistant", "content": "Rerouted to Gate 2."},
|
|
70
|
+
{"role": "user", "content": "Deliver order ORD-1 to 402 Oak Street, Apt 5, Springfield, IL 62704. Pay with my credit card."},
|
|
71
|
+
{"role": "assistant", "content": "Confirmed: 402 Oak Street, Apt 5, on the credit card."},
|
|
72
|
+
{"role": "user", "content": "Actually reroute to 900 Pine Avenue, Suite 12, Chicago, IL 60601."},
|
|
73
|
+
{"role": "assistant", "content": "Rerouted to 900 Pine Avenue, Suite 12."},
|
|
75
74
|
{"role": "user", "content": "Thanks."},
|
|
76
75
|
]
|
|
77
76
|
|
|
78
|
-
compiled, telemetry = compile_messages(messages)
|
|
77
|
+
compiled, telemetry = compile_messages(messages, schema=load_schema("logistics"))
|
|
79
78
|
|
|
80
79
|
telemetry["active_state_slots"]
|
|
81
|
-
# {'
|
|
80
|
+
# {'delivery_address': '900 Pine Avenue, Suite 12', 'payment_method': 'credit card'}
|
|
82
81
|
|
|
83
82
|
telemetry["retired_turn_indices"]
|
|
84
|
-
# [1,
|
|
83
|
+
# [1, 2] -- the two turns that asserted a now-stale address
|
|
85
84
|
```
|
|
86
85
|
|
|
86
|
+
```
|
|
87
|
+
system: [ACTIVE_AGENT_STATE]
|
|
88
|
+
- delivery_address = "900 Pine Avenue, Suite 12" (turn 3 [inferred])
|
|
89
|
+
- payment_method = "credit card" (turn 0 [inferred])
|
|
90
|
+
user: Deliver order ORD-1 to 402 Oak Street, Apt 5, Springfield, IL 62704. Pay with my credit card.
|
|
91
|
+
assistant: Rerouted to 900 Pine Avenue, Suite 12.
|
|
92
|
+
user: Thanks.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Five turns in, three out. The address that was current for one turn is gone, and
|
|
96
|
+
the one that superseded it is in a header the model can read without re-reading
|
|
97
|
+
the transcript.
|
|
98
|
+
|
|
99
|
+
> **Two things this example has to get right, and the version that was here
|
|
100
|
+
> first got both wrong.** It did not pass a `schema`, and it cannot: with no
|
|
101
|
+
> schema the read path has no vocabulary and extracts **nothing** — that is
|
|
102
|
+
> deliberate, not a bug, since a built-in default domain would match prose in
|
|
103
|
+
> every other domain. And the content has to be something the shipped schema
|
|
104
|
+
> actually matches. The earlier version of this example used *"Tower B, Flat
|
|
105
|
+
> 402"*, *"Gate 2"* and *"the Clubhouse desk"*, and claimed an output of
|
|
106
|
+
> `{'destination_address': 'Gate 2', 'dietary_allergy': 'peanut'}`. Those two
|
|
107
|
+
> slot names exist nowhere in this project, and none of that prose matches the
|
|
108
|
+
> `logistics` patterns, which want a US street address (`Oak Street`, `Pine
|
|
109
|
+
> Avenue`) and a phrase like *"my credit card"*. Run it and you got `{}` and the
|
|
110
|
+
> transcript straight back. **The first example in a README is the whole
|
|
111
|
+
> product for someone evaluating it in thirty seconds, and it was returning
|
|
112
|
+
> nothing while looking like it worked.**
|
|
113
|
+
|
|
87
114
|
Try it on your own transcript at **[context-hackdevengers.vercel.app](https://context-hackdevengers.vercel.app)** —
|
|
88
115
|
paste a conversation on the left, get the compiled context back on the right.
|
|
89
116
|
|
|
@@ -223,11 +250,23 @@ last-write-wins state register only earns its keep where a conversation keeps
|
|
|
223
250
|
changing its mind about one thing.
|
|
224
251
|
|
|
225
252
|
```bash
|
|
226
|
-
|
|
253
|
+
git clone https://github.com/j4yop/context-hackdevengers
|
|
254
|
+
cd context-hackdevengers
|
|
255
|
+
pip install -e '.[bench]'
|
|
227
256
|
python -m benchmarks fetch # 85 MB parquet shard
|
|
228
257
|
python -m benchmarks run --limit 40 --schema contextgc/schemas/coding.json
|
|
229
258
|
```
|
|
230
259
|
|
|
260
|
+
> **A clone is required, and `pip install 'contextgc[bench]'` is not enough.**
|
|
261
|
+
> The `bench` extra installs `pandas` and `pyarrow` and nothing else. The
|
|
262
|
+
> harness lives in `benchmarks/`, which is not in the wheel — the wheel ships
|
|
263
|
+
> `contextgc` only, deliberately, so the library stays dependency-free. So the
|
|
264
|
+
> command above reads
|
|
265
|
+
> `ModuleNotFoundError: No module named 'benchmarks'` from a pip install, and
|
|
266
|
+
> that is the correct behaviour, not a bug to report. Every figure in this
|
|
267
|
+
> README is reproducible from a clone; none of them is reproducible from the
|
|
268
|
+
> wheel, because the wheel does not contain the thing that measured them.
|
|
269
|
+
|
|
231
270
|
```
|
|
232
271
|
CORPUS swe-agent-trajectories
|
|
233
272
|
transcripts 40
|
|
@@ -407,8 +446,149 @@ because fixing the repeat bug in defect 6 changed what the compiler extracts.
|
|
|
407
446
|
The 100% is not a claim that the tracker is perfect. It is 100% of what was
|
|
408
447
|
labelled, and labelling is the bottleneck. Concretely, the limit is that **all 39
|
|
409
448
|
labels are one slot**: precision is currently a statement about the
|
|
410
|
-
`current_file` regex and nothing else.
|
|
411
|
-
|
|
449
|
+
`current_file` regex and nothing else. A worksheet for the second slot is built
|
|
450
|
+
below.
|
|
451
|
+
|
|
452
|
+
#### The second coding slot, unreachable and then not
|
|
453
|
+
|
|
454
|
+
`coding.failing_test` matched **740 times** in the coding corpus and **not once in
|
|
455
|
+
speech**. Every hit sat inside a pytest result — `FAILED tests/test_x.py::test_y` —
|
|
456
|
+
which is machine output the read path is *forbidden* to infer from. So the slot
|
|
457
|
+
could never fire, could never be labelled, and had no evidence behind its
|
|
458
|
+
contract. It was carried in a shipping schema by a number that was large and
|
|
459
|
+
true and meant nothing.
|
|
460
|
+
|
|
461
|
+
The number hid this because a corpus-wide count cannot tell you where the
|
|
462
|
+
matches were. `test_the_one_slot_that_never_fires_from_speech_says_so` existed
|
|
463
|
+
precisely to catch it, and the fix was to the **pattern**, not to the
|
|
464
|
+
measurement. Requiring the noun phrase that actually introduces a file — *the
|
|
465
|
+
test file `test_dispatcher.py`*, *the tests in `test_run.py`*, *the failing test
|
|
466
|
+
`test_x.py`*:
|
|
467
|
+
|
|
468
|
+
| `coding.failing_test` | before | after |
|
|
469
|
+
|---|---|---|
|
|
470
|
+
| speech observations | **0** | **78** |
|
|
471
|
+
| distinct values | 0 | **49** |
|
|
472
|
+
| observations in tool output | 740 | 14 |
|
|
473
|
+
|
|
474
|
+
Across 174,815 agent turns there are 81 noun-phrase hits, 71 of them not beside a
|
|
475
|
+
shell command. Those are assertions, not commands — which is the distinction the
|
|
476
|
+
old pattern had collapsed. Nothing the read path actually used is lost: it was
|
|
477
|
+
already discarding the other 740.
|
|
478
|
+
|
|
479
|
+
The read path now extracts `test_dispatcher.py`, `test_cognitive_complexity.py`
|
|
480
|
+
and `tests/test_cognitive_complexity.py` from the first 40 transcripts, which
|
|
481
|
+
before produced **zero**.
|
|
482
|
+
|
|
483
|
+
Two honest costs. The derived contract is now `[...]test[...].py`, which accepts
|
|
484
|
+
any path containing `test` — so it also accepts `latest_file.py` and
|
|
485
|
+
`my_test_helper.py`, and those are recorded as residual false accepts rather than
|
|
486
|
+
fitted away with a lookup table. And the slot is named `failing_test` while the
|
|
487
|
+
prose it now matches says only *a test file*; that was true of the old pattern
|
|
488
|
+
too, and renaming a shipped key is a breaking change not made to improve a
|
|
489
|
+
sentence. **49 distinct values is thin** for precision, and the worksheet below
|
|
490
|
+
exists to thicken it. It is measurable now, which it was not.
|
|
491
|
+
|
|
492
|
+
#### A cheaper check that turns out not to be good enough
|
|
493
|
+
|
|
494
|
+
With the slot reachable, the obvious next move is a check that needs no human.
|
|
495
|
+
Every `failing_test` value the read path produces from speech can be tested for
|
|
496
|
+
existence: does that path appear anywhere in what the agent's own tools listed?
|
|
497
|
+
|
|
498
|
+
| slot | read from speech | names a file the tools listed | rate |
|
|
499
|
+
|---|---|---|---|
|
|
500
|
+
| `failing_test` | 78 | 78 | **100.0%** |
|
|
501
|
+
| `current_file` | 38,375 | 38,253 | 99.7% |
|
|
502
|
+
|
|
503
|
+
`failing_test` scores 100%. **So does `current_file`, and that is the whole
|
|
504
|
+
finding: the check does not discriminate.** Both slots are near-perfect at it
|
|
505
|
+
because both name files in a repository the agent is actively working in, so
|
|
506
|
+
the file existing is nearly guaranteed and near-worthless as evidence that the
|
|
507
|
+
read path chose *correctly*.
|
|
508
|
+
|
|
509
|
+
It does rule out one real failure — the read path is not inventing paths that
|
|
510
|
+
were never there — and it rules out nothing else. A `failing_test` naming the
|
|
511
|
+
wrong-but-real test file passes it. So it is recorded as a floor, not a
|
|
512
|
+
precision figure, and the human labels remain the only route to the number.
|
|
513
|
+
|
|
514
|
+
#### The worksheet was a trap, and it is disarmed
|
|
515
|
+
|
|
516
|
+
`failing_test` is now reachable, so the next step is a worksheet. The default
|
|
517
|
+
window returns **2 rows**:
|
|
518
|
+
|
|
519
|
+
```
|
|
520
|
+
--limit 60 -> 2 rows --limit 2000 -> 21 rows
|
|
521
|
+
--limit 500 -> 7 rows --limit 6000 -> 44 rows
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Two rows read as *this slot cannot be labelled* when the shard holds 49 distinct
|
|
525
|
+
values. A person who believed that would drop the slot and report coding
|
|
526
|
+
precision as single-slot permanently — which is how it was single-slot in the
|
|
527
|
+
first place, and the reason the 100% said so little. So the worksheet now names
|
|
528
|
+
any slot short of its budget, and **refuses to guess the cause**: the window may
|
|
529
|
+
be too small, or the corpus may have no more, and only raising `--limit` tells
|
|
530
|
+
them apart. At `--limit 6000` the shortfall is real exhaustion — 44 of the 49
|
|
531
|
+
available values, the other 5 already labelled — which is the answer the tool
|
|
532
|
+
would not assert on its own.
|
|
533
|
+
|
|
534
|
+
`ws.json` is built at `--per-entity 60 --limit 6000`: 44 `failing_test` rows and
|
|
535
|
+
60 `current_file`, all `unlabelled`, no suggestions attached. It is waiting on
|
|
536
|
+
a person. **Coding precision stays at 100% (n=36, all `current_file`) until
|
|
537
|
+
someone fills it in**, and that sentence is the honest state of the number, not
|
|
538
|
+
a rounding of it.
|
|
539
|
+
|
|
540
|
+
#### The write path, checked at last
|
|
541
|
+
|
|
542
|
+
Everything above scores what the **read path** inferred. The **write path** is the
|
|
543
|
+
model declaring facts, and until now nothing had ever checked whether a declared
|
|
544
|
+
fact was *true* — which is the gap that mattered, because a declared value wins
|
|
545
|
+
over the read path by design. An unchecked declaration silently overrides
|
|
546
|
+
everything the read path got right.
|
|
547
|
+
|
|
548
|
+
The same records settle it: a capture is keyed by transcript and turn, the corpus
|
|
549
|
+
has the booking record for that transcript, and the turn indices line up.
|
|
550
|
+
|
|
551
|
+
```
|
|
552
|
+
python -m benchmarks.ground_truth travel --declarations captures/airline-14b.json
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
| 14B airline capture | count |
|
|
556
|
+
|---|---|
|
|
557
|
+
| declared values | 109 |
|
|
558
|
+
| corroborated by the record | **70** |
|
|
559
|
+
| **contradicted by the record** | **12** |
|
|
560
|
+
| unverifiable (no record saw the outcome) | 2 |
|
|
561
|
+
| unchecked (slot has no record field) | 25 |
|
|
562
|
+
|
|
563
|
+
**So the write path is wrong 12 times in 82 decidable declarations — 85.4%
|
|
564
|
+
corroborated.** And the value gate rejects **11 of those 12**. Those it catches
|
|
565
|
+
are the ones the earlier capture already showed: `passenger_id = "None"`,
|
|
566
|
+
`flight_number = "H0MVIE"` (a reservation id declared as a flight),
|
|
567
|
+
`flight_date = "2024-05-07T13:04:42"` (a timestamp, not a date), `passenger_id =
|
|
568
|
+
"Yusuf Jackson"` (a name where an id belongs).
|
|
569
|
+
|
|
570
|
+
**The twelfth is the one worth reading twice.** `cabin_class = "basic economy"`
|
|
571
|
+
where the record says `economy`. It satisfies its contract, passes the gate, and
|
|
572
|
+
is still wrong — the exact failure this project has said all along a contract
|
|
573
|
+
cannot catch, now with a row in a table rather than an assurance. A contract
|
|
574
|
+
checks shape, never truth.
|
|
575
|
+
|
|
576
|
+
Two of the numbers above are only trustworthy because of bugs found while
|
|
577
|
+
producing them:
|
|
578
|
+
|
|
579
|
+
- The record was looked up by **corpus** name (`airline`) while the marker is
|
|
580
|
+
keyed by **schema** (`travel`), so every airline record resolved to nothing and
|
|
581
|
+
109 declarations scored 0 corroborated. That reads like a finding about the
|
|
582
|
+
write path and was a lookup that found nothing.
|
|
583
|
+
- `2024-05-20` and `May 20, 2024` are the same flight date. Scoring them
|
|
584
|
+
differently called a *true* declaration a contradiction three times in one
|
|
585
|
+
conversation.
|
|
586
|
+
|
|
587
|
+
**What this does not establish.** 25 declarations sit in slots with no record
|
|
588
|
+
field at all — `total_baggages`, `cancellation_possible` — and nothing here can
|
|
589
|
+
settle them either way. And n=82 across 6 transcripts on one model is a small
|
|
590
|
+
base. The write path is *measured* now, which it was not, and it is still not
|
|
591
|
+
*proven*: one well-formed false value in twelve is the number to remember.
|
|
412
592
|
|
|
413
593
|
#### The same check on the third shipped schema, and what it cannot settle
|
|
414
594
|
|
|
@@ -423,34 +603,46 @@ whole line.
|
|
|
423
603
|
|
|
424
604
|
| | count | share |
|
|
425
605
|
|---|---|---|
|
|
426
|
-
| corroborated by the record |
|
|
427
|
-
| contradicted by the record |
|
|
428
|
-
| unverifiable |
|
|
606
|
+
| corroborated by the record | 560 | **97.4%** |
|
|
607
|
+
| **contradicted by the record** | **15** | **2.6%** |
|
|
608
|
+
| unverifiable | 17 | 3.0% |
|
|
429
609
|
|
|
430
610
|
Three things that number needs beside it.
|
|
431
611
|
|
|
612
|
+
**A record has to be scored at the right point in the conversation.** This is
|
|
613
|
+
where most of the error was, and it was in the *measurement*, not the read path.
|
|
614
|
+
`payment_history` says what was already charged and `address` says where the order
|
|
615
|
+
is now, so a `delivery_address` slot — whose whole purpose is a *requested*
|
|
616
|
+
change — looked like it was being judged against a record that was behind by
|
|
617
|
+
definition. But the record is not behind. The agent applies the change, and the
|
|
618
|
+
**next** tool result shows the new state:
|
|
619
|
+
|
|
620
|
+
apigen-1751 turn 13 "760 Elm Avenue" record before: 592 Elm after: 760 Elm
|
|
621
|
+
apigen-1817 turn 11 "123 Oak Street" record before: 463 Main after: 123 Oak
|
|
622
|
+
apigen-1865 turn 12 "828 River Road" record before: 388 Spruce after: 828 River
|
|
623
|
+
|
|
624
|
+
So a value is checked against the state as of **just before** the turn, and against
|
|
625
|
+
every state recorded **at or after** it. That took retail contradictions from **70
|
|
626
|
+
to 19** — and all three cases above were in the 70, and all three had been read by
|
|
627
|
+
hand before the data was blamed on the read path.
|
|
628
|
+
|
|
629
|
+
**And a record that never saw the outcome is silence, not disagreement.** If nothing
|
|
630
|
+
is recorded at or after the turn, the order had no chance to reflect the change, so
|
|
631
|
+
a value matching nothing is *unverifiable*. A customer who says "charge the
|
|
632
|
+
difference to my credit card" and then ends the conversation leaves no trace, and
|
|
633
|
+
calling the read path wrong for agreeing with them measures nothing. That split
|
|
634
|
+
took the 19 down to 15 — and it applies to the airline corpus too, where it moved
|
|
635
|
+
contradictions from 5 to **2**.
|
|
636
|
+
|
|
432
637
|
**A partial address is not a contradiction.** "464 Oak Street, Suite 664" is
|
|
433
|
-
correct, and the record also holds "San Diego, CA 92135".
|
|
434
|
-
corroborated addresses stopped at the street line; they
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
`delivery_address` slot exists to capture a *requested* change, so the moment a
|
|
442
|
-
conversation is about changing something, the record is behind by definition.
|
|
443
|
-
Reading the contradicted turns, they are that shape — "I would like to change the
|
|
444
|
-
shipping address to 123 Oak Street", "you can use my PayPal account for any price
|
|
445
|
-
differences along the way" — and the read path is tracking what the customer just
|
|
446
|
-
asked for. They stay counted as contradicted, because excluding them would flatter
|
|
447
|
-
the number, but they are the record answering a different question rather than the
|
|
448
|
-
tracker misreading one.
|
|
449
|
-
|
|
450
|
-
**No split of those 70 is published.** An attempt to classify them by regex was
|
|
451
|
-
wrong on turns that had already been read by hand, and a precise-looking number
|
|
452
|
-
from an unreliable classifier is worse than none. The tool prints a hand-read
|
|
453
|
-
sample and says so.
|
|
638
|
+
correct, and the record also holds "San Diego, CA 92135". 25 of the 51
|
|
639
|
+
corroborated addresses stopped at the street line; they count as corroborated and
|
|
640
|
+
the incompleteness is reported beside the rate.
|
|
641
|
+
|
|
642
|
+
The two travel contradictions that remain are the irreducible ones: a cabin class
|
|
643
|
+
taken off a baggage-allowance table, and one turn where the agent says Economy
|
|
644
|
+
while the record says `basic_economy`. The second is not a pattern bug at all — the
|
|
645
|
+
read path faithfully recorded what the agent said.
|
|
454
646
|
|
|
455
647
|
#### A ground truth that is not a person: the booking database
|
|
456
648
|
|
|
@@ -472,9 +664,9 @@ of three buckets, and reports them apart:
|
|
|
472
664
|
|
|
473
665
|
| | count | share |
|
|
474
666
|
|---|---|---|
|
|
475
|
-
| corroborated by the record | 557 |
|
|
476
|
-
| **contradicted by the record** | **
|
|
477
|
-
| unverifiable (no record for that slot) |
|
|
667
|
+
| corroborated by the record | 557 | **99.6%** |
|
|
668
|
+
| **contradicted by the record** | **2** | **0.4%** |
|
|
669
|
+
| unverifiable (no record for that slot) | 56 | 9.1% |
|
|
478
670
|
|
|
479
671
|
The rate is over corroborated + contradicted. The unverifiable column is **not** a
|
|
480
672
|
pass — it is extractions this check says nothing about, and folding it in would
|
|
@@ -646,6 +838,51 @@ to infer from tool output is a large part of why — it holds no value on the
|
|
|
646
838
|
consecutive agent actions, which is a definition chosen here and not given by
|
|
647
839
|
the data.
|
|
648
840
|
|
|
841
|
+
#### The one staleness test that needs no edit signal
|
|
842
|
+
|
|
843
|
+
The reason above is that a rate needs a ground truth for *when the file changed*.
|
|
844
|
+
But there is a weaker question with a free answer, and it is the one that
|
|
845
|
+
actually matters: **does the read path's value match the agent's most recent file
|
|
846
|
+
statement?** If it does, the tracker is not stale, whatever the agent was doing
|
|
847
|
+
in between. The vendored coding transcript answers it outright — 18 distinct
|
|
848
|
+
file statements, in order:
|
|
849
|
+
|
|
850
|
+
```
|
|
851
|
+
memset.py -> reproduce.py -> memset.py -> lexicon/lexicon/cli.py
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
The read path reproduces that sequence exactly, including the round trip: the
|
|
855
|
+
agent creates `reproduce.py`, works in it, then goes back to `cli.py`, and the
|
|
856
|
+
read path follows it out of `reproduce.py` and back. Its final value equals the
|
|
857
|
+
agent's final statement, so **the read path is not stale, and it is a faithful
|
|
858
|
+
last-statement tracker.** 81% of its matches come from the agent's speech and
|
|
859
|
+
19% from tool output; it takes the final one either way.
|
|
860
|
+
|
|
861
|
+
Two things this does *not* license. It does not produce a staleness **rate**,
|
|
862
|
+
because a rate needs the edit signal the corpus lacks. And it does not validate
|
|
863
|
+
the confounded 44.9%: one trajectory agreeing with itself is not a measurement.
|
|
864
|
+
|
|
865
|
+
The turn-by-turn version **is** available in the 14B capture, which records
|
|
866
|
+
`declared` on every turn — and it is still not measurable, for a reason worth
|
|
867
|
+
writing down. The capture has 6 transcripts; the vendored agent transcript is
|
|
868
|
+
**one** file. Aligning each transcript's model declarations to a matching agent
|
|
869
|
+
prefix requires slicing that one file six ways, which invents six trajectories
|
|
870
|
+
out of one. I did it, got 8 apparent disagreements, and discarded all 8: they
|
|
871
|
+
were artifacts of the slicing, and reporting them would have been the exact
|
|
872
|
+
failure this section is about. **A measurement that needs data the corpus does
|
|
873
|
+
not have is not a weak measurement, it is a fabricated one.**
|
|
874
|
+
|
|
875
|
+
One oddity to leave alone on purpose. Half the values are doubled
|
|
876
|
+
(`lexicon/lexicon/cli.py`, where the first `lexicon` is the conda prefix and the
|
|
877
|
+
second is the repo). That is the **agent's own text**, faithfully captured, and
|
|
878
|
+
normalising it would be fitting the tool to a transcript's confusion. Recorded
|
|
879
|
+
here so it is not later "fixed".
|
|
880
|
+
|
|
881
|
+
**Where this leaves the dimension.** Not unmeasured any more — measured, once,
|
|
882
|
+
and negative: the tracker does not go stale on the trajectory available. Still
|
|
883
|
+
no rate, and still n=1 trajectory. That is a better answer than the one it
|
|
884
|
+
replaces, and it is still not a headline.
|
|
885
|
+
|
|
649
886
|
**The two "confirmed errors" this project carried were mislabelled.** Both were
|
|
650
887
|
recorded as `current_file` extractions judged incorrect because the turn's earlier
|
|
651
888
|
prose discussed a different file. Re-reading the turns against the doctrine the
|
|
@@ -1082,10 +1319,18 @@ measurement for a guess:
|
|
|
1082
1319
|
```json
|
|
1083
1320
|
"values": {
|
|
1084
1321
|
"current_file": "(?:[\\w][\\w./-]*\\.(?:py|js|ts|tsx|jsx|go|rs|java|rb|cpp|hpp))",
|
|
1085
|
-
"failing_test": "(?:
|
|
1322
|
+
"failing_test": "(?:[\\w./-]*test[\\w./-]*\\.py)"
|
|
1086
1323
|
}
|
|
1087
1324
|
```
|
|
1088
1325
|
|
|
1326
|
+
The second line is worth pausing on, because it is a contract that accepts
|
|
1327
|
+
`latest_file.py` — `la`+`test` — and `my_test_helper.py`. The `test_` anchoring
|
|
1328
|
+
this slot used to have is gone, because the pattern that reaches it from speech
|
|
1329
|
+
no longer requires a `test_` prefix. Both junk cases are recorded as residual
|
|
1330
|
+
false accepts. The trade is deliberate: a contract that rejects 2 of 49 real
|
|
1331
|
+
values and is unreachable buys less than one that accepts everything the read
|
|
1332
|
+
path can reach and admits its 2 false accepts in the open.
|
|
1333
|
+
|
|
1089
1334
|
Deriving them mattered more than the hand-written versions looked. The old
|
|
1090
1335
|
`logistics.delivery_address` was `[\w\s.,'/-]+` and `payment_method` was
|
|
1091
1336
|
`[\w ]+`, so on a 48-value set of wrong-shaped declarations — `None`, error
|