docs: record accepted phase-2 foundation contract

This commit is contained in:
2026-09-06 02:23:15 -05:00
parent 69d1bb3aa4
commit 44f257cb06
16 changed files with 21849 additions and 0 deletions
@@ -0,0 +1,364 @@
# Foundation contract candidate: schemas and semantic rules
Planning artifacts for #53 phase 2. These files are not loaded by Mosaic,
installed as policy, or approved for implementation. The owner confirmed R1-R34;
field spellings, role bundles, state transitions, and mechanisms here are proposals.
Parent: [phase-2 contract](../2026-09-06_foundation-phase2-contract.md).
Behavior: [foundation requirements](../2026-09-06_agent-project-workspace-foundation.md).
Decision history: [schema discussion](../2026-09-06_workspace-schema-and-audit.md).
## 1. What is executable here
- [Command schema](command-events.schema.json): a closed command request and
four event variants: intent, started, finished, and recovered.
- [Record schema](records.schema.json): closed payloads for eleven declarative
record kinds, including explicit references, revisions, and optional absence.
- [Command fixtures](command-events.fixtures.json): 38 shape cases and five
deliberately shape-valid forgeries requiring separate runtime rejection.
- [Record fixtures](records.fixtures.json): 38 record cases and 16 path cases.
- [Fingerprint vectors](fingerprint-vectors.json): seven restricted-domain
examples of canonical comparison, content changes, and set/order semantics.
- [Runtime schema](runtime.schema.json) and [fixtures](runtime.fixtures.json):
155 cases for mediated control/execution, messages and artifact bodies, plus
five additional shape-valid semantic forgeries.
- [Rule model](semantic-model.py) and [fixtures](semantic-model.fixtures.json):
35 synthetic reference, permission, path, acceptance and recovery cases.
- [Author checker](check.py): checks all three schemas and fixtures/models using the
existing Python `jsonschema` package, observed version 4.26.0. Installs nothing.
Run from the repository:
```bash
python3 docs/plans/foundation-v1-candidate/check.py
```
These are design checks, not product tests. Schema validation does not prove
permissions, identity, filesystem containment, durable writes, or correct
reference resolution. The ten forged/stale records intentionally pass shape
validation. A future runtime must reject them for their semantic reasons.
The checker explicitly asserts UTC calendar validity and the
`mosaic-relative-path` format. The latter rejects paths over 4096 UTF-8 bytes,
Unicode control/format/surrogate characters, backslashes, and empty/dot/parent
components. A validator that ignores required formats must not be certified
for these contracts. String length alone counts characters, not UTF-8 bytes.
Filesystem symlinks, hard links, mounts, races, and case collisions require
separate checks at the operation boundary; this formatter does not inspect files.
## 2. Reference and publication rules
A `registryRef` identifies an exact external definition by registry, ID,
revision, and digest. It is not a new system configuration location. Harness
and settings registries must reconcile with #50; agent/scope-role authority
must resolve to reviewed repository policy. Registry absence refuses an action;
no default provider, account, role, or workspace substitution is allowed.
A `recordRef` resolves to exactly one record kind, ID, owning scope, and revision.
The resolver checks the authenticated caller's access before returning content.
A reference to another scope is not membership in that scope. A record revision
cannot move its owning scope or change kind/ID. `supersedes` must name the
immediately prior revision of the same object. Reject cycles and skipped or
competing revisions, even when each individual JSON document is valid.
Project/workspace record IDs must agree with their scope IDs. A workspace has
one owning project. Workspace registration references an active project
registration for the same agent/project. A workspace mission's optional parent
is a project mission in that project. Project missions have no parent mission
in the initial design. Task mission/dependency references must resolve to
permitted work; dependency cycles and self-dependencies refuse readiness.
Cross-scope dependency information is filtered by access and grants no extra
execution authority. Cross-project execution selection is not introduced here.
Assignment agent, task, and workspace must match its execution binding. A task
may have no mission but still has purpose, criteria, and approved intent. Its
intent decision is independently authorized; neither task nor decision grants
itself permission. Decision subject/basis/evidence references must resolve and
be consistent with the requested decision kind. A delegation authorizer cannot
grant operations outside its own reviewed delegation ceiling.
Acceptance records identify exact criteria, delivered artifact revisions,
authoring identities, and an authorized non-author reviewer through the basis
artifact. The decision schema's artifact pointer is not proof of these facts.
The acceptance resolver must inspect and validate that basis. Owner checkpoints
remain distinct from ordinary reviewer acceptance.
Context marked shared by default must be a general preference explicitly
published for that purpose. An author cannot turn private content into shared
preferences merely by changing an enum. Validate publisher authority, permitted
scopes, relevance, and provenance. Fresh rejects transcript/automatic-summary
inputs regardless of whether a caller can read the old conversation.
An adapter's `verified` declaration needs evidence for the exact image/package,
mode, operation set, and tested failure behavior. Evidence references alone do
not certify the adapter. Refuse required guarantees that lack matching evidence.
## 3. Proposed permission catalog
Operations are exact strings, not glob patterns. All are default-deny.
A scope role combines allowed operations with permitted targets; registration
restrictions can only narrow that result. `restrictions:null` means no additional
restriction, not a grant. An explicit empty operations array permits none.
A path grant with `path:null` explicitly names the whole workspace root; it
still requires the operation and all ancestor grants. No empty-string root alias.
| Proposed bundle | Operations |
|---|---|
| Reader | `work.read`, `file.read`, `work.propose` |
| Contributor | Reader operations plus `file.change`, `command.execute`, `result.submit` |
| Reviewer | `work.read`, `file.read`, `audit.read`, `task.accept` |
| Coordinator | `work.read`, `work.propose`, `task.create`, `assignment.change` |
These are review candidates, not additions to `roles/`. They are nonhierarchical.
Combined roles need an explicit reviewed bundle; do not union arbitrary role
labels at launch. A reviewer never accepts its own authored delivery.
The following operations need separate explicit grants: `registration.manage`,
`conversation.read`, `conversation.observe`, `execution.launch`,
`execution.control`, `execution.transfer`, `workspace.retire`,
`workspace.reopen`, `message.send`. `audit.read` exposes only evidence the
principal is entitled to see, not every referenced payload. No bundle grants
git integration, release, credential management, destructive maintenance, or
shared policy changes. Those stay under the repository's conductor/owner rules.
Control-plane operations authenticate the requesting user/service/agent and
check its scope grant; the authorized runtime component performs them. An agent
being launched does not need permission to launch itself. Its reviewed ceiling
instead bounds the new execution's tools and actions. For assigned work, check
both the originating delegation and the acting agent's current authority.
For a workspace action, resolve the reviewed agent ceiling, project registration,
explicit workspace registration, applicable parent/workspace mission restrictions,
task restriction, and execution restriction. Intersect operations, file targets,
and network targets independently. A missing required layer refuses; never skip
an unreadable policy. Bind an action to one assignment rather than borrowing
permissions from other assignments. Recheck revocation and current approved
intent before admission, not merely at launch.
Command network permission concerns command processes. Model-provider access
is a separate, narrowly authorized runtime channel. `network:none` for a command
cannot be satisfied merely by Pi's startup `--offline` flag. No credential file,
Docker socket, host home, or shared evidence directory belongs in the command's
writable/readable sandbox solely because it exists in the configured data root.
## 4. Command and audit rules
A command request supplies a proposed execution binding and immutable command
specification reference. These are claims to validate, not trusted identity.
The gateway resolves the authenticated channel, exact live execution/session,
agent/workspace, assignment/task revision, and active generation. A client cannot
supply event producer, authorization verdict, or observed outcome fields.
The command specification is controlled evidence, not raw metadata. It must
identify the authorized executable/arguments or shell program, cwd, permitted
input sources, output policy, and applicable constraints without containing
credentials. Exact command-specification and sandbox-limit artifact schemas
remain dependent work; these reference slots are not permission for arbitrary
JSON payloads. Required artifacts must resolve and validate before execution.
Order of operation:
1. Authenticate and validate the full request, references, permissions, current
generation, approved intent, and required coverage.
2. Reserve its idempotency identity and persist the authorization and command
intent, including the exact enforced-limit reference, before issuing effects.
3. Establish the sandbox and its process-group identity. Persist observed start
separately. Start failure produces a not-started outcome, not false success.
4. Record exit/signal plus proof that the command's entire admitted process group
can no longer act. A shell's exit code alone does not prove descendants stopped.
5. Persist the command outcome. Zero exit with proven termination can mean
command success while effects remain unverified. It never means task acceptance.
6. If effects or required recording are uncertain, block dependent work and use
authorized non-destructive checks. Append recovery evidence with its real
observation time; do not edit an earlier unknown record into success.
`command.finished` permits one initial terminal observation per command ID.
Later knowledge uses `command.recovered`, naming the prior observation and the
independent check events. Unconfirmed termination requires an unknown outcome.
A confirmed failure needs a nonzero exit or a signal. No process issued means
null exit/signal and effects not-issued. A reconciled effect claim needs evidence.
The recorder rejects duplicate event IDs, reused sequence numbers, contradictory
command bindings, causal cycles, missing intents, and unauthorized producers.
JSON Schema cannot enforce these cross-record rules. Per-execution sequence
orders its observations; clocks do not create a global order across machines.
Neither metadata nor detailed evidence may contain credentials. Use allowlisted
metadata and controlled artifact references; do not dump arguments, environment,
stdout, or RPC messages into the audit stream by default. Content classification
and release of evidence are separate authorized operations. An evidence pointer
is not an exception to the no-secrets rule.
## 5. Proposed lifecycle transitions
All changes require expected revisions and authorized writers. No last-writer-wins.
These transitions describe intended work, not changes to old run evidence.
| Object | Proposed transitions and guards |
|---|---|
| Project | Active to retired only by authorized administration, with owned work safely accounted for. Reopening requires revalidated policy; cascading membership restoration is forbidden. |
| Workspace | Active to retiring closes launch/action admission. Retiring to retired requires stopped executions and reconciled/explicitly blocked effects. Reopen moves retired to active without launching work or restoring revoked grants. |
| Registration | Active to revoked blocks affected work immediately. A later regrant is a new authorized revision after current policy checks, not automatic revival. |
| Mission | Proposed to active requires approved intent. Active/blocked may proceed to ready-for-review after evidence is available. Accepted requires authorized independent acceptance; cancellation needs separate authority. Child completion alone does not accept its parent. |
| Task | Proposed to ready requires criteria, approved intent, and resolved prerequisite readiness. Ready to active requires an assignment/claim. Active may block or submit for review. Reviewer accepts exact evidence; rejection returns work for correction. Cancellation is separate from assignment Abandon. |
| Assignment | Selected to paused on approved-plan change or unresolved effects; reconciliation may select it again within delegation. Ended is terminal with an explicit completed/abandoned/revoked/reassigned/canceled reason. Reassignment creates a new assignment, not a different agent under the old ID. |
| Session | Immutable identity/scope and creation lineage. Resume creates a new execution, not a new conversation record. Fresh creates a new conversation and preserves the predecessor only as lineage, not loaded chat. |
| Execution | Admission, observed start, stopping, observed termination, and uncertainty are separate immutable events. No reusable active claim until prior work is proven unable to act. |
| Connection | Observe/control admission checks transcript and operation grants. One controller generation per execution. Explicit transfer invalidates the old generation before new input is accepted. Disconnect is not permission to start another execution. |
The early discussion's candidate task state `abandoned` is replaced in this
proposal by assignment endReason `abandoned`; task cancellation stays explicit.
This follows Q8 rather than silently ending shared work.
## 6. Storage, retry, and crash traces
Proposed ownership remains under the sole configured data root. Shared system
configuration is still `~/.config/mosaic-dev/config.json`; reviewed role authority
still lives in repository `roles/`. No runtime directory is created by this plan.
- Project/workspace metadata: `<dataRoot>/projects/<projectId>/`, with one
workspace metadata subtree per workspace ID. Immutable revisions and checked
current indexes are separate. Shared agent definitions stay outside projects.
- Working files: `<dataRoot>/workspaces/<workspaceStorageId>/`, where an immutable
workspace record resolves storage ID. Display names never construct paths.
- Conversations: `<dataRoot>/sessions/<sessionStorageId>/`, resolved through the
scoped conversation registry, never latest-file discovery.
- Evidence: `<dataRoot>/runs/<runId>/`, with immutable inputs and uniquely named
event/result artifacts. The trusted host recorder writes; workers cannot.
- Current indexes, first-use tombstones, request decisions, and active claims:
`<dataRoot>/state/foundation/`. These are derived/control records, not a second
system configuration. Private source content retains publisher-controlled
storage and is selected into immutable launch inputs only when authorized.
A proposed host-side per-scope writer serializes admissions and checks expected
revisions. Unique temporary files, flush, atomic rename, and parent-directory
flush publish one durable transaction decision; referenced payloads are written
before that decision. Readers accept only payloads named by a complete commit
record. Indexes rebuild from committed decisions. Never infer atomicity across
several independent renames. Filesystem support and process-lock behavior need
failure tests before this design can be called implemented.
| Crash point | Recovery rule |
|---|---|
| Before committed intent | Do not issue the action. Uncommitted artifacts do not grant authority. |
| After intent, before observed start | Outcome is not assumed not-started. Reconcile the exact process/container identity before considering replay. |
| After start, before result | Block dependent work; inspect the recorded process group and authorized evidence. Timeout of Compose or an RPC acknowledgement alone is insufficient. |
| After result, before response | Return the existing request decision/result after reauthentication, never run the action again. |
| During claim transfer/replacement | Reject both stale input and conflicting launch until committed generation and old-process disposition are established. |
| During index publication | Rebuild from committed revisions; do not silently select another session or infer first use from absent files. |
| During plan revision | Pause affected work until its expected intent/assignment revisions reconcile; unaffected valid work can continue. |
Idempotency identity binds authenticated requester, request ID, operation, scope,
and normalized request digest. The same ID with different content refuses.
A repeated request reuses its existing outcome only after current visibility
checks. Automatic retention cleanup is not introduced. Keep request/first-use
history while the scope remains available; explicit pruning must retain enough
non-secret tombstone metadata to refuse ambiguous replay or missing-session
replacement. Owner-authorized data-root reset remains destructive and does not
promise restoration of those indexes. Never silently adopt leftover files after
reset into a newly registered scope.
Legacy adoption preserves original bytes and source identity before any format
conversion. Produce a new reviewed mapping/conversion receipt. Missing lineage,
ambiguous source ownership, or insufficient permission refuses adoption.
### 6.1 Fingerprint projection and vectors
The proposed comparable projection has exactly `hashScopeVersion`, `agentId`,
`soulDigest`, ordered `instructions`, ID-sorted `skills`, `harness`,
`modelSettingsDigest`, and `roleConfigDigest`. Instruction entries contain ID
and content digest. Skill entries contain ID, declared package version, and
content digest. Harness contains canonical ID, actual pinned package version,
and package/image content identity. Reject duplicate component IDs.
Digests represent the resolved operative content, not only a filename or an
administrative revision number. Normalize operative model/role settings through
their strict source schema; retain their original source revisions separately
in the full launch manifest. Do not include timestamps, display labels, private
user context, chat, progress, or credential bytes/hashes in this base projection.
Approved account selection is a non-secret launch binding, not credential content.
Serialize the projection with RFC 8785 and hash its UTF-8 bytes using SHA-256.
The seven vectors use only ASCII keys/values and safe integers. The author
checker proves those examples and skill-set sorting, not a general-purpose JCS
implementation. Instruction order changes the hash; skill inventory order does
not. Changed SOUL, skill version, model settings, or role configuration changes
it. The full launch manifest remains distinct and includes work-specific input
identity. Cross-interface notification uses the same base comparison, never
hash equality as proof of live process state.
### 6.2 Compatibility boundary with #50
The author re-read the existing auth/provider/harness plan. Its canonical harness
IDs are executable names, so registry references remain dynamic IDs such as `pi`,
not an enum of providers or a host executable chosen through PATH fallback.
The old examples spelling `claude-code` do not override its later explicit
owner ruling that the ID is `claude`.
Settings references point to the centrally authored settings profile. Account
references remain a provider/account pair, with each component using the ID
format; the combined slash-separated pair is not a filesystem path or an `Id`.
There is no independent account registry per project or workspace. Endpoint
restrictions resolve canonical provider/approved network definitions; they do
not register the provider again for each assignment.
The proposed agent-definition record is a versioned resolved representation of
reviewed identity, not a second mutable source alongside `agent.json`. An exact
reviewed-source binding and migration adapter are required before adopting this
schema. Likewise, schema `registryRef.revision`/digest are proposed identity
requirements; current #50 records do not already supply every such field.
A cached projection cannot authorize changes absent from its canonical source.
#50's per-seat generated files need reconciliation with R16's stable per-execution
launch inputs before concurrent scoped launches. Central credential ownership,
profile ceilings, explicit account choice, no identity fallback, and no writes
to default harness homes remain binding. Credential values and their hashes stay
out of these manifests. In-session account swapping remains deferred, not enabled
by a controller's access to raw engine commands.
OAuth refresh gate 7 remains unresolved. No refresh/login/credential operation
was performed during this reconciliation. A future launch requiring refresh
must refuse until the authorized registry/materializer path meets its gate;
this contract does not invent a refresh protocol or resume #50 implementation.
## 7. Managed interface ruling and completed candidate
The shapes and rules above do not establish how terminal input is mediated.
The pinned documentation describes native Pi TUI commands and a separate RPC
mode. Current Mosaic starts native TUI or print mode; it does not implement a
shared authenticated control service or arbitrary attachment to a live RPC
process. RPC events alone are not a pre-action permission gate.
Jason answered **Q28 A**: a Mosaic-controlled terminal backed by Pi is acceptable.
The same operations are intended for later desktop/web clients; native screen and
shortcut parity is not required initially. No runtime change follows from the ruling.
The [runtime contract](RUNTIME.md) and [runtime schema](runtime.schema.json) now
cover execution/claim/control records, typed requests/responses, routing, launch
inputs, command specifications/limits and concrete evidence bodies. The
[runtime fixtures](runtime.fixtures.json) include positive conditional branches
and negative cases. The [synthetic rule model](semantic-model.py) with its
[fixtures](semantic-model.fixtures.json) checks proposed reference/permission and
recovery rules, not actual observations or enforcement. The existing checker
runs them together and checks catalog/projection consistency.
New project/workspace/mission/task change, mission acceptance, decision issuance
and adoption operations are separate explicit grants, not silent bundle expansion.
All proposals now go to owner review. No capability is admitted merely because
its record passes JSON validation.
## 8. Owner-review and first-increment proposal
Start with the [owner-review package](REVIEW.md). It traces D1-D16, separates
checked proposals from unproved runtime capabilities, and requests a phase-2 verdict.
Refined first-increment recommendation: a read-only scope/permission inspector
for a coherent synthetic bundle, explicitly labelled preview with no live grants.
Owner tests two workspaces, exact membership, ambiguity, stale/missing references
and widening refusals, with no data-root/credential/session changes. Live metadata
publication is not yet the smallest safe increment: it also needs trusted writing,
retention and closure of legacy broad-mount bypasses. This refines, rather than
implements, the earlier metadata-registration proposal.
Phase-2 acceptance, Archify mapping, distinct-agent gap review and an implementation
charter remain separate gates. Author checks are not independent approval.
@@ -0,0 +1,153 @@
# #53 phase 2: owner review
**Owner: Jason. Status: phase-2 planning baseline accepted, 2026-09-06.**
This is a design contract, not a claim that Mosaic now enforces it. The existing
agent launch remains a limited legacy checkpoint. Q1-Q28 establish the behavior
requirements; this package proposes the exact records and operating rules.
## What you are approving
- One reusable agent identity, explicitly registered into projects/workspaces.
Each workspace has one project. Membership, dependencies and conversation
visibility remain separate.
- Resume by default; automatic announced first conversation only on genuine first
use. Fresh excludes old chat and rebuilds authorized work context. Damaged history
causes an error. Existing legacy history needs explicit reviewed adoption.
- Standard nonhierarchical permission bundles, narrowed by registrations, mission,
task and execution limits. Administrative/control powers are separate grants.
No task combines other assignments' permissions or accepts its own delivery.
- **Q28 A:** a Mosaic-controlled terminal backed by Pi. The broker, not terminal
shortcuts, engine text or client state, owns admission, control and routing.
One controller; explicitly authorized observers and transfer.
- Invocation-level command evidence with real enforced limits. No promise to trace
every internal file/network operation. Stopping, recovery, privacy and recording
remain mandatory even when a command reports exit zero.
- Append-only evidence, serialized publication, safe refusal and no blind replay.
Stable launch inputs; non-blocking shared-configuration notices recommend Fresh.
You are **not** approving implementation, migration, live policy changes, new
credentials, a release, mapping, independent-review acceptance, commit/push or
issue closure. Those retain their separate gates. This goal is complete only
when you accept the phase-2 contract, not when its author says the checks pass.
## Review order
1. This summary and the proposed first increment below.
2. [Permission bundles and publication/lifecycle rules](README.md).
3. [Mediated operations, evidence and implementation gates](RUNTIME.md).
4. The exact [declarative records](records.schema.json),
[command events](command-events.schema.json), and
[runtime/control/artifact shapes](runtime.schema.json), as needed.
The schema types are closed. Runtime schema includes 16 runtime/artifact kinds,
28 control operations, prompt/stop/dialog input and common responses. The separate
command request completes the 29-operation catalog. Source registry references
remain dynamic identifiers; these files are not a new live registry.
## Checks and their limits
Run the author checks with:
```bash
python3 docs/plans/foundation-v1-candidate/check.py
```
- 38 command shape cases, 38 declarative record cases and 16 path cases.
- Seven restricted-domain hash vectors, also checked against the projection type.
- 155 runtime/control/artifact cases, including valid conditional branches and
refusal shapes. Zero controller generation is invalid; actual stale-generation
refusal is a state check, not something JSON Schema alone proves.
- 35 synthetic reference, permission, path, acceptance and claim/recovery model
cases. These execute proposed rules against fixture assumptions, not live facts.
- Ten deliberately shape-valid forgeries remain shape-valid. A trusted runtime
must reject their false authority, bindings, hashes or assertions.
The checker also compares shared definitions and checks that the operation catalog
has request coverage. Fixtures are not an authenticated run, a coherent deployed
registry or a sandbox test. The reference model assumes its input observations;
it cannot authenticate a producer or observe a real process. No independent agent
has approved this package. Full JCS implementation/Unicode vectors and live
failure-injection/security tests belong to the separately chartered implementation.
## D1-D16 reconciliation
“Candidate” below means a review proposal covers the question, not that code is
implemented or the owner has approved every field.
| Design question | Candidate location and remaining proof gate |
|---|---|
| D1: scope/hierarchy | Record schemas and README reference/lifecycle rules. Actual parent, mission-completion and acyclicity checks required. |
| D2: Fresh recovery content | Context classifications, launch inputs and RUNTIME recovery projection. Actual publisher/loader provenance and no-old-chat tests required. |
| D3: delegation/visibility/revocation | Permission bundles, delegation basis and current-authority rules. Actual identity, ceiling, propagation and affected-work stopping tests required. |
| D4: genuine first use/Resume | Explicit initial/resume/fresh records, snapshot/leaf binding and historical markers. Damaged-state and initial-bootstrap tests required. |
| D5: Abandon/prerequisites | Typed work selection and assignment changes; assignment-only Abandon and independent reviewer rules. Interruption/authorization tests required. |
| D6: storage/retirement/adoption | README storage and RUNTIME retention/reopening/adoption rollback rules. Real protection, mapping and recovery tests required. |
| D7: conflicts/connections/replacement | Claim, connection, control and termination shapes plus synthetic traces. Actual single-controller, concurrency and full-cohort stopping proof required. |
| D8: uncertainty recovery | README crash traces, RUNTIME evidence, synthetic claim model. Files/processes/journal failure injection required. |
| D9: audit failure/integrity | Command events, classified artifacts, publisher ordering and outage rules. Trusted durable writer tests required. |
| D10: shared config/#50 | README compatibility and RUNTIME stable snapshots. Current #50 per-seat materialization and OAuth gate 7 remain external constraints. |
| D11: taskless work/command coverage | Closed command/control shapes and Q27 rule. No unassigned file changes; actual command isolation/recording required. |
| D12: adapter enforcement | Q28 A architecture and capability matrix. Pinned docs are evidence of documented APIs, not complete mediation. |
| D13: context/privacy | Context-source classifications, launch inputs, artifact visibility and routing. Actual loader/output/recipient tests required. |
| D14: shared interface | Q28 A plus complete operation/request catalog and common response codes. CLI presentation cannot create extra authority. |
| D15: plan changes/autonomy/acceptance | README lifecycle/reference rules, request binding, decision/delegation basis and synthetic acceptance cases. Current-intent, non-author reviewer and evidence checks must be enforced. |
| D16: fingerprints/notices | Projection, vectors and RUNTIME event-triggered comparison. No silent reload or continuous-state claim; real delivery/adapter checks required. |
The requirement source remains R1-R34 in the foundation plan. Independent mapping
and gap review may find defects in this candidate; changes must preserve owner
rulings or return for a new owner decision.
## Technical gates that acceptance does not waive
1. Actual mediation, narrow mounts/egress, credential separation and stopping.
2. Authentic, durable publication and safe recovery/idempotency.
3. Conversation/context/output privacy and current permission checks.
4. Reset/prune protection for newly required state/evidence. Current scripts have
not gained the proposed protections.
5. Reconciliation with #50's canonical definitions and stable per-execution inputs;
no credential refresh experiment was performed and OAuth gate 7 is still open.
6. **Legacy bypass closure:** current broad-mount/native launch paths cannot remain
an ungoverned route into protected foundation state. Before activating that state,
refuse those paths or establish a separately approved isolation boundary. Do not
assume a new broker makes the old adapter safe.
None requires changing a settled owner behavior today. If implementation research
cannot meet a gate with the selected adapter, stop and report instead of weakening
it or silently upgrading the pinned engine.
## Recommended first user-testable increment
**A read-only foundation scope/permission inspector using a synthetic bundle.**
Example future spelling: `mosaic foundation inspect <bundle.json>`. The later
charter settles the executable/wrapper. It validates a coherent candidate graph,
resolves exact project/workspace membership and previews the permission intersection.
Output must say **preview: no live registrations or permission grants**.
Owner test:
1. Inspect one project with two workspaces and an agent registered only in the first.
2. See the exact authorized first-workspace view and refusal for the second.
3. Try ambiguous IDs, missing parents, stale revisions and an overbroad grant;
each must fail clearly rather than select a default.
4. Confirm no data-root state, credentials, sessions or running agents changed.
This refines the earlier metadata-registration recommendation: live persistence
also requires publisher, retention and legacy-bypass protections. A read-only
inspector supplies a small usable checkpoint without pretending those already
exist. It requires a coherent semantic fixture bundle, not simply combining the
independent shape fixtures in this directory.
Only after phase-2 acceptance, separately approved mapping and independent gap
review should that increment receive an implementation charter. Owner testing
then precedes any dependent increment.
## Owner verdict
Jason explicitly answered **"accept phase 2"** after the plain-language
clarification that this accepts the plan, not a security certification or
implementation mandate. Receipt recorded at 2026-09-06 06:52 UTC.
Goal issue-53-phase2 is satisfied. Technical mapping, independent review and
implementation remain separately authorized phases; none starts automatically.
@@ -0,0 +1,319 @@
# Mediated runtime contract — phase-2 review candidate
Status: proposed technical contract after Jason's **Q28 A** ruling. No component
below has been installed. Pi remains the engine. A Mosaic-controlled terminal
is acceptable; native Pi screen/shortcut parity is not an initial requirement.
The same authorized operations are intended for later desktop/web clients.
This file completes the operation and evidence rules alongside [the package
rules](README.md) and [runtime shapes](runtime.schema.json). Closed JSON shapes
are necessary, not sufficient. `check.py` checks synthetic examples, not a live
permission system. Fixtures are independent shape cases, not one valid run graph.
## 1. Components and authority
| Component | Owns | Must not do |
|---|---|---|
| Mosaic client | Scope display, user interaction, authenticated requests, authorized rendering | Hold client-private task truth, set trusted actor/producer, bypass the broker |
| Control broker | Authentication binding, policy intersection, current claims/controllers, admission, routing | Treat agent text or engine events as approval, fall back to another scope/account |
| Trusted publisher | Serialized immutable records, artifact index, receipts, recoverable projections | Accept a worker's producer label as proof, acknowledge an undurable commit |
| Engine adapter | Exact Pi instance/session, pinned configuration, reviewed tool bridge | Expose raw engine control channels, discover ambient extensions/context |
| Command gateway and supervisor | Enforced command limits, process cohort identity, observations and stopping | Mount credentials, host control endpoints, private engine home, or shared evidence into command jobs |
The broker/publisher/supervisor are runtime infrastructure, not a new autonomous
conductor agent. Git, credentials and shared policy retain conductor/owner
authority. This design does not add a background goal scheduler.
Initial ingress proposal: a local owner client over a private, ownership-checked
local channel. Bind owner identity through OS credentials and reviewed bootstrap
policy, not a JSON principal ID. Sandboxed engines/jobs must not reach that owner
control endpoint. Agent/service ingress uses a distinct, execution/delegation-bound
channel with narrower operations. Provider API credentials are never control-plane
credentials. Remote ingress is refused until an authenticated mapping is approved
and tested; a future web interface is not permission to trust HTTP actor headers.
A compromised host owner/root is outside the sandbox boundary, not something this
contract claims to contain.
## 2. Wire, operations, and receipts
The proposed control transport uses bounded UTF-8 JSON frames. Reject duplicate
keys, non-finite numbers, unknown fields, bad encodings and metadata frames over
1 MiB before admission. No shell expansion, URL fetching or host-path resolution
is implied by an argument. Sidecar body ingestion is authenticated, private,
bounded by the selected operation/profile, and correlated to the request. It
cannot publish work or grant authority independently. Validate content and digest
before replacing a staging handle with a trusted artifact reference.
`controlRequest` defines request ID, target scope, expected record references,
optional assignment reference, exact operation and closed parameters. Actor,
producer, authorization and outcome come from the service. Public ingress validates
only its request sub-schema; a valid runtime-record or response object is never
accepted as a client request. `command.execute`
retains its separate command request shape. `inputRequest` is exclusively for
prompt, stop and approved dialog replies, with execution/connection generations.
All 29 permission names have a request contract: 28 control operations and the
separate command request. Descriptive CLI commands are wrappers, never another
permission system. JSON stdin/file forms submit the same request; they do not
allow arbitrary engine JSON. No new command described here exists today.
| Family | Required interpretation beyond shape |
|---|---|
| Project/workspace create/change | Proposed ID/payload is not a published envelope. Validate exact record-kind payload, owning scope, lifecycle and reserved/delegated target IDs; publisher supplies envelope/authorization. Workspace creation needs an existing exact project. |
| Mission/task create/change | Preserve single-parent ownership, mission policy intersection, dependency acyclicity and current approved intent. A proposed accepted status cannot bypass acceptance. |
| Assignment change | Null current reference explicitly requests creation; otherwise exact current ID/revision must match. New assignment does not abandon existing assignments. |
| Registration manage | Explicit creation/update; grantee identity, parent ceilings, allowed role and delegation must resolve. Never infer membership from creation or dependency. |
| Work read | `records` returns authorized work records; `execution-status` returns safe execution metadata; `configuration` runs the shared hash check. No transcript, account secret or private launch-input dump. |
| File read/change | Only the bound workspace, permitted path components and current limits. Contents/entries are distinct views. Put with null expected digest requires absence; delete requires an existing matching digest. No root deletion. |
| Work propose/result submit | A proposal is not approved work; a submitted result is not acceptance. File/work changes require a recorded assignment. |
| Task/mission accept | Exact subject/criteria revisions, independently authorized reviewer, verified evidence and current approval authority. |
| Decision issue | Validate decision-kind basis and issuing authority. Creation uses a new ID; revision/revocation requires the exact current decision in expectedRefs and the same immutable identity/scope. A model cannot issue owner approval because it can write prose or name the owner. |
| Conversation read/observe | Check separate conversation grants on each page/stream. Record visibility never auto-loads a transcript. |
| Execution launch/control/transfer | Apply sections 3-4, not native engine launch/attach semantics. |
| Workspace retire/reopen | Stop admission immediately; finalize retirement only after safe stop/reconciliation. Retain files/history. Reopen does not restore revoked grants or restart work. |
| Message send | Apply section 5 at both send and delivery. No automatic work assignment or acceptance. |
| Audit read | Resolve classified artifacts with the caller's own rights; knowing a reference is not permission. |
| Session adopt | Explicit reviewed decision plus preserved legacy snapshot; no filename-derived membership or in-place historical conversion. |
New creation/change/acceptance/adoption operation names are **separate explicit
grants**, not implicit additions to Reader/Contributor/Reviewer/Coordinator.
In particular `mission.accept` does not follow automatically from `task.accept`.
System-scoped registration delegation requires reviewed ceiling authority and
an approved `delegation-basis`: grantee, allowed operations, existing scopes,
preallocated new target IDs, parent decision, expiry and finite use limit. The
parent's limits still intersect it. A scope role cannot redefine shared policy.
Bootstrap creation uses explicit owner authority and an auditable bootstrap
assignment/bundle; ordinary agents cannot select a bootstrap bypass. Once work
exists, changing actions bind to their recorded assignment. Taskless discussion
and inspection never silently become file changes.
`controlResponse` distinguishes succeeded, accepted, already-active, refused and
unknown. Accepted means durable admission only. Success/acceptance needs a receipt.
An already-active response exposes identity only after authorization. Pre-admission
invalid/unauthorized requests may receive an unreceipted refusal; an audit outage
or uncertain action is never represented as success. Unknown effects require
recovery, not a retry under a new ID. Outcome data is a classified reference.
Reason codes are closed: allowed, invalid-request, not-authorized, stale-revision,
missing-state, already-active, control-conflict, retired, unsupported-capability,
audit-unavailable, unknown-effects, unsafe-replacement, request-id-conflict,
expired. Client copy must distinguish these without leaking private state.
Request identity is bound to authenticated principal, instance/epoch and canonical
request digest, including referenced input digests. A matching retry returns its
existing receipt/state. Different content under the same ID refuses. Private
bodies are not copied into a public request log. After a reset/lost index, old
channels/claims cannot authorize replay: recover the authoritative namespace or
require explicit reinitialization and new intent. Do not silently recreate it.
## 3. Launch, claims, sessions, and configuration
1. Authenticate, resolve exact identities/revisions and current policy. Closed or
ambiguous scope, bad config, missing evidence or unsupported adapter refuses.
2. Resolve work selection. Continue is the default; select/abandon name exact
assignments. Abandon changes only those assignments after authorization.
None permits conversation/inspection, not arbitrary changes.
3. Reserve the single `(agent, project, workspace)` claim in a serialized durable
transaction. Concurrency initially remains one per tuple; unsupported higher
tuning refuses rather than being silently ignored.
4. Resume selects the recorded current session or an explicit same-scope session.
Genuine first use creates and announces an initial conversation. Once history
or initialization is recorded, missing state is an error, not new first use.
Resume pins an immutable pre-load snapshot and selected leaf (null only for a
verified empty history). Verify the engine loads that history/branch; unsupported
selection refuses. These private hashes are not part of the comparable base.
5. Fresh creates a new conversation with no old chat/automatic summary. Metadata
may retain predecessor references; they are not transcript loading authority.
If an execution is active, an explicit replacement binding/generation and
authority are required. Stop admission, verify old engine and command cohorts
stopped, reconcile effects, then release its claim. Idle/abort/EOF is not proof.
6. Bootstrap the selected pinned engine in a private instance with inference and
tool admission closed. Obtain/verify native session identity and effective
settings. If the adapter cannot establish this before effects, it is unsupported.
Do not guess the latest file or invent an engine session header.
7. Publish the session binding and full launch manifest, verify snapshot digests,
then open only the approved capabilities. Record started state and actual
process identity. Incomplete bootstrap enters recovery, not automatic replay.
A manifest includes exact agent/session/settings/capability references, image
identity, comparable base projection, context snapshots, policy references and
selected assignments. `agentConfigHash` hashes the normalized base projection;
`launchManifestHash` hashes normalized `inputs`, not its own hash or timestamps.
Preserve ordered inputs. Normalize the skill set before JCS serialization.
Credentials and credential hashes are absent. Account selection is non-secret
identity metadata, resolved through #50 without fallback or in-session swapping.
Private/personal/project/work context appears only in authorized launch inputs.
Designated shared general preferences need their classification record; when they
serve as shared behavior instructions, include their content digest in the base
instruction projection. Do not omit a shared instruction merely because its
source is named USER. Context selection verifies source kind, classification,
audience and scope; a supplied category is not proof. Work context is a structured
projection of R6's approved records, preserving proposed/unverified labels.
Canonical approved configuration publication triggers a comparison for affected
executions. Also compare on client connection/reconnection and on demand. Publish
matching/changed/unknown and checked time. Missing current sources yields unknown,
not matching. Deduplicate notices by execution/old-hash/new-hash/status, retain the
current status badge, and deliver on reconnect using the recorded cursor. A local
UI toast acknowledgement changes no canonical state. No periodic model polling
or silent reload is required. These are event-triggered comparisons, not proof
of continuous live memory equality. Permission revocation acts immediately even
though ordinary configuration drift is a non-blocking Fresh recommendation.
## 4. Control and the Pi adapter boundary
A connection is observe or control. Both need their own visibility grants; control
is not implicit transcript access. There is one current controlling connection
and generation per execution. Check both execution and controller generations on
admission. Transfer is explicit, targets an authorized connection, and invalidates
old queued input before admitting new-controller input. Previously admitted work
keeps its true causal actor; transfer does not retroactively rename it.
A client disconnect does not release an execution claim or prove work stopped.
Recovery replacement without the current controller requires an explicit
qualified owner/delegation decision; knowing the current generation is not a grant.
Revocation closes affected ingress/streams and fences new unsafe actions. It also
stops affected running work, recording uncertainty until actual stopping is proven.
Unrelated independently authorized/recordable work can continue.
Proposed Pi integration: a broker-owned RPC process with a reviewed, pinned tool
bridge, no builtin tool escape, no ambient packages/extensions/templates/context,
and no raw RPC endpoint exposed to clients. The bridge requests gateway actions;
it does not execute arbitrary project commands inside the credential-bearing
engine process. Engine events report observations, never permission decisions.
This architecture is proposed, not a claim that the current adapter does it.
Initial prompt body is bounded text. Stop means fence admission and request safe
cancellation; only supervisor proof closes the execution. Dialog replies match an
outstanding approved dialog, connection, execution and generation. Unsupported
engine commands/dialogs fail visibly; no pass-through of bash, session switching,
model changes, login, package installation or reload. Additional reviewed mappings
must preserve the same contracts. Display engine text as untrusted data: no terminal
control/clipboard escape execution or web markup privileges.
## 5. Messages, queues, and late results
A message has sender, source scope, recipient agent/scope, routing mode, class,
private body reference, subject references, expiry, reply correlation and trusted
authorization. Sender identity comes from the channel. Record admission before
publishing to the queue. Delivery rechecks current sender delegation, receiver
membership, body visibility, expiry and exact target binding.
- Current-scoped-session is for permitted ordinary notices. Resolve at delivery
within that exact agent/workspace, never a global newest session. Missing or
inactive recipient can remain queued until expiry; it does not auto-launch.
- Exact-session never follows Fresh. Exact-execution requires both session and
execution identity. Results/interactive approvals use exact-execution routing.
- An approval message only references a separately valid decision. Match pending
request ID/digest, intent revision, scope and execution; text saying approved
confers nothing. Late results remain evidence for their original execution.
- Delivered means an attributable receiver acknowledgement, not task success or
acceptance. Lost acknowledgement gives unknown, not safe permission to resend.
- Retries reuse identity/content. Receiver deduplication precedes processing.
Do not claim exactly-once delivery across an unproved crash boundary.
Expiry is explicit and checked against trusted service time and policy bounds;
there is no hidden default TTL or automatic approval on timeout. Current-schema
calendar validation does not establish clock correctness.
## 6. Command limits and concrete evidence
`command-spec` contains reviewed command-profile/parameter-schema identities,
classified parameter/input references and workspace-relative cwd (null explicitly
means workspace root). No raw argv/environment appears in public metadata. The
reviewed profile fixes executable identity and parameter grammar; it is not an
agent-supplied shell wrapper. The parameter-schema reference must equal the
profile's registered schema; callers cannot substitute a weaker schema. Profile/schema bodies are immutable reviewed contract
assets identified by commit/content, not per-workspace copies of authority.
`sandbox-limits` records the selected sandbox profile, workspace grants, permitted
endpoint identities, wall-time/memory/process ceilings, and the explicit absence
of credential/host-control access. Workspace paths supplement the reviewed image,
scratch and system-filesystem rules in the profile; they do not pretend to list
all internal reads. Resolve current policy and take the least-privilege limits.
The profile must actually enforce filesystem and network boundaries, including
symlinks, mounts, /proc access, endpoint redirects/DNS and child processes.
A declaration, Pi tool allowlist or --offline flag alone is insufficient.
Initial file-effect concurrency proposal: a workspace read/write lease. Writers
are exclusive; read-only commands may share a read lease. Hold a write lease until
its cohort ends and effects are accounted for. An uncertain writer blocks affected
access, not unrelated workspaces or control-plane recovery. Finer concurrency is
a later reviewed optimization. Metadata publication uses its separate serialized
revision guard. File put uses verified content/destination and durable atomic
publication; crashes never turn absence of a result into permission to replay.
Evidence bodies now include process observations, termination proof, decision
basis and delegation basis. The publisher verifies their producer and meaning:
| Body | Required additional truth check |
|---|---|
| Process observation | Exact backend object/cohort from admitted launch, pinned image, observation attributable to supervisor; PID/name alone is insufficient |
| Termination proof | Admission is fenced and original cohort/descendants are gone. Never-started proof also establishes no start was admitted and no old producer can start it later. Absence or idle alone supplies neither proof. |
| Decision basis | Subject/criteria/evidence revisions resolve; authors derived from delivery provenance; reviewer is authorized and not an author; request-bound approvals match digest/binding |
| Delegation basis | Issuer/parent authority, target allocations, operations, expiry/use limit and ceilings all hold at use time |
| Command result | Resolve outcomeEventId to the actual command finished/recovered event with matching command/binding. There is no second outcome/signal vocabulary. Classified stdout/stderr/output references and truncation remain distinct from work acceptance. |
Artifact indexes disclose kind, scope, digest, byte length, media type, visibility,
publisher and authorization, not absolute storage paths. Index and referenced
body must agree. Metadata reads do not authorize body reads. Output capture is
bounded/classified by profile; truncation must be explicit in the result evidence,
never silently presented as full output. Credentials are allowed in neither
metadata nor evidence; do not rely on a generic secret-pattern regex as proof.
The trusted journal publishes intent before effects, started after attributable
creation, and outcome only after observed completion. Failure after effects blocks
that execution and preserves unknown state. Recovery first proves stopping and
reconciles actual files/results, then appends a new observation; it never edits an
old receipt or blindly repeats the invocation.
Launch authorization must preauthorize the supervisor's specific fail-safe fencing
and stopping of its own bound cohort when required recording fails. Attempt that
stop even if a new stop event cannot be written; do not start exploratory recovery
commands through an unaudited fallback. Signal the outage without sensitive content
and retain unknown state until recording recovers and stopping/effects are verified.
A later observed-at time records the later observation, not a fabricated stop time.
## 7. Storage, retention, and implementation admission
Use the storage ownership/publication rules in README sections 2 and 6. Mutable
indexes are rebuildable projections, not the authority. The recorded runtime
namespace/epoch and exact scope must survive restart or be declared unavailable.
No metadata search may infer scope from an engine filename or host working path.
Legacy sessions remain separate until reviewed adoption preserves the original.
Adoption rollback before activation discards only unpublished staging and appends
its failure receipt. After activation, safely stop affected work, explicitly
revise/revoke the adoption approval, and preserve all original/new history and
work effects. A now-invalid current mapping causes refusal; it does not silently
select another conversation or become first use. A separately authorized explicit
Resume/Fresh can establish the next valid mapping. Reversing work effects requires
its own reconciliation/assignment, never an automatic historical rewrite.
No new retention duration is implied: retain records and history by default.
Workspace retirement is not deletion. Evidence needed for current state, active
claims, unresolved effects, live references or required recovery cannot be pruned
without satisfying its protection/receipt rules. A pruned reference is explicitly
pruned, not current verifiable evidence. Existing reset/prune scripts have not
acquired these protections; reconcile and test them before enabling this mode.
The first mediated adapter is admitted only after an implementation charter and
proof of: complete mediation; narrow mounts/egress/credential separation; exact
session/config binding; crash-safe writer/idempotency; cohort stopping; revocation
and stale-controller fencing; context/output privacy; protected retention; closure of legacy broad-mount/unmediated entrypoints into
the protected namespace; and #50 account/materialization compatibility. Unsupported capability fails closed.
Pinned Pi documentation is not a passing admission test.
| Capability | Evidence available now | Still required before managed execution |
|---|---|---|
| Native/print launch | Current source inspected; earlier owner launch/resume example | New mediated launch implementation and negative tests |
| Exact selection and RPC events | Pinned 0.84.4 documentation read | Verify exact bootstrap/resume identity and cancelled/error cases |
| Pre-action mediation | Architecture and typed requests only | Reviewed bridge/gateway and bypass/failure tests |
| Sandbox | Pinned Pi documentation says no builtin sandbox; current Compose broad mount inspected | Actual filesystem/network/credential boundary tests |
| Stop/replace/control | Typed records and synthetic traces | Real process-cohort, crash, concurrent-client and revocation tests |
| Audit/publication/recovery | Existing writeOnce source plus candidate rules/models | Durable failure-injection and trusted-producer tests |
| Context/config/privacy | Source risks identified; hashes and candidate classifications | Admission/loading/output tests on actual adapter |
| OAuth refresh | #50 boundary read; gate 7 still open | Separately authorized provider/materializer proof |
These are explicit implementation acceptance gates, not missing permissions that
an agent may improvise around. No runtime feature claim follows from this draft.
+156
View File
@@ -0,0 +1,156 @@
"""Author-only checks for planning fixtures. Not a Mosaic runtime validator.
Run: python3 docs/plans/foundation-v1-candidate/check.py
Requires the existing jsonschema package; never installs dependencies.
"""
import json
import hashlib
import runpy
from copy import deepcopy
import unicodedata
from datetime import datetime
from pathlib import Path
from jsonschema import Draft202012Validator, FormatChecker
ROOT = Path(__file__).resolve().parent
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate JSON key: {key}")
result[key] = value
return result
def reject_constant(value):
raise ValueError(f"non-JSON numeric constant: {value}")
def load(name):
return json.loads((ROOT / name).read_text(), object_pairs_hook=unique_object,
parse_constant=reject_constant)
formats = FormatChecker()
@formats.checks("date-time")
def utc_milliseconds(value):
if not isinstance(value, str):
return True # Schema type validation handles non-strings.
try:
parsed = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%fZ")
except ValueError:
return False
return parsed.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" == value
@formats.checks("mosaic-relative-path")
def relative_path(value):
if not isinstance(value, str):
return True
try:
size = len(value.encode("utf-8"))
except UnicodeEncodeError:
return False
return (
0 < size <= 4096
and not any(unicodedata.category(c) in {"Cc", "Cf", "Cs"} for c in value)
and "\\" not in value
and all(part not in {"", ".", ".."} for part in value.split("/"))
)
assert not formats.conforms("é" * 2049, "mosaic-relative-path")
assert not formats.conforms("a\u202eb", "mosaic-relative-path")
assert not formats.conforms("2026-02-30T03:00:00.000Z", "date-time")
assert formats.conforms("2028-02-29T03:00:00.000Z", "date-time")
schema = load("command-events.schema.json")
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema, format_checker=formats)
fixtures = load("command-events.fixtures.json")
assert fixtures["fixtureVersion"] == 1
names = set()
failures = []
for case in fixtures["cases"] + fixtures["semanticObligations"]:
assert case["name"] not in names, case["name"]
names.add(case["name"])
actual = validator.is_valid(case["document"])
if actual != case["schemaValid"]:
failures.append(case["name"])
if failures:
raise SystemExit("FAIL: " + ", ".join(failures))
print(f"PASS: {len(fixtures['cases'])} shape fixtures")
print(f"PASS: {len(fixtures['semanticObligations'])} deliberate shape-valid forgeries")
records = load("records.schema.json")
Draft202012Validator.check_schema(records)
for name in ("id", "runtimeId", "revision", "digest", "time", "actor", "artifactRef"):
assert records["$defs"][name] == schema["$defs"][name], name
record_validator = Draft202012Validator(records, format_checker=formats)
record_fixtures = load("records.fixtures.json")
assert record_fixtures["fixtureVersion"] == 1
record_failures = []
for case in record_fixtures["cases"]:
if record_validator.is_valid(case["document"]) != case["schemaValid"]:
record_failures.append(case["name"])
for case in record_fixtures["typeCases"]:
subtype = {"$ref": "#/$defs/" + case["definition"], "$defs": records["$defs"]}
check = Draft202012Validator(subtype, format_checker=formats)
if check.is_valid(case["value"]) != case["schemaValid"]:
record_failures.append(case["name"])
if record_failures:
raise SystemExit("FAIL records: " + ", ".join(record_failures))
print(f"PASS: {len(record_fixtures['cases'])} record shape fixtures")
print(f"PASS: {len(record_fixtures['typeCases'])} path fixtures")
vectors = load("fingerprint-vectors.json")
assert vectors["vectorVersion"] == 1
baseline_digest = vectors["vectors"][0]["digest"]
for vector in vectors["vectors"]:
data = deepcopy(vector["input"])
assert set(vectors["excludedInputs"]).isdisjoint(data)
assert len({skill["id"] for skill in data["skills"]}) == len(data["skills"])
data["skills"].sort(key=lambda item: item["id"])
canonical = json.dumps(data, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
assert canonical.isascii(), "These fixtures do not establish general Unicode JCS behavior"
digest = "sha256:" + hashlib.sha256(canonical.encode("utf-8")).hexdigest()
assert canonical == vector["canonical"] and digest == vector["digest"], vector["name"]
assert (digest == baseline_digest) == (vector["relationToBaseline"] == "same")
print(f"PASS: {len(vectors['vectors'])} restricted-domain fingerprint vectors")
runtime = load("runtime.schema.json")
Draft202012Validator.check_schema(runtime)
for name in ("id", "runtimeId", "revision", "digest", "time", "actor", "artifactRef"):
assert runtime["$defs"][name] == schema["$defs"][name], name
assert runtime["$defs"]["operation"] == records["$defs"]["operation"]
assert set(runtime["$defs"]["controlRequest"]["properties"]["operation"]["enum"]) == (
set(records["$defs"]["operation"]["enum"]) - {"command.execute"}
)
projection_validator = Draft202012Validator(
{"$ref": "#/$defs/baseProjection", "$defs": runtime["$defs"]},
format_checker=formats)
for vector in vectors["vectors"]:
assert projection_validator.is_valid(vector["input"]), vector["name"]
runtime_validator = Draft202012Validator(runtime, format_checker=formats)
runtime_fixtures = load("runtime.fixtures.json")
assert runtime_fixtures["fixtureVersion"] == 1
runtime_failures = []
for case in runtime_fixtures["cases"] + runtime_fixtures["semanticObligations"]:
if runtime_validator.is_valid(case["document"]) != case["schemaValid"]:
runtime_failures.append(case["name"])
if runtime_failures:
raise SystemExit("FAIL runtime: " + ", ".join(runtime_failures))
print(f"PASS: {len(runtime_fixtures['cases'])} runtime/artifact shape fixtures")
print(f"PASS: {len(runtime_fixtures['semanticObligations'])} runtime semantic obligations remain shape-valid")
model_fixtures = load("semantic-model.fixtures.json")
assert model_fixtures["fixtureVersion"] == 1
model = runpy.run_path(str(ROOT / "semantic-model.py"))["evaluate"]
for case in model_fixtures["cases"]:
assert model(case) == case["expected"], case["name"]
for bundle in (fixtures, record_fixtures, runtime_fixtures, model_fixtures):
case_names = [case["name"] for case in bundle["cases"]
+ bundle.get("semanticObligations", [])]
assert len(case_names) == len(set(case_names)), "Duplicate fixture names"
print(f"PASS: {len(model_fixtures['cases'])} synthetic reference/permission/lifecycle model cases")
print("Runtime semantic enforcement is NOT implemented or tested by these checks.")
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,195 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:mosaic:planning:issue-53:command-events:candidate-1",
"title": "Planning candidate only: managed command request and evidence",
"description": "Not a runtime contract. Schema validity does not prove authentication, authorization, containment, durable recording, or reference integrity.",
"oneOf": [
{ "$ref": "#/$defs/request" },
{ "$ref": "#/$defs/event" }
],
"$defs": {
"id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$" },
"runtimeId": { "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
"revision": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 },
"digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
"time": { "type": "string", "format": "date-time", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$" },
"scope": {
"type": "object", "additionalProperties": false,
"required": ["kind", "projectId", "workspaceId"],
"properties": {
"kind": { "const": "workspace" },
"projectId": { "$ref": "#/$defs/id" },
"workspaceId": { "$ref": "#/$defs/id" }
}
},
"workRef": {
"type": "object", "additionalProperties": false,
"required": ["kind", "id", "scope", "revision"],
"properties": {
"kind": { "enum": ["task", "assignment"] },
"id": { "$ref": "#/$defs/id" },
"scope": { "$ref": "#/$defs/scope" },
"revision": { "$ref": "#/$defs/revision" }
}
},
"taskRef": { "allOf": [{ "$ref": "#/$defs/workRef" }, { "properties": { "kind": { "const": "task" } } }] },
"assignmentRef": { "allOf": [{ "$ref": "#/$defs/workRef" }, { "properties": { "kind": { "const": "assignment" } } }] },
"artifactRef": {
"type": "object", "additionalProperties": false,
"required": ["runId", "artifactId", "digest"],
"properties": {
"runId": { "$ref": "#/$defs/id" },
"artifactId": { "$ref": "#/$defs/id" },
"digest": { "$ref": "#/$defs/digest" }
}
},
"actor": {
"type": "object", "additionalProperties": false,
"required": ["kind", "principalId", "executionId"],
"properties": {
"kind": { "enum": ["human", "service", "agent"] },
"principalId": { "$ref": "#/$defs/id" },
"executionId": { "oneOf": [{ "$ref": "#/$defs/runtimeId" }, { "type": "null" }] }
},
"if": { "properties": { "kind": { "const": "agent" } } },
"then": { "properties": { "executionId": { "$ref": "#/$defs/runtimeId" } } },
"else": { "properties": { "executionId": { "type": "null" } } }
},
"binding": {
"type": "object", "additionalProperties": false,
"required": ["agentId", "scope", "sessionId", "executionId", "claimGeneration", "assignmentRef", "taskRef"],
"properties": {
"agentId": { "$ref": "#/$defs/id" },
"scope": { "$ref": "#/$defs/scope" },
"sessionId": { "$ref": "#/$defs/runtimeId" },
"executionId": { "$ref": "#/$defs/runtimeId" },
"claimGeneration": { "$ref": "#/$defs/revision" },
"assignmentRef": { "$ref": "#/$defs/assignmentRef" },
"taskRef": { "$ref": "#/$defs/taskRef" }
}
},
"request": {
"type": "object", "additionalProperties": false,
"required": ["requestVersion", "requestId", "operation", "binding", "commandSpecRef", "timeoutSeconds"],
"properties": {
"requestVersion": { "const": 1 },
"requestId": { "$ref": "#/$defs/runtimeId" },
"operation": { "const": "command.execute" },
"binding": { "$ref": "#/$defs/binding" },
"commandSpecRef": { "$ref": "#/$defs/artifactRef" },
"timeoutSeconds": { "type": "integer", "minimum": 1, "maximum": 600 }
}
},
"producer": {
"type": "object", "additionalProperties": false,
"required": ["componentId", "instanceId"],
"properties": {
"componentId": { "$ref": "#/$defs/id" },
"instanceId": { "$ref": "#/$defs/runtimeId" }
}
},
"intent": {
"type": "object", "additionalProperties": false,
"required": ["requestId", "requestDigest", "commandSpecRef", "limitsRef", "coverage"],
"properties": {
"requestId": { "$ref": "#/$defs/runtimeId" },
"requestDigest": { "$ref": "#/$defs/digest" },
"commandSpecRef": { "$ref": "#/$defs/artifactRef" },
"limitsRef": { "$ref": "#/$defs/artifactRef" },
"coverage": { "const": "command-invocation-v1" }
}
},
"started": {
"type": "object", "additionalProperties": false,
"required": ["intentEventId", "processRef"],
"properties": {
"intentEventId": { "$ref": "#/$defs/runtimeId" },
"processRef": { "$ref": "#/$defs/artifactRef" }
}
},
"finished": {
"type": "object", "additionalProperties": false,
"required": ["intentEventId", "outcome", "exitCode", "signal", "termination", "terminationProofRef", "effects", "evidenceRefs"],
"properties": {
"intentEventId": { "$ref": "#/$defs/runtimeId" },
"outcome": { "enum": ["succeeded", "failed", "canceled", "unknown"] },
"exitCode": { "type": ["integer", "null"], "minimum": 0, "maximum": 255 },
"signal": { "oneOf": [{ "type": "null" }, { "type": "string", "pattern": "^SIG[A-Z0-9]{1,16}$" }] },
"termination": { "enum": ["confirmed", "unconfirmed", "not-started"] },
"terminationProofRef": { "oneOf": [{ "$ref": "#/$defs/artifactRef" }, { "type": "null" }] },
"effects": { "enum": ["not-issued", "unverified", "reconciled"] },
"evidenceRefs": { "type": "array", "maxItems": 1024, "uniqueItems": true, "items": { "$ref": "#/$defs/artifactRef" } }
},
"allOf": [
{
"if": { "properties": { "outcome": { "const": "succeeded" } } },
"then": { "properties": { "exitCode": { "const": 0 }, "signal": { "type": "null" }, "termination": { "const": "confirmed" }, "effects": { "enum": ["unverified", "reconciled"] } } }
},
{
"if": { "properties": { "termination": { "const": "confirmed" } } },
"then": { "properties": { "terminationProofRef": { "$ref": "#/$defs/artifactRef" } } },
"else": { "properties": { "terminationProofRef": { "type": "null" } } }
},
{
"if": { "properties": { "termination": { "const": "unconfirmed" } } },
"then": { "properties": { "outcome": { "const": "unknown" }, "effects": { "const": "unverified" } } }
},
{
"if": { "properties": { "termination": { "const": "not-started" } } },
"then": { "properties": { "outcome": { "enum": ["failed", "canceled"] }, "exitCode": { "type": "null" }, "signal": { "type": "null" }, "effects": { "const": "not-issued" } } },
"else": { "properties": { "effects": { "enum": ["unverified", "reconciled"] } } }
},
{
"if": { "properties": { "signal": { "type": "string" } } },
"then": { "properties": { "exitCode": { "type": "null" } } }
},
{
"if": { "properties": { "outcome": { "const": "failed" }, "termination": { "const": "confirmed" } } },
"then": { "anyOf": [
{ "properties": { "exitCode": { "type": "integer", "minimum": 1 } } },
{ "properties": { "signal": { "type": "string" } } }
] }
},
{
"if": { "properties": { "effects": { "const": "reconciled" } } },
"then": { "properties": { "evidenceRefs": { "minItems": 1 } } }
}
]
},
"recovered": {
"type": "object", "additionalProperties": false,
"required": ["priorEventId", "checkEventIds", "decisionRef", "result"],
"properties": {
"priorEventId": { "$ref": "#/$defs/runtimeId" },
"checkEventIds": { "type": "array", "minItems": 1, "maxItems": 1024, "uniqueItems": true, "items": { "$ref": "#/$defs/runtimeId" } },
"decisionRef": { "$ref": "#/$defs/artifactRef" },
"result": { "$ref": "#/$defs/finished" }
}
},
"event": {
"type": "object", "additionalProperties": false,
"required": ["eventVersion", "eventId", "eventType", "commandId", "sequence", "recordedAt", "observedAt", "actor", "producer", "binding", "authorizationRef", "causedByEventId", "payload"],
"properties": {
"eventVersion": { "const": 1 },
"eventId": { "$ref": "#/$defs/runtimeId" },
"eventType": { "enum": ["command.intent", "command.started", "command.finished", "command.recovered"] },
"commandId": { "$ref": "#/$defs/runtimeId" },
"sequence": { "$ref": "#/$defs/revision" },
"recordedAt": { "$ref": "#/$defs/time" },
"observedAt": { "$ref": "#/$defs/time" },
"actor": { "$ref": "#/$defs/actor" },
"producer": { "$ref": "#/$defs/producer" },
"binding": { "$ref": "#/$defs/binding" },
"authorizationRef": { "$ref": "#/$defs/runtimeId" },
"causedByEventId": { "$ref": "#/$defs/runtimeId" },
"payload": { "type": "object" }
},
"allOf": [
{ "if": { "properties": { "eventType": { "const": "command.intent" } } }, "then": { "properties": { "payload": { "$ref": "#/$defs/intent" } } } },
{ "if": { "properties": { "eventType": { "const": "command.started" } } }, "then": { "properties": { "payload": { "$ref": "#/$defs/started" } } } },
{ "if": { "properties": { "eventType": { "const": "command.finished" } } }, "then": { "properties": { "payload": { "$ref": "#/$defs/finished" } } } },
{ "if": { "properties": { "eventType": { "const": "command.recovered" } } }, "then": { "properties": { "payload": { "$ref": "#/$defs/recovered" } } } }
]
}
}
}
@@ -0,0 +1,295 @@
{
"vectorVersion": 1,
"scope": "ASCII keys and values plus safe integers only; no general RFC 8785 implementation claim",
"vectors": [
{
"name": "baseline",
"input": {
"hashScopeVersion": 1,
"agentId": "agent-fixture",
"soulDigest": "sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2",
"instructions": [
{
"id": "instruction-a",
"digest": "sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce"
},
{
"id": "instruction-b",
"digest": "sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00"
}
],
"skills": [
{
"id": "skill-a",
"version": "1.0.0",
"digest": "sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8"
},
{
"id": "skill-b",
"version": "1.0.0",
"digest": "sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b"
}
],
"harness": {
"id": "pi",
"version": "0.84.4",
"digest": "sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c"
},
"modelSettingsDigest": "sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9",
"roleConfigDigest": "sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c"
},
"canonical": "{\"agentId\":\"agent-fixture\",\"harness\":{\"digest\":\"sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c\",\"id\":\"pi\",\"version\":\"0.84.4\"},\"hashScopeVersion\":1,\"instructions\":[{\"digest\":\"sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce\",\"id\":\"instruction-a\"},{\"digest\":\"sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00\",\"id\":\"instruction-b\"}],\"modelSettingsDigest\":\"sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9\",\"roleConfigDigest\":\"sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c\",\"skills\":[{\"digest\":\"sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8\",\"id\":\"skill-a\",\"version\":\"1.0.0\"},{\"digest\":\"sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b\",\"id\":\"skill-b\",\"version\":\"1.0.0\"}],\"soulDigest\":\"sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2\"}",
"digest": "sha256:adf4c227fb2cfbf04f4fcb9e0d616242ab69932a5865c42d2f8a01af149d140c",
"relationToBaseline": "same"
},
{
"name": "skill-set-reordering",
"input": {
"hashScopeVersion": 1,
"agentId": "agent-fixture",
"soulDigest": "sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2",
"instructions": [
{
"id": "instruction-a",
"digest": "sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce"
},
{
"id": "instruction-b",
"digest": "sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00"
}
],
"skills": [
{
"id": "skill-b",
"version": "1.0.0",
"digest": "sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b"
},
{
"id": "skill-a",
"version": "1.0.0",
"digest": "sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8"
}
],
"harness": {
"id": "pi",
"version": "0.84.4",
"digest": "sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c"
},
"modelSettingsDigest": "sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9",
"roleConfigDigest": "sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c"
},
"canonical": "{\"agentId\":\"agent-fixture\",\"harness\":{\"digest\":\"sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c\",\"id\":\"pi\",\"version\":\"0.84.4\"},\"hashScopeVersion\":1,\"instructions\":[{\"digest\":\"sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce\",\"id\":\"instruction-a\"},{\"digest\":\"sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00\",\"id\":\"instruction-b\"}],\"modelSettingsDigest\":\"sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9\",\"roleConfigDigest\":\"sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c\",\"skills\":[{\"digest\":\"sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8\",\"id\":\"skill-a\",\"version\":\"1.0.0\"},{\"digest\":\"sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b\",\"id\":\"skill-b\",\"version\":\"1.0.0\"}],\"soulDigest\":\"sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2\"}",
"digest": "sha256:adf4c227fb2cfbf04f4fcb9e0d616242ab69932a5865c42d2f8a01af149d140c",
"relationToBaseline": "same"
},
{
"name": "instruction-order-change",
"input": {
"hashScopeVersion": 1,
"agentId": "agent-fixture",
"soulDigest": "sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2",
"instructions": [
{
"id": "instruction-b",
"digest": "sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00"
},
{
"id": "instruction-a",
"digest": "sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce"
}
],
"skills": [
{
"id": "skill-a",
"version": "1.0.0",
"digest": "sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8"
},
{
"id": "skill-b",
"version": "1.0.0",
"digest": "sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b"
}
],
"harness": {
"id": "pi",
"version": "0.84.4",
"digest": "sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c"
},
"modelSettingsDigest": "sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9",
"roleConfigDigest": "sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c"
},
"canonical": "{\"agentId\":\"agent-fixture\",\"harness\":{\"digest\":\"sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c\",\"id\":\"pi\",\"version\":\"0.84.4\"},\"hashScopeVersion\":1,\"instructions\":[{\"digest\":\"sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00\",\"id\":\"instruction-b\"},{\"digest\":\"sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce\",\"id\":\"instruction-a\"}],\"modelSettingsDigest\":\"sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9\",\"roleConfigDigest\":\"sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c\",\"skills\":[{\"digest\":\"sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8\",\"id\":\"skill-a\",\"version\":\"1.0.0\"},{\"digest\":\"sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b\",\"id\":\"skill-b\",\"version\":\"1.0.0\"}],\"soulDigest\":\"sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2\"}",
"digest": "sha256:7512d1c8dec7a2894ce218ff5ccb71a09df7ffa0a15d3ea67d11378bf611b7e7",
"relationToBaseline": "different"
},
{
"name": "soul-content-change",
"input": {
"hashScopeVersion": 1,
"agentId": "agent-fixture",
"soulDigest": "sha256:1e988c391be3a384709c5f72d59f55a30dc6b8990de89ce40bb29bff1dd0a61e",
"instructions": [
{
"id": "instruction-a",
"digest": "sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce"
},
{
"id": "instruction-b",
"digest": "sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00"
}
],
"skills": [
{
"id": "skill-a",
"version": "1.0.0",
"digest": "sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8"
},
{
"id": "skill-b",
"version": "1.0.0",
"digest": "sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b"
}
],
"harness": {
"id": "pi",
"version": "0.84.4",
"digest": "sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c"
},
"modelSettingsDigest": "sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9",
"roleConfigDigest": "sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c"
},
"canonical": "{\"agentId\":\"agent-fixture\",\"harness\":{\"digest\":\"sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c\",\"id\":\"pi\",\"version\":\"0.84.4\"},\"hashScopeVersion\":1,\"instructions\":[{\"digest\":\"sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce\",\"id\":\"instruction-a\"},{\"digest\":\"sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00\",\"id\":\"instruction-b\"}],\"modelSettingsDigest\":\"sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9\",\"roleConfigDigest\":\"sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c\",\"skills\":[{\"digest\":\"sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8\",\"id\":\"skill-a\",\"version\":\"1.0.0\"},{\"digest\":\"sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b\",\"id\":\"skill-b\",\"version\":\"1.0.0\"}],\"soulDigest\":\"sha256:1e988c391be3a384709c5f72d59f55a30dc6b8990de89ce40bb29bff1dd0a61e\"}",
"digest": "sha256:743afecfe90e5fc88e51efa712fed9bd8b21a63495f24862d8ae41b14c849ac4",
"relationToBaseline": "different"
},
{
"name": "skill-version-change",
"input": {
"hashScopeVersion": 1,
"agentId": "agent-fixture",
"soulDigest": "sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2",
"instructions": [
{
"id": "instruction-a",
"digest": "sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce"
},
{
"id": "instruction-b",
"digest": "sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00"
}
],
"skills": [
{
"id": "skill-a",
"version": "1.0.1",
"digest": "sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8"
},
{
"id": "skill-b",
"version": "1.0.0",
"digest": "sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b"
}
],
"harness": {
"id": "pi",
"version": "0.84.4",
"digest": "sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c"
},
"modelSettingsDigest": "sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9",
"roleConfigDigest": "sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c"
},
"canonical": "{\"agentId\":\"agent-fixture\",\"harness\":{\"digest\":\"sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c\",\"id\":\"pi\",\"version\":\"0.84.4\"},\"hashScopeVersion\":1,\"instructions\":[{\"digest\":\"sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce\",\"id\":\"instruction-a\"},{\"digest\":\"sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00\",\"id\":\"instruction-b\"}],\"modelSettingsDigest\":\"sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9\",\"roleConfigDigest\":\"sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c\",\"skills\":[{\"digest\":\"sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8\",\"id\":\"skill-a\",\"version\":\"1.0.1\"},{\"digest\":\"sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b\",\"id\":\"skill-b\",\"version\":\"1.0.0\"}],\"soulDigest\":\"sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2\"}",
"digest": "sha256:e0f04f584a661c847aca886ff4283d6d30260ebe0a63a6e7ecb92f29ac91aa4f",
"relationToBaseline": "different"
},
{
"name": "model-settings-change",
"input": {
"hashScopeVersion": 1,
"agentId": "agent-fixture",
"soulDigest": "sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2",
"instructions": [
{
"id": "instruction-a",
"digest": "sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce"
},
{
"id": "instruction-b",
"digest": "sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00"
}
],
"skills": [
{
"id": "skill-a",
"version": "1.0.0",
"digest": "sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8"
},
{
"id": "skill-b",
"version": "1.0.0",
"digest": "sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b"
}
],
"harness": {
"id": "pi",
"version": "0.84.4",
"digest": "sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c"
},
"modelSettingsDigest": "sha256:72cd5dffcf6b4b2cb1986845713df70a95132b430c9ad47d8b9e190d5eddf850",
"roleConfigDigest": "sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c"
},
"canonical": "{\"agentId\":\"agent-fixture\",\"harness\":{\"digest\":\"sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c\",\"id\":\"pi\",\"version\":\"0.84.4\"},\"hashScopeVersion\":1,\"instructions\":[{\"digest\":\"sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce\",\"id\":\"instruction-a\"},{\"digest\":\"sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00\",\"id\":\"instruction-b\"}],\"modelSettingsDigest\":\"sha256:72cd5dffcf6b4b2cb1986845713df70a95132b430c9ad47d8b9e190d5eddf850\",\"roleConfigDigest\":\"sha256:c73b3b8eae276f63c1bdc0b43cfa46c011f1dee52a4dcad487e21f21730a009c\",\"skills\":[{\"digest\":\"sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8\",\"id\":\"skill-a\",\"version\":\"1.0.0\"},{\"digest\":\"sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b\",\"id\":\"skill-b\",\"version\":\"1.0.0\"}],\"soulDigest\":\"sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2\"}",
"digest": "sha256:a31265b96fa99f716115cde97292534316f67e4751c1acd83edfc9258e7234de",
"relationToBaseline": "different"
},
{
"name": "role-configuration-change",
"input": {
"hashScopeVersion": 1,
"agentId": "agent-fixture",
"soulDigest": "sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2",
"instructions": [
{
"id": "instruction-a",
"digest": "sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce"
},
{
"id": "instruction-b",
"digest": "sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00"
}
],
"skills": [
{
"id": "skill-a",
"version": "1.0.0",
"digest": "sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8"
},
{
"id": "skill-b",
"version": "1.0.0",
"digest": "sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b"
}
],
"harness": {
"id": "pi",
"version": "0.84.4",
"digest": "sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c"
},
"modelSettingsDigest": "sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9",
"roleConfigDigest": "sha256:8fd7961482cfd42e64fb7e3a5b878b1acf03d36d9f5266c06faa700d17647e4d"
},
"canonical": "{\"agentId\":\"agent-fixture\",\"harness\":{\"digest\":\"sha256:3a035a4176b40589ade570419395d80c0e38c18579db48f6a32e5661bee1fe1c\",\"id\":\"pi\",\"version\":\"0.84.4\"},\"hashScopeVersion\":1,\"instructions\":[{\"digest\":\"sha256:1b10996322128f9c87fdfdeaa052b4a915cfb47265c0f499e9ef8dd0ab0f02ce\",\"id\":\"instruction-a\"},{\"digest\":\"sha256:f41cff3c94eef22b1fa1ebf50ecf5a0a06ec275e5c7929da95ece66cac13eb00\",\"id\":\"instruction-b\"}],\"modelSettingsDigest\":\"sha256:10a0e705a201f894d7ea5f6176d53b6b64e206aa928a38d0d2c218aac89952f9\",\"roleConfigDigest\":\"sha256:8fd7961482cfd42e64fb7e3a5b878b1acf03d36d9f5266c06faa700d17647e4d\",\"skills\":[{\"digest\":\"sha256:c99aba8fe6d6d7f0e76a1bbd21e31fa8d2f624cb08918526fef3e7643c4e4ef8\",\"id\":\"skill-a\",\"version\":\"1.0.0\"},{\"digest\":\"sha256:f1f1581311e9f7cb73eb43913fed3fd894c1180af8d817d143b40f635c0ff61b\",\"id\":\"skill-b\",\"version\":\"1.0.0\"}],\"soulDigest\":\"sha256:e3c07000f9fb5e89ca8a489d9e2701b99f858dd735aabbb9cedb0455348b2cc2\"}",
"digest": "sha256:00284f7073e857b7fd43d81ea3958d269ed07dbcf68a7b8b34661745836fba2e",
"relationToBaseline": "different"
}
],
"excludedInputs": [
"taskProgress",
"conversation",
"credentials",
"privateUserContext",
"displayName",
"recordedAt",
"sourceRecordRevision"
]
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,854 @@
{
"fixtureVersion": 1,
"warning": "Synthetic model assumptions only; no runtime truth or enforcement established.",
"cases": [
{
"name": "exact-reference",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"pruned": false
},
"expected": true
},
{
"name": "wrong-reference-kind",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": {
"kind": "mission",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"pruned": false
},
"expected": false
},
{
"name": "wrong-reference-id",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": {
"kind": "task",
"id": "task-b",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"pruned": false
},
"expected": false
},
{
"name": "wrong-reference-scope",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": {
"kind": "task",
"id": "task-a",
"scope": "workspace-b",
"revision": 1,
"digest": "synthetic-digest-a"
},
"pruned": false
},
"expected": false
},
{
"name": "wrong-reference-revision",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 2,
"digest": "synthetic-digest-a"
},
"pruned": false
},
"expected": false
},
{
"name": "wrong-reference-digest",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-b"
},
"pruned": false
},
"expected": false
},
{
"name": "missing-reference",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": null,
"pruned": false
},
"expected": false
},
{
"name": "pruned-is-not-current-evidence",
"kind": "reference",
"input": {
"reference": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"target": {
"kind": "task",
"id": "task-a",
"scope": "workspace-a",
"revision": 1,
"digest": "synthetic-digest-a"
},
"pruned": true
},
"expected": false
},
{
"name": "all-layers-allow",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": true
},
{
"name": "deny-authenticated",
"kind": "permission",
"input": {
"authenticated": false,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "deny-intentCurrent",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": false,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "deny-assigned",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": false,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "missing-required-agent",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": null,
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "missing-required-project",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": null,
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "missing-required-workspace",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": null,
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "missing-required-mission",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": null,
"task": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "missing-required-task",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": null,
"execution": [
"file.read",
"file.change"
]
}
},
"expected": false
},
{
"name": "missing-required-execution",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read",
"file.change"
],
"execution": null
}
},
"expected": false
},
{
"name": "no-cross-assignment-union",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": true,
"operation": "file.change",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"mission": [
"file.read",
"file.change"
],
"task": [
"file.read"
],
"execution": [
"file.read",
"file.change"
]
},
"otherAssignment": [
"file.change"
]
},
"expected": false
},
{
"name": "taskless-scoped-read",
"kind": "permission",
"input": {
"authenticated": true,
"intentCurrent": true,
"assigned": false,
"operation": "file.read",
"layers": {
"agent": [
"file.read",
"file.change"
],
"project": [
"file.read",
"file.change"
],
"workspace": [
"file.read",
"file.change"
],
"execution": [
"file.read",
"file.change"
]
}
},
"expected": true
},
{
"name": "descendant",
"kind": "path",
"input": {
"path": "src/a.js",
"grants": [
"src"
]
},
"expected": true
},
{
"name": "prefix-is-not-ancestor",
"kind": "path",
"input": {
"path": "src-other/a.js",
"grants": [
"src"
]
},
"expected": false
},
{
"name": "root-explicit",
"kind": "path",
"input": {
"path": "docs/a.md",
"grants": [
null
]
},
"expected": true
},
{
"name": "empty-grant",
"kind": "path",
"input": {
"path": "src/a.js",
"grants": []
},
"expected": false
},
{
"name": "parent-traversal",
"kind": "path",
"input": {
"path": "src/../secret",
"grants": [
"src"
]
},
"expected": false
},
{
"name": "independent-acceptance",
"kind": "acceptance",
"input": {
"reviewerAuthorized": true,
"reviewer": "reviewer-a",
"authors": [
"author-a"
],
"requiredCriteria": [
"criterion-a"
],
"verifiedCriteria": [
"criterion-a"
]
},
"expected": true
},
{
"name": "refuse-acceptance-reviewer",
"kind": "acceptance",
"input": {
"reviewerAuthorized": true,
"reviewer": "author-a",
"authors": [
"author-a"
],
"requiredCriteria": [
"criterion-a"
],
"verifiedCriteria": [
"criterion-a"
]
},
"expected": false
},
{
"name": "refuse-acceptance-reviewerAuthorized",
"kind": "acceptance",
"input": {
"reviewerAuthorized": false,
"reviewer": "reviewer-a",
"authors": [
"author-a"
],
"requiredCriteria": [
"criterion-a"
],
"verifiedCriteria": [
"criterion-a"
]
},
"expected": false
},
{
"name": "refuse-acceptance-verifiedCriteria",
"kind": "acceptance",
"input": {
"reviewerAuthorized": true,
"reviewer": "reviewer-a",
"authors": [
"author-a"
],
"requiredCriteria": [
"criterion-a"
],
"verifiedCriteria": []
},
"expected": false
},
{
"name": "duplicate-launch",
"kind": "claim",
"input": {
"events": [
{
"operation": "launch"
},
{
"operation": "launch"
}
]
},
"expected": [
true,
false
]
},
{
"name": "audit-failure-before-effect",
"kind": "claim",
"input": {
"events": [
{
"operation": "audit-failed"
},
{
"operation": "launch"
}
]
},
"expected": [
false
]
},
{
"name": "idle-is-not-reaped",
"kind": "claim",
"input": {
"events": [
{
"operation": "launch"
},
{
"operation": "engine-idle"
},
{
"operation": "launch"
}
]
},
"expected": [
true,
false
]
},
{
"name": "uncertain-effects-block-replacement",
"kind": "claim",
"input": {
"events": [
{
"operation": "launch"
},
{
"operation": "uncertain-effects"
},
{
"operation": "launch"
}
]
},
"expected": [
true,
false
]
},
{
"name": "recovery-needs-audit",
"kind": "claim",
"input": {
"events": [
{
"operation": "launch"
},
{
"operation": "audit-failed"
},
{
"operation": "verified-stopped-and-reconciled"
},
{
"operation": "launch"
}
]
},
"expected": [
true,
false
]
},
{
"name": "recovery-invalidates-old-input",
"kind": "claim",
"input": {
"events": [
{
"operation": "launch"
},
{
"operation": "uncertain-effects"
},
{
"operation": "verified-stopped-and-reconciled"
},
{
"operation": "launch"
},
{
"operation": "input",
"generation": 1
},
{
"operation": "input",
"generation": 2
}
]
},
"expected": [
true,
true,
false,
true
]
}
]
}
@@ -0,0 +1,66 @@
"""Author-only executable examples of proposed rules, using synthetic facts.
Not an authenticator, reference resolver, sandbox, process observer, or runtime
validator. Input facts are fixture assumptions, never evidence of real state.
"""
def evaluate(case):
kind, data = case['kind'], case['input']
if kind == 'reference':
target = data['target']
return bool(target and not data['pruned'] and all(
target[key] == data['reference'][key]
for key in ('kind', 'id', 'scope', 'revision', 'digest')
))
if kind == 'permission':
if not data['authenticated'] or not data['intentCurrent']:
return False
required = ['agent', 'project', 'workspace', 'execution']
if data['assigned']:
required += ['mission', 'task']
elif data['operation'] not in ['work.read', 'file.read']:
return False
layers = data['layers']
return all(name in layers and layers[name] is not None
and data['operation'] in layers[name] for name in required)
if kind == 'path':
path = data['path'].split('/')
if any(part in ('', '.', '..') for part in path):
return False
return any(grant is None or path[:len(grant.split('/'))] == grant.split('/')
for grant in data['grants'])
if kind == 'acceptance':
return (data['reviewerAuthorized'] and data['reviewer'] not in data['authors']
and bool(data['requiredCriteria'])
and set(data['requiredCriteria']) == set(data['verifiedCriteria']))
if kind == 'claim':
# Read events as externally supplied observations. This never observes a process.
active, uncertain, audit, generation = False, False, True, 1
answers = []
for event in data['events']:
op = event['operation']
if op == 'audit-failed':
audit = False
elif op == 'audit-restored':
audit = True
elif op == 'uncertain-effects':
uncertain = True
elif op == 'verified-stopped-and-reconciled':
if audit:
active, uncertain = False, False
generation += 1
elif op == 'engine-idle':
pass # Not proof that commands/processes have stopped.
elif op == 'launch':
allowed = audit and not active and not uncertain
answers.append(allowed)
if allowed:
active = True
elif op == 'input':
answers.append(audit and active and not uncertain
and event['generation'] == generation)
else:
raise ValueError(op)
return answers
raise ValueError(kind)