harness-everything 0.47.1 → 0.47.3
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/ci.yml +14 -0
- package/CHANGELOG.md +18 -0
- package/benchmarks/issue290-pilot/README.md +199 -0
- package/benchmarks/issue290-pilot/analyze.py +299 -0
- package/benchmarks/issue290-pilot/analyze.test.py +146 -0
- package/benchmarks/issue290-pilot/check.py +19 -0
- package/benchmarks/issue290-pilot/evidence/archive.json +7 -0
- package/benchmarks/issue290-pilot/evidence/group-summary.json +684 -0
- package/benchmarks/issue290-pilot/evidence/inventory.json +3728 -0
- package/benchmarks/issue290-pilot/evidence/ledger.json +4644 -0
- package/benchmarks/issue290-pilot/evidence/replay/pairs.json +482 -0
- package/benchmarks/issue290-pilot/evidence/replay/provenance.json +24 -0
- package/benchmarks/issue290-pilot/evidence/replay/tables.md +37 -0
- package/benchmarks/issue290-pilot/evidence.tar.gz +0 -0
- package/benchmarks/issue290-pilot/export.py +110 -0
- package/benchmarks/issue290-pilot/export.test.py +65 -0
- package/benchmarks/issue290-pilot/fable-supplement.py +55 -0
- package/benchmarks/issue290-pilot/fixture.test.py +28 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/instructions.md +19 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/package.json +29 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/simple-linked-list.js +44 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/simple-linked-list.spec.js +131 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/LICENSE +7 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/cases.json +7 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/find_in_sorted.py +31 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/test_find_in_sorted.py +15 -0
- package/benchmarks/issue290-pilot/frozen-documents.json +41 -0
- package/benchmarks/issue290-pilot/grader.cjs +43 -0
- package/benchmarks/issue290-pilot/grader.test.cjs +10 -0
- package/benchmarks/issue290-pilot/hook.test.cjs +27 -0
- package/benchmarks/issue290-pilot/host-hook.cjs +35 -0
- package/benchmarks/issue290-pilot/metrics.test.py +131 -0
- package/benchmarks/issue290-pilot/preflight/amendment.md +32 -0
- package/benchmarks/issue290-pilot/preflight/environment.json +15 -0
- package/benchmarks/issue290-pilot/preflight/fable-amendment.md +20 -0
- package/benchmarks/issue290-pilot/preflight/fable-frozen-files.json +4 -0
- package/benchmarks/issue290-pilot/preflight/fable-infrastructure-amendment.md +27 -0
- package/benchmarks/issue290-pilot/preflight/fable-retry-frozen-files.json +4 -0
- package/benchmarks/issue290-pilot/preflight/fable-supplement-v1.py +47 -0
- package/benchmarks/issue290-pilot/preflight/frozen-files.json +18 -0
- package/benchmarks/issue290-pilot/preflight/graders.json +67 -0
- package/benchmarks/issue290-pilot/pricing-assumptions.json +29 -0
- package/benchmarks/issue290-pilot/protocol.json +32 -0
- package/benchmarks/issue290-pilot/read_metrics.py +166 -0
- package/benchmarks/issue290-pilot/replay.py +167 -0
- package/benchmarks/issue290-pilot/reporting.py +82 -0
- package/benchmarks/issue290-pilot/run.py +262 -0
- package/benchmarks/issue290-pilot/sources.json +10 -0
- package/benchmarks/issue290-pilot/verification/analyzer-tests.log +5 -0
- package/benchmarks/issue290-pilot/verification/final-gates.json +58 -0
- package/benchmarks/issue290-pilot/verification/fixture-tests.log +5 -0
- package/benchmarks/issue290-pilot/verification/gates.json +58 -0
- package/benchmarks/issue290-pilot/verification/npm-audit.json +84 -0
- package/benchmarks/issue290-pilot/verification/offline-correction.json +59 -0
- package/benchmarks/issue290-pilot/verification/post-report/helper-tests.log +22 -0
- package/benchmarks/issue290-pilot/verification/post-report/test-consistency.log +626 -0
- package/benchmarks/issue290-pilot/verification/post-report/test-references.log +6 -0
- package/benchmarks/issue290-pilot/verification/post-report/waza-tokens.log +710 -0
- package/benchmarks/issue290-pilot/verification/test-collision.log +28 -0
- package/benchmarks/issue290-pilot/verification/test-consistency.log +28 -0
- package/benchmarks/issue290-pilot/verification/test-docs-capabilities.log +131 -0
- package/benchmarks/issue290-pilot/verification/test-mechanism.log +1829 -0
- package/benchmarks/issue290-pilot/verification/test-mutations.log +16 -0
- package/benchmarks/issue290-pilot/verification/test-platform-compatibility.log +9 -0
- package/benchmarks/issue290-pilot/verification/test-plugin-openai.log +6 -0
- package/benchmarks/issue290-pilot/verification/test-plugin-submission.log +5 -0
- package/benchmarks/issue290-pilot/verification/test-references.log +6 -0
- package/benchmarks/issue290-pilot/verification/test-release.log +59 -0
- package/benchmarks/issue290-pilot/verification/test-repo-contract.log +5 -0
- package/benchmarks/issue290-pilot/verification/test-routing-invariants.log +785 -0
- package/benchmarks/issue290-pilot/verification/test-routing-skills.log +28 -0
- package/benchmarks/issue290-pilot/verification/test.log +3373 -0
- package/benchmarks/issue290-pilot/verification/verification-report.md +76 -0
- package/benchmarks/issue290-pilot/verification/waza-tokens.log +710 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/gates.json +26 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-collision.log +8 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-consistency.log +626 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-mechanism.log +1693 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-release.log +38 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-routing-skills.log +44 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test.log +3253 -0
- package/ci/lib/opencode-verification-loader.js +24 -0
- package/ci/mechanism-2p-opencode-v2-entrypoint.test.js +8 -1
- package/ci/mechanism-325-f3-derived-views.test.js +42 -2
- package/ci/mechanism-338-opencode-async.test.js +229 -0
- package/ci/mechanism-341-opencode-lane.test.js +14 -3
- package/ci/mechanism-36-verify-timeout.test.js +7 -168
- package/docs/platform-capabilities.md +8 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/doc-check.js +5 -4
- package/hooks/scripts/lib/doc-reconciliation-provider.js +1 -1
- package/opencode-plugin/README.md +28 -30
- package/opencode-plugin/index.mjs +249 -112
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-provider.js +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-check.js +5 -4
- package/sbom.cdx.json +7 -7
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "harness-everything",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
13
|
-
"version": "0.47.
|
|
13
|
+
"version": "0.47.3",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Miya Daniel"
|
|
16
16
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-everything",
|
|
3
3
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
4
|
-
"version": "0.47.
|
|
4
|
+
"version": "0.47.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Miya Daniel"
|
|
7
7
|
},
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -9,6 +9,18 @@ on:
|
|
|
9
9
|
workflow_call:
|
|
10
10
|
|
|
11
11
|
jobs:
|
|
12
|
+
pilot-offline-evidence:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- uses: actions/setup-node@v7
|
|
17
|
+
with:
|
|
18
|
+
node-version: 24
|
|
19
|
+
- name: Install helper dependencies
|
|
20
|
+
run: npm ci
|
|
21
|
+
- name: Verify pilot instrumentation, sanitizer and offline replay
|
|
22
|
+
run: python3 benchmarks/issue290-pilot/check.py
|
|
23
|
+
|
|
12
24
|
doc-governance:
|
|
13
25
|
runs-on: ubuntu-latest
|
|
14
26
|
steps:
|
|
@@ -106,6 +118,8 @@ jobs:
|
|
|
106
118
|
node-version: 24
|
|
107
119
|
- name: Install dependencies for regression suite
|
|
108
120
|
run: npm ci
|
|
121
|
+
- name: Verify OpenCode async process fixtures before the full suite
|
|
122
|
+
run: node ci/mechanism-338-opencode-async.test.js
|
|
109
123
|
- name: Run hermetic self-regression suite
|
|
110
124
|
run: npm test
|
|
111
125
|
- name: Run focused mutation checks
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.47.3](https://github.com/dyphn1/Harness-everything/compare/v0.47.2...v0.47.3) (2026-10-31)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **docs:** preserve exact file identities in derived comparisons ([8bc3ddb](https://github.com/dyphn1/Harness-everything/commit/8bc3ddbac38eaffa7495e70922dc1240f9aa63eb))
|
|
10
|
+
* **docs:** repin shipped verifier after exact file identity fix ([c24d0f1](https://github.com/dyphn1/Harness-everything/commit/c24d0f100ce392edd02275aa06b58f141ade6cdd))
|
|
11
|
+
* **opencode:** invalidate idle waiters on session reset ([41b2663](https://github.com/dyphn1/Harness-everything/commit/41b2663f66edbe77188c3ca9fa3b0add6f7ab76d))
|
|
12
|
+
* **opencode:** stream verification asynchronously within a shared budget ([1470247](https://github.com/dyphn1/Harness-everything/commit/1470247249548ec6ccf2acaf97463824e0268be4)), closes [#338](https://github.com/dyphn1/Harness-everything/issues/338)
|
|
13
|
+
|
|
14
|
+
## [0.47.2](https://github.com/dyphn1/Harness-everything/compare/v0.47.1...v0.47.2) (2026-10-31)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **benchmarks:** close offline pilot evidence review gaps ([fa3bee6](https://github.com/dyphn1/Harness-everything/commit/fa3bee6b948265a7203a1414a1a05dab9d6d6a58))
|
|
19
|
+
* **benchmarks:** detect explicit TDD quality gate invocations ([53c0181](https://github.com/dyphn1/Harness-everything/commit/53c018112c01ebb5b270aeb14fdce5528e195e1a)), closes [#318](https://github.com/dyphn1/Harness-everything/issues/318)
|
|
20
|
+
* **benchmarks:** make offline attribution independent of filesystem order ([998ff4b](https://github.com/dyphn1/Harness-everything/commit/998ff4b2ad31c0e7abef1dda7c74db53faec76c8))
|
|
21
|
+
* **benchmarks:** restore pinned ESM replay fixture ([664da1e](https://github.com/dyphn1/Harness-everything/commit/664da1e4f32251bd9e4c79d78fef5152cd9b86cd))
|
|
22
|
+
|
|
5
23
|
## [0.47.1](https://github.com/dyphn1/Harness-everything/compare/v0.47.0...v0.47.1) (2026-10-31)
|
|
6
24
|
|
|
7
25
|
### Bug Fixes
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Issue #290 pilot report: active-step reference disclosure
|
|
2
|
+
|
|
3
|
+
**Status:** pilot only. Issue #290 stays open. This report makes no benefit claim, no quality non-inferiority claim, and no formal efficacy claim.
|
|
4
|
+
|
|
5
|
+
Question (from [protocol.json](protocol.json)): does active-step reference disclosure reduce observed reference reads and execution overhead without losing required workflow behavior?
|
|
6
|
+
|
|
7
|
+
## Conclusion
|
|
8
|
+
|
|
9
|
+
1. **Descriptive lookup observation under an unverified instructed-eager intervention.** The corrected parent-stream classifier attributes 37,914 reference bytes to A and 7,142 to B per model (two paired episodes; −81.16%). A was instructed to read eagerly; that intervention was not structurally verified. Both arms received experiment text. These numbers do not establish a causal effect or normal-product savings, and measure bytes rather than tokens.
|
|
10
|
+
2. **That did not produce uniform cost savings.** Paired list-price input-cost deltas (arm B against arm A, same family, known costs) range from −15.9% to +93.6%, with the true-Fable Haiku family at +49.3%. The Luna true-Fable cost comparison is inconclusive because all four of its episodes have no whole-run cost.
|
|
11
|
+
3. **Artifact pass is not semantic completion.** 24 of 32 formal and supplement model episodes produced a passing artifact. The eight failures are four Haiku lookups that added prose outside the required JSON, one Luna staged-list failure, and three Luna true-Fable budget failures. Several workflow records are incomplete (see [Semantic evidence](#semantic-evidence)). Two repeats per cell cannot establish a ≤2 percentage-point non-inferiority bound.
|
|
12
|
+
4. **Budget deviation.** The aggregate output-token cap (500,000, measured as the runner's `usage.output_tokens`, which includes thinking) was exceeded by at least 8,416 tokens. The overshoot happened in the final Claude retry episode, which started under the cap. See [Budgets, timing and cost](#budgets-timing-and-cost).
|
|
13
|
+
5. **Recommendation.** Stop model spending for this pilot. Keep #290 open. Resolve the focused correctness, documentation and telemetry issues listed below before any formal expansion to at least five paired repeats per family.
|
|
14
|
+
|
|
15
|
+
## Scope and provenance
|
|
16
|
+
|
|
17
|
+
- **Source revision:** `ac519fa204ec90feb5067bc2525381b8820d045e`. Protocol registered 2026-10-14 before model calls.
|
|
18
|
+
- **Arms:** A = same canonical router and documents, plus an experiment-only eager document-read timing instruction. B = experiment text requesting active-step disclosure. Neither arm is an unmodified natural-policy control. Eager compliance remains unverified.
|
|
19
|
+
- **Models:** `gpt-6-luna` (Codex CLI 0.162.1, effort high) and `claude-haiku-5-5` (Claude CLI 2.1.299, effort high). Subscription authentication; no API keys were created or read.
|
|
20
|
+
- **Host integration:** the normal native UserPromptSubmit hook invoked the unchanged canonical router. Codex used explicit native hooks with its documented automation trust option; Claude used project-scoped settings. This is not full-plugin parity.
|
|
21
|
+
- **Public selection:** taken from `0b221bf` on `origin/fix/290-minimal-fable-workflow`, not `main`. Fixtures are pinned public tasks used through adapters:
|
|
22
|
+
- QuixBugs `find_in_sorted` at `4257f44`, seven pinned upstream JSON cases through Python assertions.
|
|
23
|
+
- Aider Polyglot `simple-linked-list` at `7e0611e`, 23 assertions through an explicit matcher adapter.
|
|
24
|
+
- This is not an official leaderboard score. TerminalBench was not run.
|
|
25
|
+
- **Workloads and timeouts:** `profile-lookup` (synthetic Harness contract probe, 180 s); `quixbugs-find-in-sorted` (repair, 300 s); `polyglot-simple-linked-list-staged` (three ordered dependent stages, 600 s).
|
|
26
|
+
- **Fable supplement:** the same linked-list prompt prefixed with `Use Fable staged execution.`, which the tier router maps to `tier3/fable-staged`. It deliberately requests the topology this issue must preserve; it is not a neutral whole-Harness benchmark.
|
|
27
|
+
- **Order deviation:** repeat 1 runs AB and repeat 2 BA within each model/workload block. The preregistration also said “alternating model blocks”, but `run.py` groups models within workloads. That planned alternation was not implemented. The immutable protocol is preserved; provider cache residency was uncontrolled.
|
|
28
|
+
- **Environment:** macOS 26.6.2 arm64; agent Node v24.14.1 observed; inspection shell Node v25.9.0 ([preflight/environment.json](preflight/environment.json)).
|
|
29
|
+
|
|
30
|
+
## Episode accounting
|
|
31
|
+
|
|
32
|
+
40 attempts were retained. None were removed.
|
|
33
|
+
|
|
34
|
+
| Set | Episodes | Outcome |
|
|
35
|
+
|---|---:|---|
|
|
36
|
+
| Initial formal (protocol) | 24 | 3 workloads × 2 arms × 2 repeats × 2 models |
|
|
37
|
+
| True-Fable supplement, Luna | 4 | Ran. 1 artifact pass; 3 agent-budget failures (600 s cap) |
|
|
38
|
+
| True-Fable supplement, Haiku (original) | 4 | Pre-model startup failures: `hook-settings.json` missing. Infrastructure, outside the behavioral denominator |
|
|
39
|
+
| True-Fable supplement, Haiku (symmetric retry) | 4 | Ran after the fixture prerequisite fix (#314). 4 artifact passes |
|
|
40
|
+
| Preflight | 4 | Two probe sets, one per model each. The initial Haiku probe hit the 3-turn smoke cap, a preflight configuration failure. Retained, not formal |
|
|
41
|
+
|
|
42
|
+
Total: 32 formal model episodes + 4 preflight + 4 pre-model infrastructure launches = 40.
|
|
43
|
+
|
|
44
|
+
## Results
|
|
45
|
+
|
|
46
|
+
The following tables are regenerated by `python3 benchmarks/issue290-pilot/replay.py`; `--check` verifies the committed ledger, groups, pair records and these exact table bytes. They replace the original whole-shell-output reference counts.
|
|
47
|
+
|
|
48
|
+
<!-- BEGIN REPLAY TABLES -->
|
|
49
|
+
<!-- Generated by replay.py; parent-stream content attribution only. -->
|
|
50
|
+
| Family | Model | Pass / quality n | Excluded attempts |
|
|
51
|
+
|---|---|---:|---:|
|
|
52
|
+
| True Fable | claude | 4/4 | 4 |
|
|
53
|
+
| True Fable | codex | 1/4 | 0 |
|
|
54
|
+
| polyglot-simple-linked-list-staged | claude | 4/4 | 0 |
|
|
55
|
+
| polyglot-simple-linked-list-staged | codex | 3/4 | 0 |
|
|
56
|
+
| profile-lookup | claude | 0/4 | 0 |
|
|
57
|
+
| profile-lookup | codex | 4/4 | 0 |
|
|
58
|
+
| quixbugs-find-in-sorted | claude | 4/4 | 0 |
|
|
59
|
+
| quixbugs-find-in-sorted | codex | 4/4 | 0 |
|
|
60
|
+
|
|
61
|
+
### Attributable reference bytes (lower bound)
|
|
62
|
+
|
|
63
|
+
| Family | Model | Included / candidate pairs | A | B | Delta |
|
|
64
|
+
|---|---|---:|---:|---:|---:|
|
|
65
|
+
| True Fable | claude | 2/2 | 28933 | 30824 | +6.5% |
|
|
66
|
+
| True Fable | codex | 2/2 | 52706 | 18190 | -65.5% |
|
|
67
|
+
| polyglot-simple-linked-list-staged | claude | 2/2 | 8315 | 6130 | -26.3% |
|
|
68
|
+
| polyglot-simple-linked-list-staged | codex | 2/2 | 44046 | 6132 | -86.1% |
|
|
69
|
+
| profile-lookup | claude | 2/2 | 37914 | 7142 | -81.2% |
|
|
70
|
+
| profile-lookup | codex | 2/2 | 37914 | 7142 | -81.2% |
|
|
71
|
+
| quixbugs-find-in-sorted | claude | 2/2 | 30156 | 6247 | -79.3% |
|
|
72
|
+
| quixbugs-find-in-sorted | codex | 2/2 | 30156 | 7731 | -74.4% |
|
|
73
|
+
|
|
74
|
+
### List-price input cost (frozen telemetry)
|
|
75
|
+
|
|
76
|
+
| Family | Model | Included / candidate pairs | A | B | Delta |
|
|
77
|
+
|---|---|---:|---:|---:|---:|
|
|
78
|
+
| True Fable | claude | 2/2 | 0.042330 | 0.063211 | +49.3% |
|
|
79
|
+
| True Fable | codex | 0/2 | unknown | unknown | unknown |
|
|
80
|
+
| polyglot-simple-linked-list-staged | claude | 2/2 | 0.022892 | 0.034584 | +51.1% |
|
|
81
|
+
| polyglot-simple-linked-list-staged | codex | 2/2 | 0.031545 | 0.031368 | -0.6% |
|
|
82
|
+
| profile-lookup | claude | 2/2 | 0.009629 | 0.008103 | -15.9% |
|
|
83
|
+
| profile-lookup | codex | 2/2 | 0.003494 | 0.004594 | +31.5% |
|
|
84
|
+
| quixbugs-find-in-sorted | claude | 2/2 | 0.016405 | 0.021596 | +31.6% |
|
|
85
|
+
| quixbugs-find-in-sorted | codex | 2/2 | 0.013630 | 0.026382 | +93.6% |
|
|
86
|
+
<!-- END REPLAY TABLES -->
|
|
87
|
+
|
|
88
|
+
Quality excludes the four pre-model startup failures and preflight. Genuine agent budget failures remain failures. Each numeric metric has its own included/excluded count in [group-summary.json](evidence/group-summary.json); missing whole-run costs remain `null`. Pair deltas use only pairs with both measurements, and an all-unknown cost cell remains unknown.
|
|
89
|
+
|
|
90
|
+
**Reference metric boundary:** declared-bundle filesystem-access candidates, total shell-output bytes, content-attributed reference bytes, and uncertain/mixed bytes are separate fields. Full-file matches and distinctive contiguous partial lines count toward a conservative lower bound; short fragments, transformed output (`cat doc | wc -l`), numbered/modified text, undeclared files and unobserved child reads do not become claimed content bytes. Unknown transformations are not assigned zero physical activity.
|
|
91
|
+
|
|
92
|
+
**A's prior zero-read anomaly:** Haiku initial staged-list A2 now has 3,065 attributable bytes, from a compound TDD/verification read missed by the old whole-command veto. This resolves that zero as a counter defect. It still does not prove that A read every requested Fable reference. Some prior B>A results change after removing non-reference/mixed bytes; true-Fable Haiku remains B>A. The lookup −81.16% remains only a descriptive observation under the unverified intervention.
|
|
93
|
+
|
|
94
|
+
## Semantic evidence
|
|
95
|
+
|
|
96
|
+
- **Artifacts are not semantic completion.** Passing an artifact check does not show that required workflow steps ran.
|
|
97
|
+
- **Claude true-Fable stage records remained `planned`** in both B episodes and are absent in the A episodes, even though all four artifacts passed.
|
|
98
|
+
- **Luna true-Fable step records stayed `pending`** in all four episodes, while the stage records show `pass` in those episodes (one `fail` in B1).
|
|
99
|
+
- **Required-binding counts are not unresolved-binding counts.** The analyzer's `required_bindings_unresolved` field counts every binding record whose status is not `loaded`, including per-step `pending` placeholders. It is retained in the ledger but not used as a headline.
|
|
100
|
+
- **TDD invocation (#318 and identity correction, 2026-10-31).** `tdd_quality_gate_name_matched_invocation` explicitly means a conservative name/path-matched invocation, not established repository identity or a successful verdict. Only supported relative gate paths and npm gate names count; unrelated `quality-gate.js` basenames, npm `--prefix`/workspace relocations (including options after the script name), quotes, comments, help and dry-run forms do not. No archived command changes its already-corrected Boolean value: all 40 are false. The old `tdd_quality_gate_executed` name survives only in immutable historical archive snapshots.
|
|
101
|
+
- **Child dispatch and accounting (Luna true Fable).** All four Luna true-Fable parent transcripts contain `spawn_agent` dispatch records, but the CLI stream does not expose worker usage or model IDs. The whole cost is therefore unknown for all four, and parent-only cost is retained separately (#315). Claude worker logs were normalized, and the child model and effort were verified as `haiku-5-5` / `high`.
|
|
102
|
+
|
|
103
|
+
## Budgets, timing and cost
|
|
104
|
+
|
|
105
|
+
### Wall time
|
|
106
|
+
|
|
107
|
+
- 5,260.8 s summed parent wall time across the 32 formal and supplement model episodes. Child waits are included; parallel durations are not added. This is under the 7,200 s aggregate budget.
|
|
108
|
+
- Excluded: four pre-model launches (4.1 s) and preflight (72.3 s).
|
|
109
|
+
|
|
110
|
+
### Output tokens (deviation)
|
|
111
|
+
|
|
112
|
+
- The runner enforces `aggregate_output_token_cap` (500,000) against `usage.output_tokens`, which includes thinking. The checks run before each episode starts ([run.py:252](run.py), [fable-supplement.py:51](fable-supplement.py)).
|
|
113
|
+
- The 32 model episodes sum to **at least 508,416** under that metric: final usage aggregates where present, plus 109,190 partial request sums for the three interrupted Luna true-Fable episodes. The total is a lower bound because unobserved worker output is excluded.
|
|
114
|
+
- The final Claude retry episode (`claude polyglot-simple-linked-list-staged`, arm A, repeat 2) started at a cumulative 485,478 and ended at 508,416, exceeding the cap by at least 8,416 (1.7%). The pre-start check admitted it, so the overshoot was not blocked. The protocol says no higher limit was authorized. Report this as a deviation.
|
|
115
|
+
- The protocol's per-episode cap of 80,000 output tokens was not exceeded. The largest observed episode total was 48,588, a partial count in an interrupted episode.
|
|
116
|
+
- The protocol text does not use the word "visible". Under a visible-only reading (subtracting thinking tokens where exposed), the total is about 165,000, well under the cap. That is not the metric the runner enforced, and it is not used to claim compliance.
|
|
117
|
+
- No further model calls were made after this point.
|
|
118
|
+
|
|
119
|
+
### Cost
|
|
120
|
+
|
|
121
|
+
- **Known token-only subtotal:** 0.50788911 USD across the 28 completed, priced formal and supplement episodes (input-only component 0.32976261 USD).
|
|
122
|
+
- **Unknown:** the whole cost of the four Luna true-Fable episodes. The worker usage that would be needed is not exposed in the parent CLI stream.
|
|
123
|
+
- **Preflight:** excluded from formal cost comparisons; the interrupted smoke episode has no asserted whole-run cost.
|
|
124
|
+
- **Basis:** historical price assumptions, not subscription billing or verified current prices. [pricing-assumptions.json](pricing-assumptions.json) freezes the original constants. The original report named the following provider URLs, but no immutable provider-page capture was retained; the Haiku `multiplier=5` is an unverified original-analyzer assumption:
|
|
125
|
+
- Luna: input 0.10, cache read 0.01, cache write 0.125, output 0.50 USD per 1M tokens; higher request-tier modifiers above 272k input tokens ([source](https://developers.openai.com/api/docs/models/gpt-6-luna)).
|
|
126
|
+
- Haiku 5.5: input 0.10, output 0.50, cache write 5 min 0.125, cache write 1 h 0.20, cache read 0.01 USD per 1M tokens; higher request-tier modifiers above 100k input tokens ([source](https://platform.claude.com/docs/en/about-claude/pricing)).
|
|
127
|
+
- **Excluded from the estimate:** Codex hosted tool fees, setup, research and orchestrator expense, and unobserved child usage. Native aggregate output includes thinking tokens, which are not double-counted.
|
|
128
|
+
|
|
129
|
+
## Exclusions and amendments
|
|
130
|
+
|
|
131
|
+
All amendments were registered before the affected model calls:
|
|
132
|
+
|
|
133
|
+
- [preflight/amendment.md](preflight/amendment.md): corrected the preflight smoke cap to the formal 35-turn cap after the initial Haiku probe; froze the fixture adaptations; defined the lookup rubric (six exact fields and JSON-only output after the hidden turn label).
|
|
134
|
+
- [preflight/fable-amendment.md](preflight/fable-amendment.md): added the genuine Fable supplement and the linked-worktree isolation it requires; shared the original budgets.
|
|
135
|
+
- [preflight/fable-infrastructure-amendment.md](preflight/fable-infrastructure-amendment.md): recorded the four Claude startup failures as infrastructure; added the fixture prerequisite check and fail-fast on absent router attribution; registered one symmetric retry for the two Claude pairs.
|
|
136
|
+
|
|
137
|
+
Exclusion rules:
|
|
138
|
+
|
|
139
|
+
- Four Claude true-Fable startups failed before model execution (`Settings file not found: .../hook-settings.json`). They are retained, classified as infrastructure, and kept outside the behavioral denominator (#314).
|
|
140
|
+
- The initial Haiku preflight probe is retained as a preflight configuration failure and is not a formal observation.
|
|
141
|
+
- The three Luna true-Fable budget failures are counted as failures. They were not excluded and no outcome-selected rerun was made.
|
|
142
|
+
- No Luna outcome was rerun.
|
|
143
|
+
|
|
144
|
+
## Known limitations
|
|
145
|
+
|
|
146
|
+
- Two repeats per model/workload/arm, one public task per family. This is not the issue's ≥5 paired repeats.
|
|
147
|
+
- Quality is not shown to be non-inferior. Artifact pass is not semantic completion.
|
|
148
|
+
- Cache residency was uncontrolled. Order effects are present (AB then BA). Some repo gates ran concurrently with early repair trials, so latency is descriptive and confounded.
|
|
149
|
+
- Costs reproduce historical assumptions, including an unverified Haiku large-context multiplier. Four Luna true-Fable whole costs are unknown. Private-host attribution cannot be independently re-fetched by replay; retained sanitized telemetry is explicitly frozen input.
|
|
150
|
+
- Host integration is an explicit project/CLI adapter, not full-plugin parity. Global scope, npm-package installation, and other host versions are not covered.
|
|
151
|
+
- Content attribution is a parent-stream lower bound. Native/numbered partial reads, unknown transformations and short coincidental matches stay uncertain. No new model run or efficacy claim follows from the corrected counters.
|
|
152
|
+
|
|
153
|
+
## Issues
|
|
154
|
+
|
|
155
|
+
| Issue | Title (short) | Relation to this PR |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| [#290](https://github.com/dyphn1/Harness-everything/issues/290) | Validate demand-driven reference loading | Parent. Refs only; stays open |
|
|
158
|
+
| [#308](https://github.com/dyphn1/Harness-everything/issues/308) | Haiku profile lookup adds prose despite JSON-only request | Refs |
|
|
159
|
+
| [#309](https://github.com/dyphn1/Harness-everything/issues/309) | `fast-uri` dev dependency advisories | Refs. No dependency change; audit baseline recorded |
|
|
160
|
+
| [#310](https://github.com/dyphn1/Harness-everything/issues/310) | `workflow-disposition --help` exits 2 | Refs |
|
|
161
|
+
| [#311](https://github.com/dyphn1/Harness-everything/issues/311) | Loaded TDD steps skip the required quality report | Refs |
|
|
162
|
+
| [#312](https://github.com/dyphn1/Harness-everything/issues/312) | Staged linked-list repair invents a head-contract conflict | Refs. Actual nested `Element` bug, no source conflict |
|
|
163
|
+
| [#313](https://github.com/dyphn1/Harness-everything/issues/313) | Scalar controller output crashes the pilot analyzer | Fixes in this PR (regression test) |
|
|
164
|
+
| [#314](https://github.com/dyphn1/Harness-everything/issues/314) | Linked Fable fixture omits Claude hook settings | Fixes in this PR (fixture regression test, prerequisite checks, fail-fast) |
|
|
165
|
+
| [#315](https://github.com/dyphn1/Harness-everything/issues/315) | Codex private-rollout dispatch and usage accounting gap | Refs |
|
|
166
|
+
| [#316](https://github.com/dyphn1/Harness-everything/issues/316) | Blocked Fable recovery reminder vs plan rejection | Refs |
|
|
167
|
+
| [#317](https://github.com/dyphn1/Harness-everything/issues/317) | Legacy contract path in `execution-phases.md:32` vs `CONTRACT-FORMAT.md:66` | Refs |
|
|
168
|
+
| [#318](https://github.com/dyphn1/Harness-everything/issues/318) | TDD quality-gate flag matches `tdd:quality` text in binding evidence | Corrected in this PR; invocation scope documented |
|
|
169
|
+
|
|
170
|
+
## Reproducibility and evidence
|
|
171
|
+
|
|
172
|
+
- **Code and inputs (committed):** [run.py](run.py), [fable-supplement.py](fable-supplement.py), [analyze.py](analyze.py), [export.py](export.py), [grader.cjs](grader.cjs), [host-hook.cjs](host-hook.cjs), [protocol.json](protocol.json), [sources.json](sources.json), [preflight/](preflight/), [fixtures/](fixtures/).
|
|
173
|
+
- **Raw evidence (local, not committed):** `/tmp/issue290-formal-20261014`, `/tmp/issue290-fable-20261014`, `/tmp/issue290-fable-claude-retry-20261014`, `/tmp/issue290-host-preflight-20261014`, `/tmp/issue290-host-preflight2-20261014`, and the analyzer output `/tmp/issue290-final-audit`. Raw files include local paths and reasoning-bearing transcripts.
|
|
174
|
+
- **Sanitized archive:** [evidence.tar.gz](evidence.tar.gz), 1,824,930 bytes, SHA-256 `93bcb349c6ebbdcd3673641f3dc23148e73e87a0ec975d31bf31d2731d828723`. It was rebuilt from the committed [export.py](export.py). It holds 621 exported files plus `inventory.json`. [evidence/archive.json](evidence/archive.json) records the archive hash and count.
|
|
175
|
+
- **Inventory:** [evidence/inventory.json](evidence/inventory.json) lists the raw and exported SHA-256 for every exported file. Replay verifies all 621 exported hashes against committed inventory entries. The original author-side verification also recorded raw-file hash comparisons; the private raw files are not available for independent verification from this checkout.
|
|
176
|
+
- **Current derived evidence:** [ledger](evidence/ledger.json) (40 rows), [group summary](evidence/group-summary.json) (22 groups), [pairs](evidence/replay/pairs.json), [generated tables](evidence/replay/tables.md), and [provenance](evidence/replay/provenance.json). The archive retains the historical audits unchanged.
|
|
177
|
+
- **Sanitization:** removes home, source-checkout and temporary paths; the host name (read at runtime); the local username; email addresses; and thinking and signature fields. The committed verification logs and `preflight/graders.json` were passed through the same sanitizer. Token counts and hashes are retained. The export and final scan reject configured provider-token, assigned-secret, authorization-header and private-key shapes, including nested JSON/log strings. Synthetic positive/negative tests exercise these classes; no finite pattern scanner proves absence of every possible secret. Public metadata remains: the `author:` field in skill frontmatter and the public GitHub owner name in documentation. One truncated shell path fragment (`/Users/d`) remains; it contains no identifying information.
|
|
178
|
+
- **Independent references:**
|
|
179
|
+
- Polyglot: the original report recorded an independently authored reference in a private preflight temp directory; that private source is not a replay input. Its SHA-256 is `65c1189d1bf4a6956e0ba917fe0005b09dffea895329fbdf89dfaad54f8350eb`. The original local re-grade recorded 23/23, and the frozen skeleton (SHA-256 `f305a2ba…`, [preflight/frozen-files.json](preflight/frozen-files.json)) passed 0/23. Both `graders.json` entries name the same temp path, so the record does not show the hash directly; this hash is recorded here.
|
|
180
|
+
- QuixBugs: the upstream reference (SHA-256 `cce13769543e2972d66ea61c9d1f1751836c719abf336be8f50f2702d19477ea`) is recorded in [preflight/graders.json](preflight/graders.json).
|
|
181
|
+
- **Historical helper tests:** original report: `analyze.test.py` 4/4, `fixture.test.py` 1/1, `hook.test.cjs` PASS. The #318 correction extends the analyzer suite to 7 tests, including recomputation of the TDD flag from all 40 archived transcripts. `py_compile` and `node --check` pass. See [verification/verification-report.md](verification/verification-report.md) for the original run.
|
|
182
|
+
|
|
183
|
+
## Offline correction and acceptance (2026-10-31)
|
|
184
|
+
|
|
185
|
+
Run from the repository root, without provider CLIs or host session directories:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
python3 benchmarks/issue290-pilot/check.py
|
|
189
|
+
# Deliberately refresh derived expected values after reviewing an analyzer change:
|
|
190
|
+
python3 benchmarks/issue290-pilot/replay.py
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The `pilot-offline-evidence` CI job runs Python helper tests, Node hook/grader regressions, syntax checks, deterministic replay and the final export/archive credential scan. Replay verifies all 621 exported file hashes and frozen reference contents against episode manifests, reruns available artifact checks, recomputes content attribution and gate-name signals, and rebuilds per-metric groups and descriptive pairs. It compares derived JSON and README tables against committed expected values.
|
|
194
|
+
|
|
195
|
+
Frozen inputs are the sanitized archive's request-usage records, aggregate usage, effort and child-dispatch metadata. These are historical observations, not independent recovery of private host sessions. Missing worker identities/usage and interrupted final aggregates remain unknown; whole costs depending on them remain `null`. Replay cannot prove the host actually billed these amounts or revalidate unretained provider prices. Raw hashes are historical metadata: only exported hashes are verifiable from this checkout.
|
|
196
|
+
|
|
197
|
+
The original `evidence.tar.gz`, inventory, transcripts, protocol, budgets and model outcomes are unchanged. The new classifier is shared by `run.py` and `analyze.py`; grader equality no longer loses undefined/NaN/infinite values through JSON conversion, both independent graders retain timeouts, and both live runners stop before another trial if canonical router attribution is absent. This correction supplies deterministic tooling evidence, not a new behavioral study. Independent review and final-head CI remain required before merge.
|
|
198
|
+
|
|
199
|
+
Cross-host replay initially exposed filesystem-dependent stage-binding order. State traversal now sorts every matched path, with a negative control that reverses filesystem enumeration. The 26 Python tests, Node regressions and local offline gate pass; [verification/offline-correction.json](verification/offline-correction.json) records tested implementation hashes and the earlier CI failures. Governance remains an external review disposition.
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Post-run attribution: distinguish file listings from returned source content."""
|
|
3
|
+
import argparse
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import re
|
|
8
|
+
import shlex
|
|
9
|
+
import subprocess
|
|
10
|
+
from read_metrics import collect_reads, is_content_command
|
|
11
|
+
|
|
12
|
+
PRICING = json.loads(Path(__file__).with_name('pricing-assumptions.json').read_text())
|
|
13
|
+
|
|
14
|
+
def is_tdd_quality_command(command, depth=0):
|
|
15
|
+
"""Recognize explicit invocations, never gate names inside argument text.
|
|
16
|
+
|
|
17
|
+
Deliberately conservative: compound shell programs and dynamic expansion
|
|
18
|
+
are not execution evidence. Supported transparent wrappers are env,
|
|
19
|
+
command/exec and a shell's single -c/-lc command.
|
|
20
|
+
"""
|
|
21
|
+
if not isinstance(command, str) or depth > 4:
|
|
22
|
+
return False
|
|
23
|
+
try:
|
|
24
|
+
lexer = shlex.shlex(command, posix=True, punctuation_chars=';&|()<>')
|
|
25
|
+
lexer.whitespace_split = True
|
|
26
|
+
tokens = list(lexer)
|
|
27
|
+
except ValueError:
|
|
28
|
+
return False
|
|
29
|
+
if any(token and all(c in ';&|()<>' for c in token) for token in tokens):
|
|
30
|
+
return False
|
|
31
|
+
while tokens and re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*=.*', tokens[0]):
|
|
32
|
+
tokens.pop(0)
|
|
33
|
+
if not tokens:
|
|
34
|
+
return False
|
|
35
|
+
executable = tokens.pop(0).replace('\\', '/').rsplit('/', 1)[-1]
|
|
36
|
+
if executable in ('command', 'exec'):
|
|
37
|
+
if tokens[:1] == ['--']:
|
|
38
|
+
tokens.pop(0)
|
|
39
|
+
return is_tdd_quality_command(shlex.join(tokens), depth+1)
|
|
40
|
+
if executable == 'env':
|
|
41
|
+
if tokens[:1] == ['--']:
|
|
42
|
+
tokens.pop(0)
|
|
43
|
+
return is_tdd_quality_command(shlex.join(tokens), depth+1)
|
|
44
|
+
if executable in ('sh', 'bash', 'zsh'):
|
|
45
|
+
return (len(tokens) == 2 and tokens[0] in ('-c', '-lc')
|
|
46
|
+
and is_tdd_quality_command(tokens[1], depth+1))
|
|
47
|
+
if executable in ('npm', 'npm.cmd'):
|
|
48
|
+
while tokens and tokens[0].startswith('-'):
|
|
49
|
+
option = tokens.pop(0)
|
|
50
|
+
if option not in ('--silent', '-s', '--quiet'):
|
|
51
|
+
return False
|
|
52
|
+
if len(tokens) < 2 or tokens[0] not in ('run', 'run-script') or tokens[1] not in ('tdd:quality', 'test:tdd-quality'):
|
|
53
|
+
return False
|
|
54
|
+
options = tokens[2:tokens.index('--')] if '--' in tokens else tokens[2:]
|
|
55
|
+
return all(option in ('--silent', '-s', '--quiet') for option in options)
|
|
56
|
+
if executable in ('node', 'node.exe'):
|
|
57
|
+
while tokens and tokens[0] in ('--no-warnings', '--enable-source-maps'):
|
|
58
|
+
tokens.pop(0)
|
|
59
|
+
return bool(tokens and tokens[0].replace('\\', '/').removeprefix('./') in (
|
|
60
|
+
'tdd/scripts/quality-gate.js', '.harness/tdd/scripts/quality-gate.js'))
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
def inspect_episode(episode, *, offline=False, frozen=None, documents=None):
|
|
64
|
+
row=json.loads((episode/'analysis.json').read_text())
|
|
65
|
+
manifest=json.loads((episode/'manifest.json').read_text())
|
|
66
|
+
source=str(Path(__file__).resolve().parent.parent.parent)
|
|
67
|
+
events=[]
|
|
68
|
+
for line in (episode/'transcript.jsonl').read_text().splitlines():
|
|
69
|
+
try:events.append(json.loads(line))
|
|
70
|
+
except json.JSONDecodeError:pass
|
|
71
|
+
if documents is None:
|
|
72
|
+
bundle = json.loads((Path(__file__).with_name('frozen-documents.json')).read_text())
|
|
73
|
+
documents = {name: value['content'] for name, value in bundle['documents'].items()
|
|
74
|
+
if name in manifest.get('documents', {})}
|
|
75
|
+
reads, commands, errors = collect_reads(events, documents, source)
|
|
76
|
+
children=[]; ttft=None; requests=[]; private_dispatch=[]
|
|
77
|
+
for event in events:
|
|
78
|
+
if event.get('type')=='assistant':
|
|
79
|
+
children.extend(tool for tool in event.get('message',{}).get('content',[])
|
|
80
|
+
if tool.get('type')=='tool_use' and tool.get('name') in ('Task','Agent'))
|
|
81
|
+
if event.get('type')=='result':
|
|
82
|
+
ttft=event.get('ttft_ms');row['subagent_stats']=event.get('subagent_stats')
|
|
83
|
+
row['permission_denials']=event.get('permission_denials')
|
|
84
|
+
row['host_error']=event.get('is_error');row['terminal_reason']=event.get('terminal_reason')
|
|
85
|
+
if offline:
|
|
86
|
+
frozen = frozen or {}
|
|
87
|
+
requests = frozen.get('request_usage', [])
|
|
88
|
+
private_dispatch = frozen.get('private_collaboration_dispatch', [])
|
|
89
|
+
row['child_audits'] = frozen.get('child_audits', [])
|
|
90
|
+
row['effective_effort_reported'] = frozen.get('effective_effort_reported')
|
|
91
|
+
row['usage'] = frozen.get('usage')
|
|
92
|
+
row['telemetry_provenance'] = 'frozen sanitized audit; private host sessions unavailable'
|
|
93
|
+
else:
|
|
94
|
+
if row['model']['engine']=='claude':
|
|
95
|
+
sid=next((e.get('session_id') for e in events if e.get('subtype')=='init'),None)
|
|
96
|
+
efforts=set()
|
|
97
|
+
if sid:
|
|
98
|
+
for file in (Path.home()/'.claude/projects').glob('*/'+sid+'.jsonl'):
|
|
99
|
+
for line in file.read_text().splitlines():
|
|
100
|
+
e=json.loads(line)
|
|
101
|
+
if e.get('effort'):efforts.add(e['effort'])
|
|
102
|
+
row['effective_effort_reported']=sorted(efforts) if efforts else None
|
|
103
|
+
by_id={}
|
|
104
|
+
for event in events:
|
|
105
|
+
message=event.get('message',{})
|
|
106
|
+
if event.get('type')=='assistant' and message.get('id') and message.get('usage'):
|
|
107
|
+
previous=by_id.get(message['id'],{})
|
|
108
|
+
if message['usage'].get('output_tokens',0)>=previous.get('output_tokens',0):
|
|
109
|
+
by_id[message['id']]=message['usage']
|
|
110
|
+
child_ids={e['tool_use_result']['agentId'] for e in events
|
|
111
|
+
if isinstance(e.get('tool_use_result'),dict) and e['tool_use_result'].get('agentId')}
|
|
112
|
+
child_audits=[]
|
|
113
|
+
if sid:
|
|
114
|
+
for parent_file in (Path.home()/'.claude/projects').glob('*/'+sid+'.jsonl'):
|
|
115
|
+
for agent_id in child_ids:
|
|
116
|
+
child_file=parent_file.with_suffix('')/'subagents'/('agent-'+agent_id+'.jsonl')
|
|
117
|
+
if not child_file.exists():
|
|
118
|
+
child_audits.append({'agent_id':agent_id,'usage_observability':'unknown'})
|
|
119
|
+
continue
|
|
120
|
+
raw=child_file.read_text();child_events=[json.loads(line) for line in raw.splitlines()]
|
|
121
|
+
child_models=set();child_efforts=set();child_requests={}
|
|
122
|
+
for event in child_events:
|
|
123
|
+
message=event.get('message',{})
|
|
124
|
+
if event.get('effort'):child_efforts.add(event['effort'])
|
|
125
|
+
if message.get('model'):child_models.add(message['model'])
|
|
126
|
+
if event.get('type')=='assistant' and message.get('id') and message.get('usage'):
|
|
127
|
+
previous=by_id.get(message['id'],{})
|
|
128
|
+
if message['usage'].get('output_tokens',0)>=previous.get('output_tokens',0):
|
|
129
|
+
by_id[message['id']]=message['usage'];child_requests[message['id']]=message['usage']
|
|
130
|
+
(episode/('child-'+agent_id+'.jsonl')).write_text(raw)
|
|
131
|
+
child_audits.append({'agent_id':agent_id,'models':sorted(child_models),
|
|
132
|
+
'efforts':sorted(child_efforts),'request_usage':list(child_requests.values()),
|
|
133
|
+
'raw_sha256':hashlib.sha256(raw.encode()).hexdigest()})
|
|
134
|
+
row['child_audits']=child_audits
|
|
135
|
+
requests=list(by_id.values())
|
|
136
|
+
all_model_usage=row.get('actual_model_reported')
|
|
137
|
+
if isinstance(all_model_usage,dict) and set(all_model_usage)=={row['model']['model']}:
|
|
138
|
+
aggregate=all_model_usage[row['model']['model']]
|
|
139
|
+
row['parent_usage']=row.get('usage')
|
|
140
|
+
row['usage']={
|
|
141
|
+
'input_tokens':aggregate['inputTokens'],'output_tokens':aggregate['outputTokens'],
|
|
142
|
+
'cache_read_input_tokens':aggregate['cacheReadInputTokens'],
|
|
143
|
+
'cache_creation_input_tokens':aggregate['cacheCreationInputTokens'],
|
|
144
|
+
'output_tokens_details':{'thinking_tokens':aggregate.get('thinkingTokens')},
|
|
145
|
+
}
|
|
146
|
+
row['usage_scope']='all parent/worker modelUsage, reconciled with unique request IDs'
|
|
147
|
+
else:
|
|
148
|
+
sid=next((e.get('thread_id') for e in events if e.get('type')=='thread.started'),None)
|
|
149
|
+
if sid:
|
|
150
|
+
seen=set()
|
|
151
|
+
for file in (Path.home()/'.codex/sessions').glob('*/*/*/*'+sid+'*'):
|
|
152
|
+
private_events=[json.loads(line) for line in file.read_text().splitlines()]
|
|
153
|
+
for event in private_events:
|
|
154
|
+
payload=event.get('payload',{})
|
|
155
|
+
if event.get('type')=='response_item' and payload.get('type')=='function_call' and payload.get('name') in ['spawn_agent','wait_agent']:
|
|
156
|
+
arguments=json.loads(payload.get('arguments','{}'))
|
|
157
|
+
outputs=[x.get('payload',{}).get('output') for x in private_events
|
|
158
|
+
if x.get('payload',{}).get('type')=='function_call_output' and x['payload'].get('call_id')==payload.get('call_id')]
|
|
159
|
+
private_dispatch.append({'tool':payload['name'],'arguments':arguments,'outputs':outputs,
|
|
160
|
+
'evidence':'attributable parent rollout response_item; absent from CLI JSON spawn items'})
|
|
161
|
+
if event.get('type')!='event_msg' or payload.get('type')!='token_count':continue
|
|
162
|
+
info=payload.get('info') or {};total=info.get('total_token_usage');last=info.get('last_token_usage')
|
|
163
|
+
if not total or not last:continue
|
|
164
|
+
key=json.dumps(total,sort_keys=True)
|
|
165
|
+
if key not in seen:requests.append(last);seen.add(key)
|
|
166
|
+
if children:
|
|
167
|
+
known_children = [child for child in row.get('child_audits', []) if child.get('request_usage')]
|
|
168
|
+
if len(known_children) < len(children):
|
|
169
|
+
row.setdefault('child_audits', []).append({'usage_observability': 'unknown'})
|
|
170
|
+
bindings=[];steps=[];strategies=[];stages=[]
|
|
171
|
+
for file in sorted((episode/'state').rglob('workflow-obligations.json')):
|
|
172
|
+
data=json.loads(file.read_text())
|
|
173
|
+
for step in data.get('requirements',[]):
|
|
174
|
+
steps.append({'id':step['id'],'type':step.get('stepType'),'status':step.get('status')})
|
|
175
|
+
bindings.extend({'step':step['id'],**b} for b in step.get('requiredBindings',[]))
|
|
176
|
+
for file in sorted((episode/'state').rglob('workflow-run.json')):
|
|
177
|
+
data=json.loads(file.read_text());strategies.append(data.get('strategy'))
|
|
178
|
+
for file in sorted((episode/'state').rglob('*.json')):
|
|
179
|
+
data=json.loads(file.read_text())
|
|
180
|
+
if isinstance(data,dict) and data.get('schemaVersion')==2 and data.get('stageId') and data.get('runId'):
|
|
181
|
+
stages.append({'file':str(file.relative_to(episode)),'stage':data['stageId'],'status':data.get('status'),
|
|
182
|
+
'dependsOn':data.get('dependsOn'),'requiredBindings':data.get('requiredBindings'),
|
|
183
|
+
'verification':data.get('verification')})
|
|
184
|
+
bindings.extend({'stage':data['stageId'],**b} for b in data.get('requiredBindings',[]))
|
|
185
|
+
for read in reads:
|
|
186
|
+
paths=read.get('paths') or [read.get('path','')]
|
|
187
|
+
read['declared_binding_matches']=[{'id':b.get('id'),'step':b.get('step'),
|
|
188
|
+
'stage':b.get('stage'),'path':b.get('path')}
|
|
189
|
+
for b in bindings if b.get('path') and any(b['path'] in p for p in paths)]
|
|
190
|
+
usage=row.get('usage') or {};price=None;input_price=None;reconstructed_price=None
|
|
191
|
+
rates=PRICING[row['model']['engine']]
|
|
192
|
+
# Independently re-run the immutable upstream cases against the retained
|
|
193
|
+
# final artifact, never an agent-writable checker or cases file.
|
|
194
|
+
if row['workload']=='quixbugs-find-in-sorted':
|
|
195
|
+
checker="import json,importlib.util,sys;from pathlib import Path;s=importlib.util.spec_from_file_location('target',sys.argv[1]);m=importlib.util.module_from_spec(s);s.loader.exec_module(m);cases=[json.loads(x) for x in Path(sys.argv[2]).read_text().splitlines()];assert all(m.find_in_sorted(*args)==expected for args,expected in cases);print('PASS all 7 immutable upstream cases')"
|
|
196
|
+
try:
|
|
197
|
+
check=subprocess.run(['python3','-c',checker,str(episode/'find_in_sorted.py'),
|
|
198
|
+
str(Path(__file__).resolve().parent/'fixtures/quixbugs/cases.json')],capture_output=True,text=True,timeout=20)
|
|
199
|
+
row['independent_grader']={'pass':check.returncode==0,'exit':check.returncode,'stdout':check.stdout,'stderr':check.stderr}
|
|
200
|
+
except subprocess.TimeoutExpired:
|
|
201
|
+
row['independent_grader']={'pass':False,'timeout':True}
|
|
202
|
+
elif row['workload']=='polyglot-simple-linked-list-staged':
|
|
203
|
+
try:
|
|
204
|
+
check=subprocess.run(['node',str(Path(__file__).resolve().parent/'grader.cjs'),
|
|
205
|
+
str(episode/'simple-linked-list.js')],capture_output=True,text=True,timeout=20)
|
|
206
|
+
row['independent_grader']={'pass':check.returncode==0,'exit':check.returncode,'stdout':check.stdout,'stderr':check.stderr}
|
|
207
|
+
except subprocess.TimeoutExpired:
|
|
208
|
+
row['independent_grader']={'pass':False,'timeout':True}
|
|
209
|
+
else:row['independent_grader']=row['grader']
|
|
210
|
+
if row['model']['engine']=='codex' and usage and usage.get('cache_write_input_tokens')==0:
|
|
211
|
+
price=((usage['input_tokens']-usage['cached_input_tokens'])*rates['input']+
|
|
212
|
+
usage['cached_input_tokens']*rates['cache_read']+usage['output_tokens']*rates['output'])/1e6
|
|
213
|
+
elif row['model']['engine']=='claude':price=row.get('host_reported_cost_usd')
|
|
214
|
+
if requests:
|
|
215
|
+
input_price=0.;reconstructed_price=0.
|
|
216
|
+
for request in requests:
|
|
217
|
+
if row['model']['engine']=='codex':
|
|
218
|
+
n=request['input_tokens'];cached=request['cached_input_tokens'];writes=request.get('cache_write_input_tokens')
|
|
219
|
+
if writes is None:input_price=None;reconstructed_price=None;break
|
|
220
|
+
multiplier=rates['large_context_input_multiplier'] if n>rates['large_context_above'] else 1
|
|
221
|
+
c=((n-cached-writes)*rates['input']+cached*rates['cache_read']+writes*rates['cache_write'])*multiplier/1e6
|
|
222
|
+
output_cost=request['output_tokens']*rates['output']*(rates['large_context_output_multiplier'] if n>rates['large_context_above'] else 1)/1e6
|
|
223
|
+
else:
|
|
224
|
+
n=request.get('input_tokens',0);cached=request.get('cache_read_input_tokens',0)
|
|
225
|
+
writes=request.get('cache_creation_input_tokens',0);split=request.get('cache_creation')
|
|
226
|
+
if writes and not split:input_price=None;reconstructed_price=None;break
|
|
227
|
+
split=split or {};multiplier=rates['large_context_multiplier'] if n+cached+writes>rates['large_context_above'] else 1
|
|
228
|
+
c=(n*rates['input']+cached*rates['cache_read']+split.get('ephemeral_5m_input_tokens',0)*rates['cache_write_5m']+
|
|
229
|
+
split.get('ephemeral_1h_input_tokens',0)*rates['cache_write_1h'])*multiplier/1e6
|
|
230
|
+
output_cost=request.get('output_tokens',0)*rates['output']*multiplier/1e6
|
|
231
|
+
input_price+=c;reconstructed_price+=c+output_cost
|
|
232
|
+
mapping={'input_tokens':'input_tokens','output_tokens':'output_tokens'}
|
|
233
|
+
if row['model']['engine']=='codex':mapping['cached_input_tokens']='cached_input_tokens'
|
|
234
|
+
else:
|
|
235
|
+
mapping['cache_read_input_tokens']='cache_read_input_tokens'
|
|
236
|
+
mapping['cache_creation_input_tokens']='cache_creation_input_tokens'
|
|
237
|
+
sums={field:sum(r.get(field,0) for r in requests) for field in mapping}
|
|
238
|
+
input_reconciles=all(sums[field]==usage.get(field) for field in mapping if field!='output_tokens')
|
|
239
|
+
if not input_reconciles:input_price=None;reconstructed_price=None
|
|
240
|
+
elif row['model']['engine']=='claude':
|
|
241
|
+
# Assistant stream records expose initial/provisional output usage.
|
|
242
|
+
# Final aggregate output is usable when all requests share one tier.
|
|
243
|
+
tiers={rates['large_context_multiplier'] if r.get('input_tokens',0)+r.get('cache_read_input_tokens',0)+r.get('cache_creation_input_tokens',0)>rates['large_context_above'] else 1 for r in requests}
|
|
244
|
+
reconstructed_price=(input_price+usage['output_tokens']*rates['output']*next(iter(tiers))/1e6) if len(tiers)==1 and input_price is not None else None
|
|
245
|
+
else:sums={};input_reconciles=False
|
|
246
|
+
# No tokenizer estimate: bytes are measured, tokenizer/content tokens unknown.
|
|
247
|
+
row.update({'observed_reference_reads':reads,'reference_returned_bytes':sum(x['returned_bytes'] for x in reads),
|
|
248
|
+
'exact_reference_bytes_lower_bound':sum(x['exact_reference_bytes'] for x in reads),
|
|
249
|
+
'observed_errors':errors,'required_bindings':bindings,'steps':steps,'strategies':strategies,
|
|
250
|
+
'stages':stages,'child_calls':children,'ttft_ms':ttft,'api_equivalent_cost_usd':price,
|
|
251
|
+
'tdd_quality_gate_name_matched_invocation':any(is_tdd_quality_command(c) for c in commands),
|
|
252
|
+
'cost_basis':'historical price assumptions; provider snapshot unavailable; not subscription billing',
|
|
253
|
+
'request_usage':requests,'api_equivalent_input_cost_usd':input_price,
|
|
254
|
+
'request_usage_sums':sums,'input_usage_reconciles':input_reconciles,
|
|
255
|
+
'request_output_usage_complete':sums.get('output_tokens')==usage.get('output_tokens') if requests else False,
|
|
256
|
+
'reconstructed_total_cost_usd':reconstructed_price,
|
|
257
|
+
'reference_content_tokens':None,
|
|
258
|
+
'reference_filesystem_access_candidates':sum(x['filesystem_access_candidates'] for x in reads),
|
|
259
|
+
'reference_shell_output_bytes':sum(x['shell_output_bytes'] for x in reads),
|
|
260
|
+
'uncertain_or_mixed_output_bytes':sum(x['uncertain_or_mixed_output_bytes'] for x in reads)})
|
|
261
|
+
row['private_collaboration_dispatch']=private_dispatch
|
|
262
|
+
row['parent_api_equivalent_input_cost_usd']=input_price
|
|
263
|
+
row['parent_api_equivalent_cost_usd']=price
|
|
264
|
+
if any(x['tool']=='spawn_agent' for x in private_dispatch) or any(
|
|
265
|
+
x.get('usage_observability') == 'unknown' for x in row.get('child_audits', [])):
|
|
266
|
+
row['api_equivalent_input_cost_usd']=None
|
|
267
|
+
row['api_equivalent_cost_usd']=None
|
|
268
|
+
row['child_usage_observability']='unknown; dispatched worker usage/model ID not exposed in parent CLI stream'
|
|
269
|
+
else:row['child_usage_observability']=frozen.get('child_usage_observability', 'unknown; private host attribution unavailable') if offline else 'no attributable native spawn in inspected parent rollout'
|
|
270
|
+
if (row.get('stop_reason')=='wall-time-budget' or row.get('terminal_reason')=='max_turns') and row.get('router_attributed'):
|
|
271
|
+
row['classification']='definitive-agent-budget-failure'
|
|
272
|
+
row['budget_failure']=True
|
|
273
|
+
row['cost_observability']='partial; final aggregate unavailable'
|
|
274
|
+
# Retain measured request counters without pretending the interrupted
|
|
275
|
+
# in-flight request has complete billed/output telemetry.
|
|
276
|
+
row['observed_request_token_sums']={field:sum(r.get(field,0) for r in requests)
|
|
277
|
+
for field in ['input_tokens','cached_input_tokens','cache_read_input_tokens',
|
|
278
|
+
'cache_creation_input_tokens','output_tokens','reasoning_output_tokens']}
|
|
279
|
+
else:row['budget_failure']=False;row['cost_observability']='final aggregate retained' if usage else 'unknown'
|
|
280
|
+
if row['budget_failure'] or ('unknown' in row['child_usage_observability']):
|
|
281
|
+
row['api_equivalent_cost_usd'] = None
|
|
282
|
+
row['api_equivalent_input_cost_usd'] = None
|
|
283
|
+
row['reconstructed_total_cost_usd'] = None
|
|
284
|
+
(episode/'audit.json').write_text(json.dumps(row,indent=2)+'\n')
|
|
285
|
+
return row
|
|
286
|
+
|
|
287
|
+
if __name__=='__main__':
|
|
288
|
+
parser=argparse.ArgumentParser();parser.add_argument('roots',nargs='+');parser.add_argument('--output',required=True)
|
|
289
|
+
args=parser.parse_args();rows=[]
|
|
290
|
+
for root in args.roots:
|
|
291
|
+
for episode in sorted(Path(root).iterdir()):
|
|
292
|
+
if (episode/'analysis.json').exists():
|
|
293
|
+
row=inspect_episode(episode);row['series']=Path(root).name;rows.append(row)
|
|
294
|
+
out=Path(args.output);out.mkdir(parents=True,exist_ok=True)
|
|
295
|
+
(out/'audited-rows.json').write_text(json.dumps(rows,indent=2)+'\n')
|
|
296
|
+
from reporting import summarize_groups
|
|
297
|
+
summary = summarize_groups(rows)
|
|
298
|
+
(out/'group-summary.json').write_text(json.dumps(summary,indent=2)+'\n')
|
|
299
|
+
print(json.dumps(summary,indent=2))
|