Skip to content
Merged
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
18 changes: 9 additions & 9 deletions PAA.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ where the implementation deliberately stops.
| `AutonomyEvent`, `EventStore` | Autonomy event | Uses the contract's fourteen-field event representation and an implementation protocol with explicit ordering, transaction, uniqueness, and append-only semantics. |
| `SqliteEventStore` | Evidence/event log substrate | Supplies the default append-only SQLite store, including storage-level update/delete rejection. |
| `store_evidence`, `verify_evidence` | Evidence record binding | Content-addresses exact evidence bytes with SHA-256 and fails closed on missing or changed bytes. |
| `import_events` | Archive replay | Imports already validated contract-shaped events without regenerating identifiers or timestamps. The Scout conformance capture proves field and projection continuity across extraction. |
| `paa-contracts` conformance suite | Published contract | Checks schema vocabulary, declarations, event histories, evidence addressing, invalid semantic cases, and the Scout pre-cutover capture against the same packaged corpus. |
| `import_events` | Archive replay | Imports already validated contract-shaped events without regenerating identifiers or timestamps. The legacy conformance capture proves field and projection continuity across extraction. |
| `paa-contracts` conformance suite | Published contract | Checks schema vocabulary, declarations, event histories, evidence addressing, invalid semantic cases, and the pre-cutover capture against the same packaged corpus. |

## Lifecycle coverage

Expand All @@ -44,13 +44,13 @@ field loss, that the lifecycle can produce them, that content addresses are
re-derived from bytes, and that runtime-owned negative cases fail for the
published reason.

`examples/scout-archive/pre-cutover-capture.json` adds the consumer-boundary
proof. It was generated with Scout's pre-cutover lifecycle implementation at
`examples/legacy-archive/pre-cutover-capture.json` adds the consumer-boundary
proof. It was generated with the source consumer's pre-cutover lifecycle implementation at
commit `721c37facac64f12a164e510c9a0aa647a960cba`, then imported into the
extracted runtime. The test reproduces its event rows, motion projection, and
resolved position exactly.

Scout's production `autonomy_events` table contained zero rows at cutover. The
The source consumer's production `autonomy_events` table contained zero rows at cutover. The
capture is therefore evidence from the real pre-cutover implementation, not a
claim that a production autonomy transition occurred. Keeping that distinction
in the artifact is part of the citation bar.
Expand All @@ -61,7 +61,7 @@ in the artifact is part of the citation bar.

The runtime validates evaluator identities and producer registration but does
not run graders, judges, invariants, or human-review systems. Those are consumer
domain code. Scout, for example, owns the producers named by its declarations.
domain code. Each consumer owns the producers named by its declarations.

### Promotion-rule evaluation

Expand Down Expand Up @@ -91,8 +91,8 @@ effect committed in a consumer's separate database. A demotion can land after
the read and before that effect. The event history remains valid, but the
in-flight effect used a stale permit.

`EventStore` exists for consumers that cannot accept that window. Scout
implements the protocol over its own database, so the position read authorizing
`EventStore` exists for consumers that cannot accept that window. A consumer can
implement the protocol over its own database, so the position read authorizing
a publication and the publication claim share one `BEGIN IMMEDIATE` lock
domain. The general runtime exposes this escape hatch; it cannot manufacture
cross-database atomicity for every consumer.
Expand All @@ -110,7 +110,7 @@ The accurate claim is:

> `paa-runtime` implements PAA's declared autonomy-transition lifecycle and
> passes the published conformance corpus, including replay of a history
> captured from Scout's pre-cutover implementation.
> captured from the source consumer's pre-cutover implementation.

It is not a claim that the runtime implements evaluation, worker attestation,
or every consumer's governed effect.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ uv run mypy src/paa_runtime

The implementation-to-spec mapping, including explicit non-matches, is in
[`PAA.md`](PAA.md). The conformance corpus also includes a contract-shaped
history captured from Scout's pre-cutover implementation. Scout production had
history captured from the source consumer's pre-cutover implementation. Its production database had
zero autonomy events at cutover, so that artifact is deliberately labeled an
implementation capture rather than production transition history.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Replay a history captured from Scout's pre-cutover implementation."""
"""Replay a history captured from a consumer's pre-cutover implementation."""

from __future__ import annotations

Expand All @@ -12,10 +12,10 @@
from paa_runtime.replay import import_events


def test_scout_pre_cutover_capture_reproduces_its_projections(
def test_legacy_pre_cutover_capture_reproduces_its_projections(
runtime_config: RuntimeConfig,
) -> None:
archive_path = contracts.EXAMPLES_ROOT / "scout-archive" / "pre-cutover-capture.json"
archive_path = contracts.EXAMPLES_ROOT / "legacy-archive" / "pre-cutover-capture.json"
archive: dict[str, Any] = json.loads(archive_path.read_text(encoding="utf-8"))

assert archive["capture_kind"] == "pre-cutover-implementation"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"production_event_count_at_cutover": 0,
"events": [
{
"actor": "scout-capture-operator",
"actor": "capture-operator",
"created_at": "2026-08-11T19:07:04.074003Z",
"declaration_version": 1,
"event": "motion_proposed",
Expand All @@ -20,7 +20,7 @@
"to_position": "hotl"
},
{
"actor": "scout-capture-operator",
"actor": "capture-operator",
"created_at": "2026-08-11T19:07:04.083086Z",
"declaration_version": 1,
"event": "motion_approved",
Expand All @@ -36,7 +36,7 @@
"to_position": "hotl"
},
{
"actor": "scout-capture-operator",
"actor": "capture-operator",
"created_at": "2026-08-11T19:07:04.083179Z",
"declaration_version": 1,
"event": "position_changed",
Expand Down Expand Up @@ -66,7 +66,7 @@
"motions": [
{
"approved_at": "2026-08-11T19:07:04.083086Z",
"approved_by": "scout-capture-operator",
"approved_by": "capture-operator",
"approved_reason": "approved captured pre-cutover promotion",
"declaration_version": 1,
"evidence_ref": "evidence/paa/76ea9113c9000f7fab53d48dd9149b101686cceda42798f84469a050f1294315/evidence.json",
Expand All @@ -75,7 +75,7 @@
"from_position": "hitl",
"motion_id": "36205f56-87a5-4343-9d2c-44308b23a1a8",
"proposed_at": "2026-08-11T19:07:04.074003Z",
"proposed_by": "scout-capture-operator",
"proposed_by": "capture-operator",
"proposed_reason": "captured pre-cutover promotion",
"rejected_at": null,
"rejected_by": null,
Expand Down
2 changes: 1 addition & 1 deletion examples/refund_quickstart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ The script creates an isolated temporary database and evidence tree, proposes
repository. `refund_approval.v1.yaml` remains in the shared contract corpus so
the quickstart, conformance suite, and paa.dev schema reference use one fixture.

This is adoption-oriented synthetic pedagogy. Scout's separately labeled
This is adoption-oriented synthetic pedagogy. The separately labeled
pre-cutover capture is the cross-implementation evidence artifact.
2 changes: 1 addition & 1 deletion packages/paa-contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ PAA is implementation-neutral by construction, which is only true if the contrac

```
paa-contracts ← paa-runtime (reference implementation, conformance suite)
← Scout (production consumer, task-schema validation)
← consumer (production consumer, task-schema validation)
← your implementation
```

Expand Down
2 changes: 1 addition & 1 deletion packages/paa-contracts/hatch_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
("schemas", "schemas"),
("examples/paa-tasks", "examples/paa-tasks"),
("examples/runtime-conformance", "examples/runtime-conformance"),
("examples/scout-archive", "examples/scout-archive"),
("examples/legacy-archive", "examples/legacy-archive"),
)


Expand Down
2 changes: 1 addition & 1 deletion packages/paa-contracts/scripts/verify_built_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"schemas",
"examples/paa-tasks",
"examples/runtime-conformance",
"examples/scout-archive",
"examples/legacy-archive",
)

_DATA_MARKER = "/_data/"
Expand Down
4 changes: 2 additions & 2 deletions packages/paa-contracts/src/paa_contracts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

The dependency direction is the point. An implementation depends on the
contract; the contract never depends on an implementation. ``paa-runtime`` is
the first consumer, Scout's task-schema conformance test is the second, and a
the first consumer, a task-schema conformance test is the second, and a
second implementation in any language gets its fixtures the same way the first
one does. That is implementation-neutrality made mechanical instead of
asserted.
Expand Down Expand Up @@ -135,7 +135,7 @@ class InvalidCase(TypedDict):
"schemas",
"examples/paa-tasks",
"examples/runtime-conformance",
"examples/scout-archive",
"examples/legacy-archive",
)


Expand Down
2 changes: 1 addition & 1 deletion packages/paa-contracts/tests/test_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_resolution_requires_every_artifact_root(self, tmp_path: Path) -> None:
assert contracts._missing_roots(tmp_path) == (
"examples/paa-tasks",
"examples/runtime-conformance",
"examples/scout-archive",
"examples/legacy-archive",
)

def test_the_resolved_root_is_complete(self) -> None:
Expand Down
18 changes: 8 additions & 10 deletions tests/test_declarations.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
"""Access-layer tests for paa_runtime.declarations.

Ported from Scout's tests/test_paa_declarations.py. Only the access-layer
Ported from the source consumer's declaration tests. Only the access-layer
concern comes with the package — loader output, transition extraction,
vocabulary, deployment values, evaluator-version resolution,
filename/identity invariants, and fail-closed error handling. The
schema-stage conformance class (``TestSchemaConformance``, marked
``paa_contract``) and Scout's checkout-discovery harness in
``tests/conftest.py`` do not port: that harness stays in Scout and is
``paa_contract``) and the checkout-discovery harness do not port: that harness is
being replaced separately (paa-contracts), and this package ships no
conftest.

Scout's declaration loader used to read from a checked-in
The source declaration loader used to read from a checked-in
``contracts/paa/`` directory and resolve evaluators against a
module-global ``PRODUCER_REGISTRY``. Both are now supplied by the
caller, so these tests build small declaration fixtures under
``tmp_path`` and define a local registry tuple instead of reading
Scout's repository.
the source repository.
"""

from __future__ import annotations
Expand Down Expand Up @@ -78,7 +77,7 @@
"autonomous": "offline",
}

# Mirrors Scout's outbound_content_publish.v1.yaml: active deployment,
# Mirrors the source outbound_content_publish.v1.yaml: active deployment,
# a declared scopes block, a cases promotion window.
_OUTBOUND_LIKE: dict[str, object] = {
"task": "outbound_publish",
Expand Down Expand Up @@ -113,7 +112,7 @@
},
}

# Mirrors Scout's inbound_reply_surfacing.v1.yaml: shadow deployment, no
# Mirrors the source inbound_reply_surfacing.v1.yaml: shadow deployment, no
# scopes block, a duration promotion window, and a "future" evaluator.
_DURATION_TASK: dict[str, object] = {
"task": "reply_surfacing",
Expand Down Expand Up @@ -147,7 +146,7 @@
},
}

# Mirrors Scout's canonical_promotion.v1.yaml: disabled deployment, no
# Mirrors the source canonical_promotion.v1.yaml: disabled deployment, no
# scopes block, a fifty-case promotion window, and a "future" evaluator.
_CASES_TASK: dict[str, object] = {
"task": "canonical_promotion_task",
Expand Down Expand Up @@ -193,8 +192,7 @@
def _write_declaration(directory: Path, document: dict[str, object]) -> Path:
"""Write *document* as ``<task>.v<version>.yaml`` under *directory*.

Mirrors Scout's ``_write_versioned_declaration`` technique (see
Scout's tests/test_content_publishing.py:63) for building an
Mirrors the source suite's versioned-declaration technique for building an
isolated one-file declarations directory in a test.
"""
path = directory / f"{document['task']}.v{document['version']}.yaml"
Expand Down
24 changes: 12 additions & 12 deletions tests/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
show/list workflows — the core of the event-sourced PAA autonomy control
plane.

Ported from Scout's tests/test_paa_service.py. Scout's in_memory_state
Ported from the source consumer's service tests. Its in_memory_state
fixture, its module-global PRODUCER_REGISTRY, and its checked-in
contracts/paa declarations directory don't exist here — this package
ships no conftest, so a real SqliteEventStore against a tmp_path database
plus small local declaration fixtures (mirroring tests/test_declarations.
py's approach) stand in for them.
ships no conftest, so a real SqliteEventStore against a tmp_path database plus
small local declaration fixtures (mirroring tests/test_declarations.py's
approach) stand in for them.
"""

from __future__ import annotations
Expand Down Expand Up @@ -60,7 +60,7 @@
"autonomous": "offline",
}

# Mirrors Scout's outbound_content_publish.v1.yaml: active deployment, a
# Mirrors the source outbound_content_publish.v1.yaml: active deployment, a
# declared scopes block, hitl<->hotl promotion/demotion — the declaration
# B1's "declares scopes" branch and the scope-allowlist tests exercise.
_OUTBOUND_TASK: dict[str, object] = {
Expand Down Expand Up @@ -89,7 +89,7 @@
},
}

# Mirrors Scout's inbound_reply_surfacing.v1.yaml: no scopes block — the
# Mirrors the source inbound_reply_surfacing.v1.yaml: no scopes block — the
# declaration B1's "declares no scopes" branch exercises.
_INBOUND_TASK: dict[str, object] = {
"task": INBOUND,
Expand Down Expand Up @@ -128,7 +128,7 @@ def _versioned_config(
) -> RuntimeConfig:
"""A RuntimeConfig pointed at a one-task declarations directory cloned
from _OUTBOUND_TASK with version/initial_position overridden — mirrors
Scout's _write_versioned_declaration technique for isolating a
the source suite's versioned-declaration technique for isolating a
declaration-version bump in a test."""
Comment on lines 129 to 132
document = dict(_OUTBOUND_TASK)
document["version"] = version
Expand Down Expand Up @@ -160,7 +160,7 @@ def config(declarations_dir: Path, evidence_root: Path, tmp_path: Path) -> Runti
evidence_root=evidence_root,
registry=_REGISTRY,
db_path=tmp_path / "autonomy_events.db",
actor_env_var="SCOUT_PAA_ACTOR",
actor_env_var="PAA_TEST_ACTOR",
)


Expand Down Expand Up @@ -474,7 +474,7 @@ def test_actor_resolution_order(
evidence_file: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("SCOUT_PAA_ACTOR", "env-actor")
monkeypatch.setenv("PAA_TEST_ACTOR", "env-actor")
explicit = svc.propose(
store, config, task=OUTBOUND, scope=BLUESKY, to_position="hotl",
evidence_path=evidence_file, actor="explicit-actor",
Expand All @@ -491,9 +491,9 @@ def test_actor_env_var_name_is_configurable(
self, monkeypatch: pytest.MonkeyPatch,
) -> None:
"""B2: resolve_actor reads config.actor_env_var, not a hardcoded
SCOUT_PAA_ACTOR — a differently named variable is honored."""
monkeypatch.delenv("SCOUT_PAA_ACTOR", raising=False)
monkeypatch.setenv("SCOUT_PAA_ACTOR", "wrong-actor")
configured actor variable — a differently named variable is honored."""
monkeypatch.delenv("PAA_TEST_ACTOR", raising=False)
monkeypatch.setenv("PAA_TEST_ACTOR", "wrong-actor")
monkeypatch.setenv("CUSTOM_PAA_ACTOR", "right-actor")
assert svc.resolve_actor(None, env_var="CUSTOM_PAA_ACTOR") == "right-actor"

Expand Down
Loading