docs: record accepted phase-2 foundation contract
This commit is contained in:
@@ -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.
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user