Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
- **OpenShell link retargets retain supported authority expansions.** A selected policy link changing targets still requires reference review, and the same change now also reports every widening established from the captured policies. This covers a linked document moving from enforced request restrictions to audit-only, and an independent policy widening beside a same-byte link retarget. Static comparison and runtime-enforcement limits are unchanged. (#942)
- Configured committed verification reads selected OpenShell file-link chains from the same verified Git object store as its application snapshot, including mixed-case registrations and chains of up to eight hops. Generic archives still reject links; configured snapshots still reject unrelated, escaping, cyclic, directory and over-bound links, selected links that would not open as written (through a missing or non-directory component, or with an empty one such as a trailing slash), and submodules. `verification prepare` now captures every host dependency the boundary snapshot reads before building its plan, as configured `verify` already did. Worker replay therefore rejects a selected-link retarget even when policy bytes are identical, and portable plans now bind Claude hook scripts too, so a hook script that is a symbolic link makes the worker reject the plan. Scoped host-comparison archives now resolve in-tree link chains of up to eight hops (was seven). No schema or contract version changes. (#942, #945)
- **An empty preflight plan no longer grants merge or completion.** A plan that named no changed file, capability request or host permission request returned the shared `complete` state, whose permissions include `merge` and `report_complete`, with no verifier run behind it. Preflight `0.6` answers it with the new `planning_only` state, which owes no action and authorizes nothing, and every preflight route now denies every permission in both the model and the published schema; `complete` and `review_publishable` cannot appear. Docs-only plans still route to `verify`, and protected surfaces and drift still stop for a human. `0.5` stays frozen and readable as a base preflight. Runtime contract 41 → 42; `minimum_control_contract_version` stays 21. Hooks written by `install-hooks` before this change accept only preflight `0.5`, so their instruction-structure check fails closed until `install-hooks --write` is re-run. See the `planning-only preflight` migration note in `STABILITY.md`. (#610)
- **Published schema documents keep their bytes across releases.** A version bump no longer rewrites agent-handoff v9, agent-result v3 or agent-boundary-result v3, and the generator refuses to change any `docs/*-schema*.json` a release tag carries. (#1044)
- Commit the application review benchmark (`benchmark/application-q2/`): the 49 pinned development pull requests, the runner, the hand-scoring protocol, the 2026-09-30 ledger, reproduced from fresh clones with every status, row and comparison id identical (Q2 1/49; re-read, 12 members are not SDK/ADK wiring changes, so Q0 is 37/49, and MIS_TALENT#6 is Q1, so Q1 is 2/49), and a holdout selection rule, the definitions it uses and a recorded repository pool, frozen by digest before the reader changes they will judge. Each release now records `Q2: n/49 development, m/≥30 holdout`. Development evidence only; no success-rate claim. (#908)
- **`diff --application` reads tools lists built by an expression.** An OpenAI Agents SDK or Google ADK agent's `tools=` (and an SDK agent's `handoffs=`, an ADK agent's `sub_agents=`) built by a spread, `+`, a conditional, `or`, a filter or another module's list is read member by member instead of reported as one dynamic expression. A part it cannot read — a call, a builder's parameter, `self.tools` — is named with its location on that agent, which stays incomplete, and the members read are still compared. A member held only under a condition carries `bound_when`, and a change to the condition alone is a `changed` row whose direction is not established. `application_comparison_schema_version` 0.2 → 0.3. The Google ADK dynamic-tools limit now names its file and line and its agent, a starred element the ADK reader cannot read no longer leaves the agent's list read as complete, and an imported `handoffs=` list no longer reads as one handoff named after the list. The SDK recovery reason `sdk_literal_tool_list_concatenation_unsupported` is retired: that form is read (#584). For `scan`, an ADK list that came through a name or from another module is not counted as a proven surface, so no `scan` decision moves. On the 49-PR development corpus (`benchmark/application-q2/`), pull requests with any row go from 9 to 11 and those still carrying a tools-list limit from 28 to 24, most of the rest a builder's parameter (#874); MIS_TALENT#7 now reports the `Finance_Agent` row the 2026-09-30 ledger found missing. On 131 open SDK/ADK pull requests, every status is unchanged and two gain rows, all 16 checked against source. (#909)
- **`diff --application` reads tools bound as objects.** An MCP server or toolset (Google ADK `McpToolset`; OpenAI Agents SDK `MCPServerStdio`, `MCPServerSse`, `MCPServerStreamableHttp` in `mcp_servers=`), an agent exposed as a tool (SDK `agent.as_tool(...)`, ADK `AgentTool(agent=...)`, and a repository helper that returns one from its single `return`), a hosted or built-in tool (SDK `WebSearchTool`, `FileSearchTool`, `CodeInterpreterTool`, `ComputerTool`; ADK `google_search`, `built_in_code_execution`, `load_memory`) and the SDK's `function_tool(f)` are bindings of their agent, where they used to read as an unresolved tool or "assigned a value, not a function definition". A row's side carries `object`: an MCP server's transport, the URL's host or the program's file name, the names of the credentials it sends and its tool filter; the agent an agent tool wraps; a built-in's name. A changed host, command, transport, credential name, filter or wrapped agent is a `changed` row. A URL's path or query, a header value and a command's arguments are digested (`endpoint_sha256`), never printed. Identity is the import, never the spelling: a same-named local function, a shadowed, reassigned or patched import, a vendored `google/adk` package and another package's `load_memory` are not built-ins. A helper whose return cannot be resolved, an `.as_tool` on a value not identified as an agent, an object changed after it is built, and an `mcp_servers=` member that is not an MCP server stay named limits; a part of an object's identity that is not read (a host from a builder's parameter) leaves the binding present but makes a change to it `not_established`. An SDK agent's `mcp_servers=`, which the comparison did not read before, is read member by member. Read for `diff --application` only: `scan` reads what it did. `application_comparison_schema_version` 0.3 → 0.4. On the 49-PR development corpus (`benchmark/application-q2/`) against `main` at `7c160cfb`, every status is unchanged, pull requests with any row go from 9 to 10, and no row is dropped: cuppibla/adk_tutorial#9's `update_plan` is now an established `added` row (its base `google_search` was unresolved), buriro-ezekia/cinescout-ai#9 gains a `not_established` row naming the base MCP toolset's host, transport and `Authorization` source, and 24 (adk_tutorial#7) and 41 (Lling0000/OpenCMO#41) limits are gone. zendah21/capstone_project#3 is `compared` with `load_memory`, `inspect_schema` and `execute_sql` added. On 131 open SDK/ADK pull requests every status is unchanged; 7 gain rows (14 new rows, 2 of them `not_established`, and 3 candidate removals now established, all checked against source), and 2 gain a named `mcp_servers` limit where a server list was not read before, as 2 development members do. What remains a limit: a helper with more than one `return`, an agent tool whose agent has no literal name, a re-exported built-in, a pydantic settings class, and #874's caller-supplied lists. (#910, #866)
Expand Down
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ CI runs `python scripts/generate_schemas.py --check` and fails fast
with a unified diff if a committed schema drifts from the live model.
The same drift is also caught by `tests/test_schema_roundtrip.py`, so
your test suite will reject the change locally before CI does.
A `docs/*-schema*.json` file that a release tag carries is published and
keeps its bytes: it is pinned in `scripts/published_schema_pins.json`, and
the generator writes nothing and exits 1 rather than change one. Put the
change in a successor schema version instead (#1044). The schemas are
rendered by the locked pydantic (`constraints/dev.txt`); another version can
render a published file differently, and the generator refuses that too.
After a report or contract change, run
`python scripts/regenerate_goldens.py`, inspect the semantic diff, then run
`python scripts/regenerate_goldens.py --check` and the affected behavioral
Expand Down
7 changes: 7 additions & 0 deletions STABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,13 @@ that cannot be expressed additively needs `2.0`. A deprecation cycle is counted
in **shipped releases**, never in elapsed time on unreleased `main` — a
deprecation nobody could install gave nobody the chance to migrate. Every
published `docs/report-schema.v<version>.json` keeps its bytes forever.
So does every other `docs/*-schema*.json` a release tag carries: each is
pinned by sha256 in `scripts/published_schema_pins.json`, the generator refuses
to rewrite one, and a change needs a successor schema version (#1044). A
release bump no longer rewrites agent-handoff v9, agent-result v3 or
agent-boundary-result v3: their schema default for `tool.version` stays
`1.2.0`, the value v1.2.0 published (v1.0.0 and v1.1.0 each stamped its own
version there), while the field itself still reports the running version.

The freeze is conditional: it holds while no breaking change lands, and a
breaking change restarts it and invalidates every qualification receipt
Expand Down
10 changes: 10 additions & 0 deletions docs/release-runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,16 @@ The shape that holds:
rehearsal is green: `release-rehearsal.yml` for a qualified release; for an
advisory one, Release Engine Smoke and then `release-advisory-rehearsal.yml`
on the same commit.
The change that sets the version regenerates with
`python scripts/generate_schemas.py` and then runs
`python scripts/generate_schemas.py --pin`, which pins in
`scripts/published_schema_pins.json` every `docs/*-schema*.json` the tag
will publish. While the version has no tag,
`tests/test_published_schema_freeze.py` fails until every one is pinned, so
nothing reaches the tag unpinned. A version bump changes no schema document
(#1044). If the generator refuses because one would change, either a model
changed a published file, which needs a successor schema version, or the
installed pydantic is not the locked one.
4. Push the tag: `git tag v0.16.0 && git push origin v0.16.0`.
5. The `verify` job runs unattended.
6. Approve the `pypi` environment gate on the `publish` job, using the readiness
Expand Down
16 changes: 12 additions & 4 deletions docs/report-1-0-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,18 @@ receipt collected against the frozen schema — see
consumer gates on, or changing what a value means is a major.
3. **Every published schema URL keeps its bytes forever.** `docs/report-schema.v<version>.json`
is immutable once committed; new content goes to a new version. This is
enforced by byte hash in `tests/test_effect_coverage.py`
(`_PUBLISHED_SCHEMA_SHA256`), because "committed equals generated" — which
is all `generate_schemas.py --check` proves — does not prove that a content
change moved the version.
enforced by byte hash, because "committed equals generated" does not prove
that a content change moved the version. Every `docs/*-schema*.json` a
release tag carries is pinned in `scripts/published_schema_pins.json`: the
set is derived from the tags, not from a hand list, and a published file
that is missing, unpinned or changed fails `generate_schemas.py --check` and
`tests/test_published_schema_freeze.py`. The generator refuses to rewrite
one and names the successor-version route instead (#1044). No schema
embeds the running package version any longer, so a release bump leaves
them all byte-identical: agent-handoff v9, agent-result v3 and
agent-boundary-result v3, which did, keep the `tool.version` default of
`1.2.0` that v1.2.0 published (v1.0.0 and v1.1.0 each stamped its own
version there).
4. **A deprecation cycle is counted in shipped releases, never in elapsed
time on unreleased `main`.** A surface deprecated in `1.n` may be removed no
earlier than `2.0`, and only if `1.n` was actually published. Time a
Expand Down
Loading
Loading