Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
2bc8c0a
test: pin schema-driven recipe section pagination
Trecek Jul 23, 2026
94649d5
feat: define recipe section schema contract
Trecek Jul 23, 2026
9ce4594
feat: enforce recipe artifact section schema
Trecek Jul 23, 2026
7c4e93f
feat: add deterministic recipe section planner
Trecek Jul 23, 2026
c0d04a9
feat: wire typed recipe section requests
Trecek Jul 23, 2026
6d33dc7
docs: define recipe section continuation contract
Trecek Jul 23, 2026
3649d90
fix: complete recipe section pagination verification
Trecek Jul 23, 2026
e46e301
fix: close recipe pagination audit gaps
Trecek Jul 24, 2026
2e74b6b
fix(review): validate recipe section definitions at construction
Trecek Jul 24, 2026
b0065eb
fix(review): derive recipe page formats from section schema
Trecek Jul 24, 2026
35d49cf
fix(review): decouple recipe retirement from pagination
Trecek Jul 24, 2026
40f6d4e
fix(review): establish one-way recipe section contracts
Trecek Jul 24, 2026
6bf9f98
fix(review): validate recipe page descriptor families
Trecek Jul 24, 2026
db76eda
fix(review): bound pagination candidate materialization
Trecek Jul 24, 2026
5e65593
fix(review): distinguish pagination convergence failures
Trecek Jul 24, 2026
18b6cd5
test(review): cover recipe cache byte accounting
Trecek Jul 24, 2026
b06dae7
fix(review): satisfy recipe pagination architecture gates
Trecek Jul 24, 2026
ada933b
fix(review): retain recipe schema mismatch diagnostics
Trecek Jul 24, 2026
d292620
test(review): pin recipe section identity digests
Trecek Jul 24, 2026
b0c969b
test(review): pin recipe plan manifest fields
Trecek Jul 24, 2026
137f403
test(review): exercise fragment verifier failures
Trecek Jul 24, 2026
e425806
test: assert canonical schema diagnostic
Trecek Jul 24, 2026
114117d
fix(review): validate recipe section behaviors
Trecek Jul 24, 2026
453af47
fix(review): share recipe digest policy constants
Trecek Jul 24, 2026
89aa120
fix(review): bound recipe schema diagnostics
Trecek Jul 24, 2026
bf87a93
fix(review): reject negative page cache limits
Trecek Jul 24, 2026
eaa00de
fix(review): share recipe content format metadata
Trecek Jul 24, 2026
9c30efb
fix(review): isolate retirement callback failures
Trecek Jul 24, 2026
7f05cb6
fix(review): validate typed cancellation factories eagerly
Trecek Jul 24, 2026
9d53f27
test(review): cover missing and null recipe sections
Trecek Jul 24, 2026
292593e
fix(review): satisfy review regression guards
Trecek Jul 24, 2026
240f70d
fix(review): reject duplicate recipe section parts
Trecek Jul 24, 2026
b5d7e27
fix(review): enforce one recipe envelope producer call
Trecek Jul 24, 2026
2a76a7b
fix(review): centralize recipe section classification
Trecek Jul 24, 2026
3b6373b
fix(review): synchronize recipe page-plan cache admission
Trecek Jul 24, 2026
eeca9ae
fix(review): derive failure floor from canonical renderer
Trecek Jul 24, 2026
b94cd8d
fix(review): keep failure renderer in canonical registry owner
Trecek Jul 24, 2026
ea141aa
test(arch): ratchet core constant inventory
Trecek Jul 24, 2026
35d302d
fix(review): reject empty dynamic recipe sections
Trecek Jul 24, 2026
8c57b80
fix(review): validate recipe section request bounds
Trecek Jul 24, 2026
fd38bf4
fix(review): validate recipe page descriptors
Trecek Jul 24, 2026
db46545
fix(review): log recipe artifact reload failures
Trecek Jul 24, 2026
caf3272
fix(review): move recipe failure rendering to server
Trecek Jul 24, 2026
d6bf85c
fix(review): validate provisional fragment descriptors
Trecek Jul 24, 2026
27aff8a
test(arch): track relocated recipe renderer export
Trecek Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ generic_automation_mcp/
| `migration/` | IL-2 | Versioned migration engine + failure store |
| `fleet/` | IL-2 | Campaign dispatch, semaphore, sidecar, liveness, state persistence |
| `server/` | IL-3 | FastMCP server — tools/, kitchen gating, session-type dispatch |
| `server/recipe_section/` | IL-3 | Final invariant verification for schema-driven recipe-section page plans |
| `cli/` | IL-3 | CLI — doctor/, update/, fleet/ subcommands, ui/, session/ management |
| `hooks/` | — | coding-agent hook scripts — guards/, formatters/ |
| `agents/` | — | Bundled agent definition markdown files served as MCP resources |
Expand Down
21 changes: 20 additions & 1 deletion docs/decisions/0004-recipe-redelivery.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ is relaxed, the `load_recipe` / `open_kitchen` / `get_recipe_section` end-to-end
recovery path — including envelope re-delivery plus per-step pulls — must be tested as
the recovery path.

### Schema-driven pull continuation

The fixed pullable sections are `content`, `ingredients_table`, `orchestration_rules`,
`stop_step_semantics`, `errors`, and `warnings`; a validated post-prune step is a
separate dynamic raw-YAML definition. Every page pins `pagination_version`,
`section_registry_sha256`, `section_sha256`, and `page_plan_sha256`.

The four exhaustive reconstruction algorithms are:

- `raw-text`: verify contiguous UTF-8 byte ranges and concatenate content.
- `json-array-page`: run `json.loads` on every complete array page and extend in order.
- `json-scalar-page`: run `json.loads` on every string page and concatenate decoded text.
- `json-element-fragment`: decode string fragments, concatenate one canonical element,
verify `element_sha256`, then parse that element once.

Consumers reject an unknown pagination version or unknown content format, mixed
identities, gaps, overlaps, duplicates, a page after the terminal page, or a terminal
page carrying `next_part`. They must not guess or repair a malformed continuation.

## Consequences

- `recipe_read_guard.py` error messages direct the agent to call `load_recipe` (and,
Expand All @@ -59,4 +78,4 @@ the recovery path.
`load_recipe` / `open_kitchen` / `get_recipe_section` re-delivery restores full
pipeline execution capability, including envelope re-delivery plus per-step pulls.
- `test_copied_config_has_auto_compact_limit` validates the primary defense path:
`setup_session_dir` preserves the override in the copied `config.toml`.
`setup_session_dir` preserves the override in the copied `config.toml`.
15 changes: 15 additions & 0 deletions docs/decisions/0005-output-budget-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ recorded explicitly in the issue body.
explicit decision through final enforcement. History retention is never read as the
selected outer result.

### Recipe-section byte budget

`RECIPE_SECTION_MANDATORY_FAILURE_CODES` defines the exact compact code-only failures,
and `RECIPE_SECTION_RESPONSE_FLOOR_BYTES` is their maximum UTF-8 size. Configuration
below that floor is invalid. After request admission, the request-specific ceiling is:

`recipe_section_bound_bytes = min(response_max_bytes, conservative_general_result_limit)`

The current ordinary Codex policy deliberately keeps the conservative limit at
10,000 bytes. It is not the generic token×4 projection. Planning trial-renders each
complete outer response with compact canonical JSON and accepts only pages within the
captured UTF-8 bound. Oversized arrays use complete pages or
`json-element-fragment`; there is no truncation and no dropped element. A terminal
page omits `next_part`.

## Operational Signals

Output-budget instrumentation uses low-cardinality structured counters or events for:
Expand Down
7 changes: 6 additions & 1 deletion docs/research/codex-delivery-conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Status: blocked

**Probe contract:** `codex-recipe-delivery-v1`
**Probe contract:** `codex-recipe-delivery-v2`
**Decision date:** 2026-07-22
**Model identity:** `gpt-5.6-sol`

Expand Down Expand Up @@ -54,6 +54,11 @@ probe retained the bounded `recipe_pull`, pulled content with the same `body_sha
no transport-truncation marker, and echoed the digest in the terminal next-request canary.
`task test-smoke-codex` passed the dedicated oracle on `gpt-5.6-sol`.

The v2 oracle also creates multiple server-authoritative override warnings, including one
oversized `warnings` element. It reconstructs the ordered list from `json-array-page` and
`json-element-fragment` pages, verifies `section_sha256`, `element_sha256`, and
`page_plan_sha256`, and requires terminal omission of `next_part`.

This pass proves the fail-closed envelope/pull recovery path. It does not prove an
authoritative selected outer limit, raw pre-truncation bytes, or a protected pre-call event,
so it cannot enable `ATTESTED_INLINE`.
Expand Down
8 changes: 8 additions & 0 deletions src/autoskillit/cli/_prompts_kitchen.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ def _build_open_kitchen_prompt(
"artifact_blob_size_bytes=recipe_pull.artifact_blob_size_bytes, "
"body_sha256=recipe_pull.body_sha256, "
"body_size_bytes=recipe_pull.body_size_bytes). "
"Require a known pagination_version and stable section_registry_sha256, "
"section_sha256, and page_plan_sha256 across pages. Reconstruct raw-text by "
"concatenating byte ranges; json-array-page by JSON-decoding and extending; "
"json-scalar-page by JSON-decoding and concatenating strings; and "
"json-element-fragment by JSON-decoding fragments, concatenating and verifying "
"the canonical element, then parsing it once. Reject unknown pagination_version "
"or unknown content_format; do not guess. Follow has_more/next_part, and require "
"a terminal page to omit next_part. "
"Do not read recipe YAML files directly.\n\n"
"OPTIONAL STEP SEMANTICS:\n"
"- optional: true means the step is SKIPPED when its skip_when_false ingredient\n"
Expand Down
7 changes: 5 additions & 2 deletions src/autoskillit/config/_config_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
DRY_WALKTHROUGH_VERIFIED_MARKER,
KNOWN_BACKEND_NAMES,
LABEL_LIFECYCLE_REGISTRY,
RECIPE_SECTION_RESPONSE_FLOOR_BYTES,
IssueLabelState,
OutputFormat,
get_logger,
Expand Down Expand Up @@ -358,8 +359,10 @@ class OutputBudgetConfig:
shell_max_inline_bytes: int = 12_000

def __post_init__(self) -> None:
if self.response_max_bytes <= 0:
raise ValueError("response_max_bytes must be positive")
if self.response_max_bytes < RECIPE_SECTION_RESPONSE_FLOOR_BYTES:
raise ValueError(
f"response_max_bytes must be at least {RECIPE_SECTION_RESPONSE_FLOOR_BYTES} bytes"
)


@dataclass
Expand Down
22 changes: 22 additions & 0 deletions src/autoskillit/core/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ from .types import CORE_PACKS as CORE_PACKS
from .types import DATA_MANIFEST_SOURCE_TYPES as DATA_MANIFEST_SOURCE_TYPES
from .types import DISPATCH_ID_ENV_VAR as DISPATCH_ID_ENV_VAR
from .types import DRY_WALKTHROUGH_VERIFIED_MARKER as DRY_WALKTHROUGH_VERIFIED_MARKER
from .types import DYNAMIC_RECIPE_SECTION_DEF as DYNAMIC_RECIPE_SECTION_DEF
from .types import FEATURE_REGISTRY as FEATURE_REGISTRY
from .types import FLEET_DISPATCH_MODE as FLEET_DISPATCH_MODE
from .types import FLEET_DISPATCH_TOOLS as FLEET_DISPATCH_TOOLS
Expand Down Expand Up @@ -220,6 +221,19 @@ from .types import (
)
from .types import RECIPE_PACK_REGISTRY as RECIPE_PACK_REGISTRY
from .types import RECIPE_PACK_TAGS as RECIPE_PACK_TAGS
from .types import (
RECIPE_SECTION_CONTENT_FORMAT_REGISTRY as RECIPE_SECTION_CONTENT_FORMAT_REGISTRY,
)
from .types import (
RECIPE_SECTION_MANDATORY_FAILURE_CODES as RECIPE_SECTION_MANDATORY_FAILURE_CODES,
)
from .types import (
RECIPE_SECTION_PAGINATION_POLICY_DIGEST as RECIPE_SECTION_PAGINATION_POLICY_DIGEST,
)
from .types import RECIPE_SECTION_PAGINATION_VERSION as RECIPE_SECTION_PAGINATION_VERSION
from .types import RECIPE_SECTION_REGISTRY as RECIPE_SECTION_REGISTRY
from .types import RECIPE_SECTION_REGISTRY_DIGEST as RECIPE_SECTION_REGISTRY_DIGEST
from .types import RECIPE_SECTION_RESPONSE_FLOOR_BYTES as RECIPE_SECTION_RESPONSE_FLOOR_BYTES
from .types import REQUIRED_CONSUMER_FIELDS as REQUIRED_CONSUMER_FIELDS
from .types import RESERVED_LOG_RECORD_KEYS as RESERVED_LOG_RECORD_KEYS
from .types import RESPONSE_BACKSTOP_EXEMPTION_REGISTRY as RESPONSE_BACKSTOP_EXEMPTION_REGISTRY
Expand Down Expand Up @@ -367,6 +381,9 @@ from .types import RecipeLoadError as RecipeLoadError
from .types import RecipeNotFoundError as RecipeNotFoundError
from .types import RecipePackDef as RecipePackDef
from .types import RecipeRepository as RecipeRepository
from .types import RecipeSectionContentFormatDef as RecipeSectionContentFormatDef
from .types import RecipeSectionDef as RecipeSectionDef
from .types import RecipeSectionValidationFinding as RecipeSectionValidationFinding
from .types import RecipeSource as RecipeSource
from .types import ResponseBackstopExemptionDef as ResponseBackstopExemptionDef
from .types import RestartScope as RestartScope
Expand Down Expand Up @@ -413,6 +430,7 @@ from .types import WriteBehaviorSpec as WriteBehaviorSpec
from .types import WriteEvidence as WriteEvidence
from .types import WriteExpectedResolver as WriteExpectedResolver
from .types import assert_prompt_sentinel as assert_prompt_sentinel
from .types import canonical_recipe_section_json as canonical_recipe_section_json
from .types import closure_authority_spec_from_args as closure_authority_spec_from_args
from .types import compute_remaining as compute_remaining
from .types import describe_capability_mismatches as describe_capability_mismatches
Expand All @@ -424,6 +442,9 @@ from .types import is_path_like_token as is_path_like_token
from .types import is_valid_codex_model_id as is_valid_codex_model_id
from .types import model_class as model_class
from .types import parse_plan_paths as parse_plan_paths
from .types import recipe_section_digest as recipe_section_digest
from .types import recipe_section_element_digest as recipe_section_element_digest
from .types import recipe_section_plan_digest as recipe_section_plan_digest
from .types import resolve_payload_field as resolve_payload_field
from .types import resolve_skill_name as resolve_skill_name
from .types import resolve_target_skill as resolve_target_skill
Expand All @@ -433,3 +454,4 @@ from .types import strip_context_window_suffix as strip_context_window_suffix
from .types import truncate_text as truncate_text
from .types import unsatisfied_backend_capabilities as unsatisfied_backend_capabilities
from .types import validate_label_transition as validate_label_transition
from .types import validate_recipe_artifact_sections as validate_recipe_artifact_sections
1 change: 1 addition & 0 deletions src/autoskillit/core/types/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Type re-export hub and all typed building blocks for the autoskillit package (IL
| `_type_checkpoint.py` | `SessionCheckpoint` frozen dataclass and `compute_remaining()` helper for session resume |
| `_type_backend.py` | `BackendCapabilities` frozen dataclass, `CLAUDE_CODE_CAPABILITIES` constant, `CmdSpec`, `SkillSessionConfig`, `ClaudeEventData`, `CodexEventData`, `SessionEvent`, `AgentSessionResult` |
| `_type_recipe_delivery.py` | Typed Codex recipe budgets, protected-host evidence definitions, requests, attestations, and delivery decisions |
| `_type_recipe_sections.py` | Recipe-section schema validation plus canonical section, element, and plan digest helpers |
| `_type_capture.py` | `CaptureEntrySpec` and `CaptureValueTypeError` for typed capture contract enforcement |
| `_type_dispatch_identity.py` | `DispatchIdentity` frozen value object, `PromptContractError`, and `assert_prompt_sentinel` for sentinel contract enforcement |
| `_type_helpers.py` | Text processing and skill-name extraction utilities |
Expand Down
3 changes: 3 additions & 0 deletions src/autoskillit/core/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
from ._type_protocols_workspace import __all__ as _protocols_workspace_all
from ._type_recipe_delivery import * # noqa: F401, F403
from ._type_recipe_delivery import __all__ as _recipe_delivery_all
from ._type_recipe_sections import * # noqa: F401, F403
from ._type_recipe_sections import __all__ as _recipe_sections_all
from ._type_results import * # noqa: F401, F403
from ._type_results import __all__ as _results_all
from ._type_results_execution import * # noqa: F401, F403
Expand Down Expand Up @@ -99,6 +101,7 @@
+ _results_all
+ _results_execution_all
+ _recipe_delivery_all
+ _recipe_sections_all
+ _resume_all
+ _session_env_all
+ _subprocess_all
Expand Down
Loading
Loading