Skip to content

Output Budget Protocol Audit Remediation#4277

Merged
Trecek merged 30 commits into
developfrom
impl-rectify_output_budget_protocol_2026-07-15_135615-20260715-163305
Jul 18, 2026
Merged

Output Budget Protocol Audit Remediation#4277
Trecek merged 30 commits into
developfrom
impl-rectify_output_budget_protocol_2026-07-15_135615-20260715-163305

Conversation

@Trecek

@Trecek Trecek commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Apply this remediation as a delta on the existing implementation branch at 093179c8f, whose diff already contains the four-layer Output Budget Protocol. The remediation makes every model-visible reduction artifact-backed, makes projected_utf8_bytes describe the exact canonical handler response, enforces an explicit registered-schema/wire-schema/handler-type/post-conversion policy, ratchets both response exemptions to independent measured ceilings, and emits only bounded, path-free telemetry.

The implementation is split into four ordered phases, each independently gated by clean pre-commit and task test-all runs with evidence recorded in .autoskillit/evidence/output-budget-remediation/manifest.json:

Implementation Plan

Plan file: .autoskillit/temp/make-plan/output_budget_protocol_audit_remediation_plan_2026-07-15_194745.md

🤖 Generated with Claude Code via AutoSkillit

Trecek and others added 13 commits July 16, 2026 21:37
Add generated Codex child delivery probe (T13) with spawn/wait/rollout
linkage assertions, strengthen deep-investigate E2E (T14) with workflow
event normalization proving completed agent waves, inter-batch synthesis,
post-report D6 validators, and Claude 200K provenance. Register generated
agent TOMLs in session config. Add Taskfile smoke target. Raise
investigate spawn ceiling to 16 with wave completion ordering contracts.
Index fresh GraphQL closure postcondition for issues #4253/#3938 and
PR #4259.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 4 gate: 30941 passed, 611 skipped, 55 xfailed at 3e10de464.
Evidence manifest validated with --mode incremental.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remediation recipe grew to 183,103 chars on develop. Bump load_recipe
ceiling to 185,000 and open_kitchen to 186,000. Update measurement IDs,
registry digest, ADR doc, Codex token limit fixture, and all pinned
test values. Fix shifted line numbers in schema version allowlist and
symbol count in subpackage structure test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The maxima assertion in test_canonical_recipe_responses_fit_independent_registry_ceilings
hardcoded byte counts that included the absolute path to builtin_scripts_dir(),
which varies by checkout location (100 chars locally vs 41 in CI = 59-byte gap).
Normalize the rendered payload by replacing the resolved scripts path with its
template placeholder before measuring, making the pinned maxima environment-independent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@Trecek Trecek left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit PR Review — Verdict: approved_with_comments

Comment thread src/autoskillit/server/_notify.py Outdated
Comment thread src/autoskillit/server/_response_conformance.py Outdated
Comment thread src/autoskillit/server/_response_budget.py
max_utf8_bytes=exemption.max_utf8_bytes,
)
return result
return bounded_response_budget_failure(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning] defense: Exemption ceiling breach drops the entire response with no artifact persisted: when an exempt tool (open_kitchen/load_recipe) exceeds its measured ceiling, this branch returns bounded_response_budget_failure WITHOUT writing a spill artifact — the only enforcement path that is not artifact-backed, contradicting the PR's stated 'every model-visible reduction artifact-backed' goal. The reused cause code internal_invariant_failed also obscures the actual condition (exemption ceiling exceeded). For load_recipe, ceilings were measured against bundled recipes (measurement_id bundled-recipes-all-modes-2026-07-16); a larger user-local recipe reaches this path at runtime and loses the recipe delivery entirely, in tension with ADR-0004's re-delivery obligation. Consider persisting the artifact before failing closed and/or introducing a distinct cause code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid observation — flagged for design decision, with the cause-code half applied in 0645ba6: exemption ceiling breaches now report a distinct exemption_ceiling_exceeded cause instead of reusing internal_invariant_failed. The artifact-persistence half is deliberately not applied: ADR-0005 "Accepted Gaps" item 3 documents fail-closed-without-echo as intended, and test_exemption_overage_fails_closed_and_does_not_spill (tests/server/test_response_backstop.py:302) explicitly encodes the no-spill behavior. Persisting a spill artifact on this path — and resolving the ADR-0004 re-delivery tension for user-local recipes — requires a human decision to amend the ADR.

Comment thread src/autoskillit/core/types/_type_constants.py
Comment thread tests/server/test_wire_compat.py

@Trecek Trecek left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit review: warning-only findings detected. See inline comments — no blocking changes required.

Trecek and others added 6 commits July 17, 2026 10:41
logger.exception attaches exc_info that logger.error dropped, keeping the
structured event name while restoring post-hoc debuggability of MCP tool
crashes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
assert isinstance(...) disappears under PYTHONOPTIMIZE, and convert_result
exists on the base Tool class, so an invariant violation would proceed
silently rather than fail. An explicit TypeError keeps behavior identical
in both configurations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_ProjectionNonconvergentError now embeds measured/projected/max_bytes and
attempted state count, and the catch site surfaces the detail via the
module's structured event pattern instead of swallowing the message.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reusing internal_invariant_failed obscured the actual condition when an
exempt tool exceeds its measured ceiling. The fail-closed no-spill
behavior itself is unchanged (deliberate per ADR-0005 accepted gap 3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PROBE_POLICY_IDENTITY previously hashed only OUTPUT_DISCIPLINE_BLOCK, so
editing the runtime-injected OUTPUT_DISCIPLINE_DIGEST left stale cached
probe results valid. A combined digest over both policy texts now feeds
the cache key; OUTPUT_DISCIPLINE_BLOCK_SHA256 remains for SKILL.md
byte-identity checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OUTPUT_DISCIPLINE_COMBINED_SHA256 raises the split-module __all__ union
from 108 to 109 symbols.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@Trecek Trecek left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit PR Review — Verdict: changes_requested

Comment thread src/autoskillit/execution/backends/_codex_config.py
Comment thread src/autoskillit/server/_response_budget.py
Comment thread src/autoskillit/server/git.py
Comment thread src/autoskillit/server/tools/tools_workspace.py
Comment thread src/autoskillit/hooks/guards/output_budget_guard.py
Comment thread tests/execution/test_headless_core.py
Comment thread tests/infra/test_release_sanity.py Outdated
Comment thread tests/skills_extended/test_audit_friction_output_budget.py
Comment thread src/autoskillit/server/_response_budget.py
Comment thread src/autoskillit/server/_response_budget.py

@Trecek Trecek left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit review: 3 critical and 10 warning findings detected. See inline comments for details.

Outside Diff Range

These findings target lines not in the diff and could not be posted as inline comments:

tests/hooks/test_codex_hooks_format_contract.py

  • L19 [info/tests]: test_output_budget_guard_is_wired_with_identical_matchers uses next() without a default over HOOK_REGISTRY; if the guard

Trecek and others added 6 commits July 17, 2026 12:48
When combined JSON was under inline_max_chars but individual condensed
fields exceeded it, preview/truncation markers embedded literal 'None'
for the artifact path. Now forces a re-spill with inline_max_chars=0
when any condensed field needs truncation but no artifact was created.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously only -q/--quiet had combined-flag detection (e.g. -qn).
Now extracts all single-char short flags from names and checks bundled
forms, fixing bypass where 'grep -rn' passed but 'grep -r -n' was denied.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commit 4a8d65a updated exemption ceilings but missed two prose
sentences in Ceiling and Backstop Reconciliation: 53,000 -> 54,500
tokens, 212,000 -> 218,000 implied bytes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
File-level exemption silently suppressed detection of any future
personal-path leak. Now uses (path, lineno) granularity targeting
only lines 17-18 (the INCIDENT_LOG_SEARCH fixture).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guard has 5 silent exit paths that all produce empty stdout. Requiring
non-empty output ensures classification actually ran rather than passing
vacuously on a bail-out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ible path

shape_json_response had zero direct unit tests; added passthrough and
spill-with-metadata cases. _plain_spill_envelope's preview_limit==0
None-return (irreducible_shape failure) was untested; added a tiny-config
test that forces convergence failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Trecek and others added 5 commits July 17, 2026 12:58
Instead of requiring non-empty stdout from BOUNDED commands (which
correctly produce none), add a separate test proving the guard actually
classifies a known-hazardous command (grep -r) as deny. This catches
bail-out regressions without breaking BOUNDED command semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_bounded_failure cascades to progressively smaller responses; max_bytes=10
was too small, yielding '{}' without 'success' key. Set max_bytes=50 which
allows '{"success":false}' while still forcing _plain_spill_envelope to
return None.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
historical_context entries have bound_to_commit=false and
closure.historical_artifacts are marked non-reconstructible — these
are informal, non-portable evidence that was intentionally never
tracked. The validator now checks structural metadata (field presence,
SHA format) without requiring the referenced files to exist on disk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
historical_context and closure.historical_artifacts now validate field
presence and format only (not file existence or hash match), matching
the manifest's own bound_to_commit=false / non-reconstructible semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route RecursionError from deep-nesting projection to the irreducible_shape
failure path so the already-persisted artifact pointer survives, degrade
deep JSON strings to the non-recursive plain spill, and reject nonpositive
response_max_bytes at config construction with a terminating preview loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant