Status: Draft implementation in progress
Target: 0.3.0-draft
Created: 2026-07-12
Updates: executable core landed in valp_cli/control_plane.py, plugins.py, and conformance.py; final normative incorporation remains pending stable review
VALP 0.2.0 defines task routing, runtime adapters, dispatch receipts,
evidence gates, review, approvals, and audit. It does not yet define one
installation-wide control authority, an authoritative persistent capability
registry, or strict machine contracts for messages, executable state,
claim-to-evidence binding, deterministic failures, independent review,
protocol migration, and provider plugins.
This RFC defines the target contracts for 0.3.0-draft. A user explicitly selects
an Installation Leader, while a deterministic protocol core constrains what
that leader can do. Capability truth is stored as separate evidence layers:
official_claim, local_presence, live_callable, and task_verified.
Commands and results cross a strict message boundary, state is derived from an
append-only event log, completion claims are bound to immutable evidence, and
plugins remain untrusted provider adapters outside the protocol core.
The repository now implements a bounded draft core for these contracts: control
root bootstrap, explicit leader selection, epoch fencing, message/event
projection, capability registry, plugin manifest checks, migration planning,
and isolated conformance fixtures. It does not change the stable 0.2.0
release claim or runtime support claims. The remaining semantics become
normative only after acceptance, incorporation into SPEC.md, live adapter
evidence, and the stable-version gates in this RFC.
The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, and MAY describe requirements for a future implementation that claims conformance with this RFC after it is accepted. They do not describe features already implemented by the current reference CLI.
The current normative protocol remains SPEC.md version 0.2.0-draft, and the
current released evaluation surface remains 0.2.0. In particular:
0.2.0 task folders remain valid inputs to current readers;VALP currently answers a task-level question: “What proof makes an agent’s claim of completion trustworthy?” A full installation also needs to answer:
Without these contracts, a runtime can have an impressive demo while leaving control-plane reliability implicit. The gap is the march from a visible task record to a restart-safe, provider-neutral, testable system of record.
This RFC defines proposed contracts for:
0.3.0 stable.This RFC does not:
valp audit;0.3.0 release from the draft core alone; live adapter,
task-reducer, plugin isolation, and public E2E evidence remain required.installationcontrol rootbootstrap controllerInstallation Leadertask coordinatorleader epochprotocol corecapability observationcapability registryclaimsubject digestpluginThe proposed control path is:
user
-> bootstrap controller
-> explicit Installation Leader selection
-> fresh installation-owned Leader session provisioning and health proof
-> Leader epoch activation
-> deterministic protocol core
-> strict message and event ledgers
-> persistent state and capability projections
-> isolated provider/runtime plugins
-> task adapters and agents
-> claims, evidence, independent review, and audit
The following invariants apply:
completed remains distinct from VALP Done.| Mode | Installation Leader | Required control-plane proof |
|---|---|---|
| Full Mode | Exactly one active, addressable leader | Strict messages, leader epoch, live capability checks, runtime submission proof, event/state persistence, claims, review, and audit |
| Remote Mode | Exactly one active leader visible to both local and remote sides | Full Mode proof plus authenticated or explicitly attested remote transport identity and remote evidence location |
| Manual Mode | A user-designated human may be leader | Persistent metadata, manual receipt labels, claims/evidence, review, and audit; no Full Mode submission or live-callability claims |
Auto Visible Mode remains an intake policy layered over one of these modes. It does not select an Installation Leader and does not weaken the selected mode’s proof requirements.
A new installation MUST move through:
uninitialized
-> bootstrapping
-> discovering_leader_candidates
-> awaiting_leader_selection
-> awaiting_leader_start
-> activating_leader
-> active | blocked
Leader epoch 0 is permanently reserved for bootstrap. An epoch 0 message is
valid only while installation state is bootstrapping,
discovering_leader_candidates, awaiting_leader_selection,
awaiting_leader_start, or activating_leader, and only for the fixed
bootstrap discovery, explicit selection, and exact Leader-start message kinds.
Epoch 0 MUST NOT authorize task dispatch, plugin activation, general
installation mutation, or any other task gate-bearing side effect.
User selection is a core-owned approval operation, not an epoch 0 leader
command. Selection persists intent and leaves the epoch at 0; it does not
adopt or activate the bootstrap session. When the fresh Leader session passes
binding and health validation, the core MUST append
bootstrap_completed, allocate active leader epoch 1, and permanently fence
epoch 0. After that event, replaying any epoch 0 message fails as
VALP-E-LEADER-EPOCH, even if the installation later becomes degraded,
blocked, migrated, rotated, or retired.
The bootstrap controller MAY inspect declared metadata, local presence, and a bounded live health probe. Before selection it MUST NOT:
task_verified capability;The user-facing selection record MUST show, for each candidate:
principal id and type
provider/runtime identity, when known
declared coordination capabilities
local presence observation
live health observation and timestamp
permission boundary
context policy
known limitations
Full Mode MUST remain blocked until the user explicitly selects one candidate.
Manual Mode MAY select a human principal. Absence of a suitable candidate is a
visible leader_candidate_unavailable failure, not permission to guess.
An accepted selection MUST persist:
installation_id
selection_id
leader_principal_id
leader_principal_type
proposed_leader_epoch
selected_by
selected_at
candidate_observation_refs
candidate_passport_digest
permission_scope
protocol_version
approval_ref
previous_selection_ref, when rotating
record_digest
The selection record proposes an epoch but does not activate it. The core MUST
assign the first active leader_epoch as 1 only after the exact
installation-owned session is bound and healthy. Each later activation MUST use
the prior active epoch plus 1; epoch values MUST NOT be reused. A leader MUST
include the active epoch in every gate-bearing command. A command from bootstrap
epoch 0, an older active epoch, or a future epoch MUST be rejected without
changing task state.
leader start MUST resolve the selected passport’s adapter and launch contract,
create a fresh non-focused installation-owned runtime attachment, and record
its exact runtime identity, generation, cwd or equivalent context, launch
digest, health proof, attachment digest, and receipts before activation. The
installation Leader authority is the selected principal plus installation id
and fenced epoch; it is not the pane, tab, terminal, cwd, or window used to
present it. It MUST NOT adopt the bootstrap session, a focused user session,
an existing Worker, or a session matched only by product name, label, cwd, or
visible text.
leader open is callable from any caller workspace. If the current attachment
exists, it focuses or attaches that runtime without changing the Leader epoch.
If it is gone, the adapter provisions a replacement and the core fences the old
attachment before recording the new generation. Calling ordinary leader start
while a Leader is active has the same open behavior. The same principal still
has an explicit leader restart operation; a different principal requires
explicit leader rotate <principal>. Both explicit operations preserve prior
bindings and receipts, fence the prior epoch, provision a fresh generation, and
activate the next epoch only after health proof passes.
If adapter provisioning or binding validation fails after preparation, the
core appends leader_session_start_failed with the operation, selected
principal, proposed epoch/generation, adapter, protocol error, and blocking
event, then transitions through leader_activation_failed to blocked. It
does not invent binding or session fields and does not advance the active epoch.
One narrow first-start recovery path is permitted. leader recover-start
requires explicit user approval and one exact runtime session id. It is legal
only while the installation is blocked at epoch 0, no Leader or binding
exists, the selection is unchanged, and the latest valid receipt is the
blocking failed start for epoch 1 and the same generation. The adapter must
address only that session, must not create or move runtime state, and must
re-prove deterministic installation scope, Agent identity, cwd, non-focus,
complete launch identity, process generation, and bounded health. Successful
recovery preserves the failure receipt and appends recovery approval plus
normal binding and activation evidence; any mismatch returns to blocked.
The Installation Leader MUST:
The Installation Leader MUST NOT:
task_verified observation as live proof without a
current probe;Task coordination MAY be delegated by an accepted message that names the task, delegate, scope, expiry, and expected evidence. Delegation MUST NOT authorize installation migration, plugin enablement, leader rotation, or approval on behalf of the user.
Cooperative leader rotation is a controlled migration. It MUST:
leader_epoch + 1;Acceptance of leader_rotation_approved MUST atomically enter
rotating_leader and freeze the current epoch for new gate-bearing work. Until
leader_rotation_completed or leader_rotation_failed, the core MUST reject
every current-epoch gate-bearing command except the explicitly defined
rotation, checkpoint, and recovery commands. Rejection fails as
VALP-E-LEADER-ROTATING and MUST NOT mutate task or installation state. This
freeze is core-enforced; it does not depend on the current leader remaining
reachable or cooperating.
If the old and new leaders both act, only the active epoch is accepted. The
other command fails as VALP-E-LEADER-EPOCH.
Emergency rotation MUST remain possible when the active leader cannot perform the cooperative sequence. Before task execution, the core MUST persist a bounded leader-health policy containing:
health_policy_id
leader principal id and epoch
probe kind
per-attempt timeout
maximum attempts
maximum observation window
success and failure predicates
required evidence kinds
policy digest
The core may declare a leader unreachable only when the recorded policy is
exhausted or when an independently observed process/session death satisfies a
recorded failure predicate. The health record MUST include each attempt’s core
sequence, start/deadline time, result, transport or process status, and redacted
evidence ref. A suspected compromise is not inferred from a timeout; it requires
a user-submitted leader_compromise_declared record with incident evidence and
the same explicit approval used for emergency rotation.
The deterministic emergency path is:
leader_health_failed or
leader_compromise_declared, transitions active -> degraded, and blocks
gate-bearing side effects from the affected epoch;emergency_leader_rotation_approved, checkpoints state,
allocates leader_epoch + 1, appends the new selection, and atomically
fences every earlier epoch;leader_rotation_completed returns the installation
to active.If replacement activation fails, the installation remains degraded with the
old epoch fenced and task side effects blocked. A user may approve another
candidate within the recorded recovery budget, move the installation to
blocked, or retire it. No recovery path may reactivate or reuse the stale
epoch. Every later message from a fenced epoch fails as
VALP-E-LEADER-EPOCH.
Every authoritative discovery run MUST inspect and record all of the following surfaces:
Each surface MUST produce observations or an explicit unknown, unavailable,
blocked, or not_applicable result with a reason. An implementation MUST NOT
omit an unsupported surface and then treat the scan as complete. Names and
bounded metadata may be recorded directly; secrets, private configuration, and
unrestricted command output remain excluded.
Discovery proves different layers for different surfaces. An installed Skill
or registered MCP server may prove local_presence; only a current bounded
handshake or equivalent safe probe may prove live_callable. Tool/resource
enumeration is valid only for the exact live session, server version, permission
scope, and observation expiry recorded. Official manifests remain
official_claim until stronger local or task evidence exists.
Capability truth MUST preserve four separate layers:
| Layer | Question answered | Minimum provenance | Routing strength |
|---|---|---|---|
official_claim |
What does a provider, agent, runtime, or manifest claim? | source identity, version, claim ref, observation time | weak declaration |
local_presence |
Is the component discoverable in this installation? | bounded local probe, path or package ref, version, environment fingerprint | presence only |
live_callable |
Did a safe current probe succeed? | probe kind, result, timestamp, expiry, output/evidence ref | strong but time-bounded |
task_verified |
Did the capability produce accepted evidence in a real task? | task id, exact capability/scope, evidence and review refs, completion time | strongest but contextual |
Layers MUST NOT be collapsed into one boolean. official_claim does not imply
installation. local_presence does not imply callability. live_callable
does not imply correct task output. task_verified does not eliminate the need
for a fresh live check.
Each observation MUST contain:
schema_version
observation_id
installation_id
registry_sequence
leader_epoch
subject_id
capability_id
layer
status
source_principal_id
source_kind
observed_at
expires_at, when time-bounded
environment_fingerprint
version_or_runtime_report
permission_scope
evidence_refs
previous_observation_ref, when superseding
content_digest
Allowed observation status values are:
present
absent
pass
fail
unknown
stale
blocked
not_applicable
The protocol core assigns registry_sequence. Wall-clock time is metadata and
MUST NOT be the sole ordering mechanism. Observations are append-only; a newer
observation supersedes but does not erase history.
The registry projection MUST include:
installation_id
registry_revision
last_observation_sequence
generated_at
active_leader_epoch
entries keyed by subject_id and capability_id
latest valid observation for each evidence layer
effective routing status and reason
staleness state
permission boundary
projection_digest
The projection is authoritative only as a deterministic fold of valid
observations. On restart, an implementation MUST compare the event/observation
chain and reproduce the same projection bytes and digest or enter blocked
with a registry-consistency failure. It MUST NOT silently rebuild different
truth from a fresh scan and discard the persisted history.
Discovery plugins may propose observations; only the core may accept and sequence them. Reconciliation MUST:
A failed or unavailable probe MUST NOT preserve an old live_callable: pass as
current truth. The old observation remains in history but becomes stale or is
superseded according to its expiry.
Task routing MUST require:
local_presence and unexpired live_callable proof for capabilities needed
to perform automated side effects;task_verified observation only after accepted task evidence and
review exist for that exact capability and scope.Discovery output MUST NOT contain tokens, secrets, raw private configuration, or unrestricted command output. Sensitive details remain in the provider or runtime and are referenced through redacted evidence.
An implementation MAY choose a database or another durable store internally,
but it MUST export the following logical artifacts in canonical JSON or JSONL.
The control-root path is implementation-defined and recorded in
installation.json.
| Logical artifact | Proposed schema | Purpose |
|---|---|---|
installation.json |
valp-installation.v1 |
Installation identity, control-root metadata, active protocol version, and active leader epoch |
leader-selections.jsonl |
valp-leader-selection.v1 |
Append-only user selection and rotation history |
capability-observations.jsonl |
valp-capability-observation.v1 |
Append-only layered discovery evidence |
capability-registry.json |
valp-capability-registry.v1 |
Deterministic registry projection |
messages.jsonl |
valp-message.v1 |
Accepted and rejected control-plane messages |
events.jsonl |
valp-event.v1 |
Immutable state-transition facts |
state.json |
valp-executable-state.v1 |
Rebuildable installation and active-task projection |
claims.jsonl |
valp-claim.v1 |
Structured assertions and their disposition |
evidence-manifest.json |
valp-evidence-manifest.v1 |
Content-addressed evidence metadata |
failures.jsonl |
valp-failure.v1 |
Deterministic protocol and adapter failures |
reviews.jsonl |
valp-review.v1 |
Independent review requests, findings, and verdicts |
protocol-manifest.json |
valp-protocol-manifest.v1 |
Supported protocol/schema ranges and extension policy |
migration-plan.json |
valp-migration-plan.v1 |
Dry-run migration plan and preconditions |
migration-receipt.json |
valp-migration-receipt.v1 |
Applied, rolled-back, or blocked migration result |
plugins/<plugin-id>/manifest.json |
valp-plugin-manifest.v1 |
Plugin identity, compatibility, schemas, permissions, and limits |
Existing task-local artifacts, including routing, dispatch receipts, expected
evidence, findings, approvals, recommendations, and final synthesis, remain
required. The proposed artifacts constrain how a 0.3 control plane creates
and evaluates those task records; they do not replace them.
All proposed schemas MUST:
schema_version;Every gate-bearing command, event request, query, and result MUST use a validated envelope. A proposed envelope is:
{
"schema_version": "valp-message.v1",
"protocol_version": "0.3.0-draft",
"message_id": "msg-01K...",
"installation_id": "inst-01K...",
"leader_epoch": 7,
"task_id": "TASK-123",
"sequence": 42,
"kind": "command.dispatch.submit",
"sender_principal_id": "principal-leader",
"recipient_principal_id": "principal-runtime",
"correlation_id": "corr-01K...",
"causation_id": "msg-01J...",
"idempotency_key": "dispatch:TASK-123:worker-a:1",
"created_at": "2026-07-12T10:00:00Z",
"expires_at": "2026-07-12T10:05:00Z",
"permission_scope": ["dispatch.submit"],
"payload_schema": "valp-dispatch-submit.v1",
"payload": {},
"refs": ["tasks/TASK-123/agents/worker-a/dispatch.md"],
"content_digest": "sha256:..."
}
content_digest MUST cover the canonical envelope with the digest field
omitted. The accepted draft canonicalization is UTF-8 JSON Canonicalization
Scheme (JCS) plus SHA-256. A later RFC may add algorithms through an explicit
protocol-manifest negotiation; an implementation MUST NOT silently substitute
another algorithm.
An unauthenticated content_digest establishes canonical content or byte
identity only. It does not establish authorship, origin, authorization, or
authenticated integrity because an attacker able to replace the content may
also recompute the digest. Those guarantees require the separately recorded
principal, permission, and authenticated transport or signature evidence.
The core MUST process a message in this order:
parse bytes
-> validate envelope schema and payload schema
-> verify protocol/schema compatibility
-> verify content digest
-> resolve sender and recipient principals
-> check installation id and active leader epoch
-> check permission and approval scope
-> check expiry and state revision preconditions
-> deduplicate idempotency key
-> append accepted or rejected message record
-> append event, if accepted command changes state
-> update projection
-> emit structured result
No side effect may occur before validation, authorization, epoch, revision, and
idempotency checks pass. If an adapter cannot enforce that boundary, it MUST
not claim 0.3 Full Mode conformance.
Accepted installation sequences MUST start at 1 and increase by exactly one
for each accepted message. The first accepted bootstrap discovery message
therefore receives installation sequence 1; rejected messages do not consume
an accepted sequence. Sender-provided sequence values MAY assist transport
ordering but MUST NOT override the core sequence. Replaying the same
idempotency key with the same digest returns the recorded result without
allocating a new accepted sequence. Reusing it with a different digest fails as
VALP-E-IDEMPOTENCY-CONFLICT.
Core message kinds use these namespaces:
command.*
query.*
result.*
event.*
Plugin-defined messages use plugin.<plugin-id>.*. A plugin namespace cannot
define or shadow core receipt, approval, evidence-validity, review, migration,
or Done semantics.
The envelope is transport-neutral. A runtime may carry it through a local queue, authenticated API, hosted run, SSH channel, pane controller, or manual copy workflow. The adapter MUST record the transport identity and proof grade.
Markdown and UI text are renderings, not the authoritative message. If a human edits a rendered dispatch before submission, the changed content MUST receive a new message id and digest. Manual Mode may use human attestations but MUST keep manual receipt labels and MUST NOT claim authenticated Full Mode transport.
State is not a prose status label. A conforming core MUST implement:
validated command + expected revision
-> accepted immutable event
-> deterministic projection update
-> structured result with new revision
Every state-changing command MUST include an expected state revision. The core
performs compare-and-swap semantics. A stale revision fails as
VALP-E-STATE-CONFLICT; it MUST NOT partially apply.
Each event MUST contain:
event_id
installation_sequence
installation_id
leader_epoch
task_id, when task-scoped
event_kind
accepted_message_id
prior_revision
new_revision
occurred_at
actor_principal_id
payload_schema and payload
prior_event_digest
event_digest
An implementation MUST atomically commit the event and its authoritative
projection, or recover by replaying committed events. A projection without a
matching event is invalid. An event chain or projection-digest mismatch places
the affected installation or task in blocked until repaired through an
audited recovery procedure.
Legal installation transitions are:
uninitialized -> bootstrapping
bootstrapping -> discovering_leader_candidates | blocked
discovering_leader_candidates -> awaiting_leader_selection | blocked
awaiting_leader_selection -> awaiting_leader_start | blocked
awaiting_leader_start -> activating_leader | blocked
activating_leader -> active | blocked
active -> restarting_leader | reconciling_capabilities | migrating | rotating_leader | degraded | blocked | retired
restarting_leader -> active | blocked
reconciling_capabilities -> active | degraded | blocked
rotating_leader -> active | blocked
migrating -> active | rollback_required | blocked
rollback_required -> active | blocked
degraded -> reconciling_capabilities | rotating_leader | migrating | blocked | retired
blocked -> activating_leader | active | retired
Every transition MUST be caused by one of the named events and guards below:
| Transition | Required trigger event | Minimum guard |
|---|---|---|
uninitialized -> bootstrapping |
installation_initialized |
installation id and protocol manifest accepted |
bootstrapping -> discovering_leader_candidates |
bootstrap_discovery_started |
epoch 0; read-only discovery policy active |
bootstrap state -> blocked |
bootstrap_failed |
deterministic bootstrap failure recorded |
discovering_leader_candidates -> awaiting_leader_selection |
leader_candidate_discovery_completed |
bounded discovery completed with explicit results for every required surface |
awaiting_leader_selection -> awaiting_leader_start |
leader_selection_approved |
explicit user approval names one observed candidate; active Leader and epoch remain unset |
awaiting_leader_start -> activating_leader |
leader_start_requested |
selected passport digest, adapter, and launch contract validate |
activating_leader -> active |
leader_activated |
epoch 1 or next epoch allocated; bounded health and reconciliation pass |
activating_leader -> blocked |
leader_activation_failed |
activation budget exhausted or safety guard failed |
blocked -> activating_leader |
leader_start_recovery_approved |
explicit approval names one exact partial session; epoch is 0; no binding exists; latest failed-start receipt, selection, epoch, generation, and blocking event match |
active -> reconciling_capabilities |
capability_reconciliation_started |
accepted leader command and current registry revision |
reconciling_capabilities -> active |
capability_reconciliation_completed |
required surfaces recorded and routing-critical probes pass |
reconciling_capabilities -> degraded |
capability_reconciliation_partially_failed |
non-critical surface failed and affected work is fenced |
reconciling_capabilities -> blocked |
capability_reconciliation_safety_failed |
a routing-critical capability, permission, or registry guard failed |
active -> rotating_leader |
leader_rotation_approved |
cooperative rotation has explicit user approval and checkpoint |
active -> restarting_leader |
leader_restart_requested |
explicit restart fences the current binding and names the next generation |
restarting_leader -> active |
leader_activated |
fresh same-principal session is bound and healthy; prior epoch is fenced |
restarting_leader -> blocked |
leader_activation_failed |
replacement provisioning or binding validation failed; prior epoch remains recorded |
active -> degraded |
leader_health_failed, leader_compromise_declared, or plugin_dependency_quarantined |
recorded bounded health/incident evidence or deterministic affected-task closure |
active -> blocked |
control_plane_consistency_failed |
event/state, registry, message, or other safety-critical projection cannot be reproduced |
degraded -> rotating_leader |
emergency_leader_rotation_approved |
explicit user approval names replacement and epoch to fence |
rotating_leader -> active |
leader_rotation_completed |
new epoch activated; every prior epoch fenced; health/reconciliation pass |
rotating_leader -> blocked |
leader_activation_failed |
replacement provisioning or binding validation failed; no new epoch is activated |
active or degraded -> migrating |
migration_apply_approved |
digest-matched dry-run plan and explicit approval |
migrating -> active |
migration_activated |
staged target validates and atomically activates |
migrating -> rollback_required |
migration_activation_failed |
source checkpoint remains recoverable |
migrating -> blocked |
migration_unrecoverable |
neither activation nor immediate rollback is safe |
rollback_required -> active |
migration_rollback_completed |
source checkpoint and projections validate |
rollback_required -> blocked |
migration_rollback_failed |
rollback validation fails |
degraded -> reconciling_capabilities |
degraded_reconciliation_approved |
affected epochs/plugins/tasks remain fenced |
degraded -> blocked |
degraded_recovery_exhausted |
recorded recovery budget is exhausted |
blocked -> active |
blocking_failure_resolved |
failure, replacement evidence, review, and required approval are resolved |
active, degraded, or blocked -> retired |
installation_retirement_approved |
explicit user approval and final checkpoint |
retired is a terminal control state whose only access mode is
retired-read-only. All message, event, leader, capability, state, receipt,
claim, evidence, failure, review, approval, plugin, and migration ledgers MUST
remain immutable, readable, exportable, and auditable. Readers may inspect or
export those bytes without appending to the retired installation. Retirement
accepts no new gate-bearing commands and is not deletion, purge, key
destruction, retention-policy change, or authorization for any of those
actions. A future destructive lifecycle would require a separate protocol
contract and explicit user approval; it cannot be inferred from retired.
The 0.3 task vocabulary starts from the existing 0.2 lifecycle, including
publish, scan, route, dispatch, execute, verify, review, fix, approval, record,
and terminal outcomes. A future schema patch MUST enumerate legal transitions
and their preconditions. At minimum:
done is reachable only through the Done reducer after every required gate
is resolved;completed cannot transition a task directly to done;verifying or moves it to
blocked when the retry budget is exhausted;fixing or
blocked according to the recorded iteration budget;approval_required;done -> blocked is permitted only through
plugin_dependency_quarantined or another future protocol-defined
evidence-invalidation event with a deterministic dependency closure;The executable state projection MUST include active blockers, gate results, latest receipt by selected principal, expected evidence status, review status, approval status, recommendation resolution, protocol/schema versions, and the last accepted event id. Human-readable summaries MUST be derived from this state, not maintained as competing truth.
Every gate-bearing assertion MUST have a claim_id and record:
claim_id
installation_id
task_id, when task-scoped
claimant_principal_id
claim_kind
subject_ref
subject_digest
predicate
asserted_value
scope
created_at
required_evidence_kinds
evidence_refs
status
verifier_principal_id, when reviewed
review_ref, when reviewed
supersedes_claim_id, when applicable
claim_digest
Allowed claim statuses are:
declared
supported
verified
rejected
superseded
blocked
supported means evidence is attached and schema-valid. verified means an
authorized verifier evaluated the evidence and exact subject digest against
recorded criteria. Only verified gate-bearing claims may satisfy 0.3 Full
Mode Done Criteria.
Each evidence item MUST record:
evidence_id
evidence_kind
content_ref
content_digest
media_type
byte_length
created_at
producer_principal_id
collection_method
redaction_state
validity_state
supporting_claim_ids
Task evidence refs remain task-relative safe paths under the task evidence root. Installation evidence refs remain control-root relative. Absolute paths, parent traversal, self-referential support, missing content, and digest mismatch MUST NOT satisfy a claim.
Changing an artifact changes its digest and invalidates prior claim and review coverage for that artifact. A new claim may supersede the old one, but history MUST remain visible.
Every protocol rejection or adapter failure that affects control flow MUST write:
failure_id
error_code
error_schema_version
installation_id
task_id, when task-scoped
phase
accepted_or_rejected_message_id
state_revision
leader_epoch
retriable
retry_class
safe_summary
diagnostic_ref
affected_refs
deterministic_state_effect
created_at
failure_digest
Provider diagnostics MAY be attached by reference, but the stable protocol error code and state effect MUST NOT depend on provider wording. Secrets and private prompts MUST be redacted from failure output.
| Code | Meaning | Required state effect | Retriable by default? |
|---|---|---|---|
VALP-E-MESSAGE-SCHEMA |
Envelope or payload is invalid | reject message; no task-state change | no |
VALP-E-MESSAGE-DIGEST |
Content digest does not match the received canonical bytes | reject message; no task-state change | no |
VALP-E-PROTOCOL-UNSUPPORTED |
No compatible protocol/schema range | reject before side effects | no |
VALP-E-LEADER-EPOCH |
Command uses a fenced epoch | reject; no task-state change | no |
VALP-E-LEADER-ROTATING |
Cooperative rotation has frozen the current epoch | reject gate-bearing work; preserve rotating_leader |
no retry until rotation resolves |
VALP-E-LEADER-UNREACHABLE |
Bounded active-leader health policy is exhausted | transition installation to degraded; block affected epoch side effects |
no retry without recovery action |
VALP-E-PERMISSION-DENIED |
Principal lacks declared scope | reject; preserve approval state | no |
VALP-E-APPROVAL-REQUIRED |
Explicit user approval is missing | move or keep task at approval_required |
no |
VALP-E-IDEMPOTENCY-CONFLICT |
Same key has different content | reject; return prior record ref | no |
VALP-E-STATE-CONFLICT |
Expected revision is stale | reject; return current revision | yes, after reread |
VALP-E-CAPABILITY-STALE |
Required live observation expired | block dispatch pending reconciliation | yes, after probe |
VALP-E-EVIDENCE-MISSING |
Required evidence is absent or invalid | keep verifying or block at budget | yes, within budget |
VALP-E-REVIEW-BLOCKED |
Review is missing, stale, or blocking | move to fixing or blocked | yes, within budget |
VALP-E-PLUGIN-BOUNDARY |
Plugin exceeded manifest permissions | quarantine plugin and block affected work | no automatic retry |
VALP-E-MIGRATION-UNSUPPORTED |
No safe migration path exists | remain on prior version or block partial migration | no |
VALP-E-REGISTRY-CONSISTENCY |
Registry bytes and projection digest cannot be reproduced | block installation routing | no automatic retry |
Given the same canonical message, active protocol manifest, leader epoch, state revision, registry revision, and approval state, the core MUST return the same protocol error code and state effect. Timestamps and unique failure ids may differ; the decision may not.
Retries MUST be bounded and recorded. Validation, permission, epoch, digest, plugin-boundary, and unsupported-migration failures are not transient. State-conflict retries MUST reread state and submit a new message with a new message id while retaining the logical correlation id. Runtime transport and evidence delays MAY retry within a task-local budget. The same blocker twice requires scope reduction; the same blocker three times stops and asks the user, unless a stricter project policy stops earlier.
A retry does not erase failure evidence and does not reuse a failed message id. Recovery MUST cite the prior failure and replacement evidence.
Zero-trust review means no claim is accepted because a model, provider, maintainer, or prior task was trusted. It does not by itself claim a particular cryptographic identity system.
A review request MUST include:
review_id
reviewer_principal_id
claim_ids
subject_refs and subject_digests
criteria_schema and criteria_version
required evidence refs
independence requirement
risk class
requested_at
A review result MUST include:
review_id
reviewer_principal_id
reviewer_capability_observation_refs
reviewed_claim_ids
reviewed_subject_digests
criteria_version
findings
verdict
confidence_limits
completed_at
review_digest
Allowed verdicts are:
pass
fail
abstain
blocked
Review rules:
abstain and blocked are visible non-pass outcomes.0.3 gate.Review does not replace verification. Verification establishes evidence; review evaluates whether that evidence supports the exact claim under the criteria.
Protocol discovery MUST occur before negotiated gate-bearing messages. Every
adapter MUST expose one fixed, read-only operation named valp.hello using the
non-negotiated valp-hello.v1 UTF-8 JSON shape. A local control-root adapter
implements the same operation by reading the fixed logical artifact
protocol-manifest.json; other transports map valp.hello to their documented
read-only discovery call.
The request contains only:
{
"hello_schema": "valp-hello.v1",
"kind": "hello.request",
"nonce": "caller-generated-nonce"
}
The response contains only:
{
"hello_schema": "valp-hello.v1",
"kind": "hello.response",
"nonce": "caller-generated-nonce",
"installation_id": "inst-01K...",
"implementation_id": "implementation-name-and-version",
"supported_protocol_read_versions": ["0.3.0-draft", "0.2.0"],
"supported_protocol_write_versions": ["0.3.0-draft"],
"manifest_ref": "protocol-manifest.json",
"manifest_digest": "sha256:..."
}
The request nonce MUST be a non-empty canonical ASCII base64url string. The
response MUST echo the exact same ASCII byte sequence, and the caller MUST
reject any response whose nonce differs byte-for-byte. The nonce provides
request/response correlation only. It MUST NOT be treated as replay protection,
freshness, authentication, authorization, or peer-identity evidence unless a
separate bounded replay contract explicitly defines those properties. A caller
MUST NOT reuse a nonce among concurrently outstanding hello requests.
valp-hello.v1 has no extension namespace, leader epoch, permission grant,
plugin payload, task command, or side effect. Request and response are each
limited to 16 KiB. An adapter MUST use one bounded transport attempt and record
its deadline and result. An unreadable schema, wrong nonce, oversized response,
missing required field, unreadable manifest, or version sets with no mutual
read/write intersection fails as VALP-E-PROTOCOL-UNSUPPORTED before any
gate-bearing message. A transport that cannot return a response by its recorded
deadline emits a transport failure instead of waiting indefinitely.
The read/write arrays contain unique, exact protocol version tokens in descending preference order. Negotiation selects the highest SemVer token, including prerelease precedence, that appears in both peers’ read and write sets. Implementations MUST NOT interpret an exact hello token as a range.
The hello and manifest digests establish content identity only unless the transport or a future signature contract separately authenticates them. After the fixed hello succeeds, peers read the manifest, select one mutually supported protocol and payload-schema set, and then use the strict message envelope from Section 9.
Every conforming installation MUST publish a protocol manifest containing:
active_protocol_version
supported_protocol_read_range
supported_protocol_write_range
supported_schema_versions by artifact
required core message kinds
enabled extension namespaces
digest algorithms
migration paths
implementation identity and version
manifest_digest
Before exchanging gate-bearing messages, peers MUST find a mutually supported protocol and payload-schema range. Writers MUST use one selected version for the entire message. Readers MUST NOT silently coerce an unsupported safety field, receipt state, approval state, evidence validity, review verdict, migration state, or failure code into success.
Additive, non-safety fields MAY be ignored or preserved when their schema says
so. Unknown safety-relevant fields or message kinds fail closed as
VALP-E-PROTOCOL-UNSUPPORTED.
Protocol and artifact schema versions remain independent:
A writer MUST declare the oldest reader it expects to consume its output. A reader MUST expose unsupported artifacts as blocked or legacy-read-only rather than rewriting them silently.
Extensions MUST use a stable namespace owned by the defining implementation or plugin. Extensions may add diagnostics, provider metadata, or UI hints. They MUST NOT redefine:
leader selection or epoch
message acceptance
state revision or legal transitions
receipt meanings
claim or evidence validity
approval requirements
review independence
failure state effects
migration success
Done Criteria
Any proposal to change those semantics requires a protocol RFC, compatibility analysis, migration plan, conformance fixtures, and review.
Every plugin MUST declare:
plugin_id
plugin_version
plugin_kind
provider_identity, when applicable
supported_protocol_range
input_message_schemas
output_message_schemas
requested_permissions
filesystem scopes
network scopes
command scopes
secret handles, never secret values
resource and timeout limits
failure namespace
entrypoint identity or runtime binding
manifest_digest
The manifest MAY also declare may_call_plugins as a unique array of exact
target plugin_id values. A missing or empty array means that the plugin MUST
NOT make plugin-to-plugin calls. Wildcards, provider-wide grants, and implicit
transitive grants are forbidden.
Plugin kinds may include discovery, runtime adapter, provider adapter, tool broker, transport, renderer, or evidence collector. The core contract is the same regardless of provider branding.
Plugins are untrusted. They MUST:
Plugins MUST NOT:
Before accepting a plugin-to-plugin invocation, the core MUST verify that the
active caller manifest explicitly lists the exact target plugin_id in
may_call_plugins. A missing, empty, wildcard, unknown, or non-matching entry
fails as VALP-E-PLUGIN-BOUNDARY before the target is invoked or a dependency
event is appended.
The core MUST append a plugin_dependency_added event before each accepted
plugin invocation. The event records:
installation_sequence
task_id
plugin_id, plugin_version, and manifest_digest
parent message and event ids
direct dependency kind
calling plugin identity, when this is a plugin-to-plugin call
declared output/evidence refs
These events form the authoritative directed dependency graph. Its nodes are
tasks and versioned plugin manifests; its edges are direct task -> plugin and
plugin -> plugin dependencies. On a boundary violation at installation
sequence N, the core MUST compute the affected-task set from events accepted
through N: a task is affected if and only if it has a directed path to the
quarantined plugin. The failure and quarantine event MUST record the graph
revision, N, direct edges, transitive closure, affected task ids, and a closure
content digest.
The core brokers plugin permissions and validates every output. A boundary
violation MUST quarantine the versioned plugin manifest, reject new invocations,
append VALP-E-PLUGIN-BOUNDARY, and apply the recorded dependency closure. Each
affected task MUST move to blocked through
plugin_dependency_quarantined. If an affected task previously had a terminal
result, the invalidation event reopens its projection as blocked, marks
dependent claims/evidence blocked, and revokes the current audit pass until
replacement evidence and re-review restore Done. A task with no path to the
quarantined plugin MUST NOT be blocked, reopened, or have evidence invalidated
by that violation.
Other installations SHOULD continue when their process, filesystem, secret, and transport isolation from the quarantined plugin is proven. If a shared plugin host makes cross-installation isolation unknown, each exposed installation records its own blocked or degraded state; it MUST NOT guess that it is unaffected.
An implementation that cannot enforce its declared plugin permissions MUST
report that limitation and MUST NOT enable the plugin for 0.3 Full Mode. It
may expose the integration as Manual Mode or experimental, with no Full Mode
claim.
Task and installation artifacts MUST reference core capability ids, principal ids, message kinds, claims, and evidence independently of one provider’s model or tool names. Provider metadata belongs in registry observations and plugin namespaces. Replacing a provider plugin MUST NOT require rewriting historical core evidence; it creates new observations, messages, and events.
Migration is a high-risk, explicit operation. A 0.3 implementation MUST NOT
silently rewrite an installation, task folder, schema, leader identity, plugin
permission, or evidence digest when it opens older data.
Every migration MUST use this sequence:
read source manifest and artifacts
-> validate source byte identity and structural consistency
-> select an explicit supported migration path
-> produce a dry-run plan
-> identify lossy or irreversible steps
-> record required approvals and free-space/runtime preconditions
-> checkpoint source state and active leader epoch
-> quiesce affected writers
-> apply into a staged target
-> validate target schemas, replay, projections, claims, and audit
-> atomically activate target or roll back
-> write migration receipt
The dry-run plan MUST include:
migration_id
source protocol and schema versions
target protocol and schema versions
source and target artifact digests
ordered transforms
legacy fields that cannot be promoted
expected state and registry revisions
required disk/runtime capabilities
affected plugins and tasks
approval requirements
rollback strategy
validation commands or conformance cases
plan_digest
The migration receipt MUST state applied, rolled_back, or blocked. It MUST
cite the exact plan digest, before/after manifests, event and registry
checkpoints, validation results, actor, approval, and failure refs.
0.2.0 To 0.3.0-draftThe required compatibility policy is:
0.2.0 task folders remain immutable legacy evidence;0.3 reader MUST identify them as legacy-read-only unless an explicit
import plan is approved;0.3 wrapper
records that cite the legacy source;official_claim or local_presence
observations when provenance is sufficient, but MUST NOT become
live_callable or task_verified without fresh proof;completed states MUST NOT be promoted into verified claims;0.3 zero-trust review gate;0.2.0 audit behavior remains available for unmodified legacy tasks.A new 0.3 installation begins with no selected leader. The user selects the
leader after bootstrap discovery. No migration tool may infer selection from a
local overlay, prior coordinator, provider default, or recent task history.
A failed preflight leaves the source active and the target uncommitted. A
failure after writer quiescence MUST either restore the source checkpoint or
leave the installation blocked with rollback_required; it MUST NOT report
partial success. Irreversible transforms require a separately visible user
approval and an export of original evidence before activation.
Downgrade is a migration, not a version flag change. It is permitted only when an explicit reverse path preserves all safety-relevant semantics. Otherwise the installation remains on the newer version or exports a read-only compatibility view.
The command names below define the intended control surface. The reference CLI implements the installation, candidate, selection, start, restart, show, and rotation slices; the broader RFC remains draft and may be refined before acceptance, provided the contracts remain testable.
| Proposed command | Purpose | Side-effect boundary |
|---|---|---|
valp install init |
Create installation identity and protocol manifest, then enter bootstrap | writes only bootstrap metadata; no leader or dispatch |
valp leader candidates |
Run safe candidate discovery and show evidence layers | read-only probes plus observation proposals |
valp leader select <principal> |
Record explicit user selection without activation | requires explicit user approval |
valp leader start |
Provision, attach, health-check, and activate or open the selected Leader | creates or opens one installation-owned runtime attachment |
valp leader open |
Open the active Leader from any caller workspace | focuses the current attachment or fences a missing one for replacement |
valp leader restart |
Fence and replace the same selected Leader session | explicit user command; preserves prior binding and receipts |
valp leader show |
Show exact active binding, session, generation, epoch, launch digest, and health | read-only |
valp leader rotate <principal> |
Provision and activate a different selected principal under a fenced new epoch | explicit user approval |
valp capabilities discover |
Run bounded discovery through enabled plugins | records validated observations |
valp capabilities reconcile |
Fold observations into a new registry revision | core-only projection update |
valp capabilities show [--layer ...] |
Inspect observations and effective status | read-only |
valp message validate <file> |
Validate schema, digest, compatibility, and refs | no side effects |
valp state show [--task ...] |
Show executable projection and blockers | read-only |
valp state replay [--check] |
Rebuild and compare event-derived projection | check mode read-only; repair separately gated |
valp claim show [--task ...] |
Show claims, evidence coverage, and verdicts | read-only |
valp review request ... |
Create exact-digest review request | writes request; does not create pass verdict |
valp protocol status |
Show active/supported protocol and schema ranges | read-only |
valp protocol migrate --to <version> --dry-run |
Produce a migration plan | no activation |
valp protocol migrate --plan <file> --apply |
Apply an approved, digest-matched plan | explicit approval required |
valp plugin list|inspect |
Show manifests, scopes, health, and quarantine state | read-only |
valp plugin enable|disable <id> |
Change plugin activation | permission and approval gated |
valp conformance [--profile ...] |
Run schema, reducer, failure, plugin, migration, and E2E fixtures | isolated test side effects only |
Existing publish, scan, route, dispatch, preflight, audit, and
doctor commands remain the 0.2.0 surface until implementation work changes
them. In 0.3, they MUST send validated messages to the core instead of writing
authoritative state directly.
CLI output MUST distinguish:
proposal vs accepted command
observation vs registry projection
runtime completion vs verified VALP claim
review request vs pass verdict
dry-run plan vs applied migration
experimental plugin vs Full Mode conforming plugin
legacy-read-only vs native 0.3 artifact
Implementation MUST proceed in independently reviewable phases. Later phases cannot claim completion from earlier document or fixture work.
Deliverables:
Exit condition: RFC accepted through normal repository governance. No runtime claim is made.
Deliverables:
0.2 legacy-read-only fixtures.Exit condition: schema and vector tests pass on every platform claimed for the reference CLI. No Full Mode claim is made.
Deliverables:
Exit condition: crash/restart, replay, split-brain epoch, duplicate-message, illegal-transition, bootstrap-epoch replay, bounded unreachable-leader rotation, and registry-consistency tests pass.
Deliverables:
0.2 output;0.2 task evidence is rewritten.Exit condition: representative existing examples and real sanitized tasks have explained projection parity or approved protocol differences.
Deliverables:
Exit condition: negative tests prove each gate fails closed and fixed evidence can recover through a visible correction cycle.
Deliverables:
0.2 legacy reader, dry-run migration planner, staged apply, and rollback.Exit condition: denied plugin operations cannot mutate authoritative state, and migration fault injection either restores the source or leaves an explicit blocked state.
Deliverables:
Exit condition: all stable-version Done Criteria in Section 21 pass. Only then
may maintainers consider 0.3.0 stable.
Conformance MUST be claimed by profile, not as a vague all-or-nothing label:
| Profile | Required behavior |
|---|---|
core-reader |
Parse, validate, preserve, and safely reject supported artifacts without mutation |
core-writer |
Strict messages, event/state reducer, registry, claims, failures, review, and persistence |
manual-mode |
Explicit human leader, manual receipts, claims/evidence, review, and audit without Full Mode claims |
full-mode-adapter |
Live principal/session control, current capabilities, submission proof, state mapping, evidence collection, failures, and audit |
remote-mode-adapter |
Full Mode contract plus explicit remote identity, transport, and evidence-location proof |
plugin-host |
Manifest validation, least privilege, isolation, quarantine, and namespaced diagnostics |
migration |
Dry-run, staged apply, validation, rollback/blocking, and legacy preservation |
An implementation MUST publish which profiles and platforms it passed, the test-suite version, artifact digests, and known limitations. Passing one profile MUST NOT be marketed as passing another.
The conformance suite MUST include:
valp-hello.v1 success, malformed/oversized response, timeout, nonce
byte mismatch, correlation-only semantics, unreadable manifest, and
incompatible exact-version-set cases;1, duplicate replay without a
new sequence, and gap/conflict rejection;0 allowed only for read-only discovery in bootstrap states
and permanently fenced after epoch 1 activation;degraded, explicit user approval, new epoch allocation, failed replacement,
current-epoch gate rejection between rotation approval and completion, and
stale-epoch rejection;Tests MUST include negative cases. A suite that demonstrates only the happy path is not a conformance suite.
Each conformance run MUST emit:
implementation and version
source revision or immutable build id
protocol manifest digest
test-suite revision
selected profile and platform
fixture digests
start/end timestamps
pass/fail/skip counts
failure refs
environment limitations
result digest
Skipped safety tests prevent a full profile pass unless the profile explicitly does not include that behavior.
0.3.0 stable requires one real non-HERDR Full Mode adapter. It MUST NOT invoke
HERDR, reuse HERDR pane submission as its delivery mechanism, or merely write
synthetic queue records. It must control a real addressable worker or hosted run
and export its own submission, lifecycle, output, failure, and evidence proof.
The public sanitized scenario MUST demonstrate:
valp-hello.v1 exchange before negotiated messages;1, read-only epoch 0 discovery,
explicit user selection, epoch 1 activation, and epoch 0 replay
rejection;degraded, receives explicit user approval,
activates a new epoch, and rejects a message from the stale epoch;valp audit with zero failures;The E2E may run on one declared supported platform. That proves only that adapter/platform pair. Other platform claims require their own conformance evidence.
0.3.0 Stable-Version Done Criteria0.3.0-draft may be used during implementation. The stable 0.3.0 designation
MUST NOT be published until all criteria below are satisfied.
SPEC.md.1, bootstrap epoch 0 fencing,
bounded emergency leader rotation, and named transition triggers are
implemented and tested.completed cannot satisfy Done without expected evidence.0.2.0.0.2.0 task folders remain readable and auditable by the legacy
path.0.2 imports preserve original bytes/digests and never infer a leader,
live capability, verified claim, or modern review.Passing repository unit tests alone is insufficient. Passing a synthetic fixture alone is insufficient. A visible demo alone is insufficient. Stable means the control-plane contracts survive negative tests, restart, independent review, migration, and at least one real non-HERDR runtime path.
This RFC provides content-identity, state-consistency, and decision contracts, not authenticated integrity or a blanket security claim. Implementations MUST document:
The protocol MUST store references and redacted summaries instead of secrets, raw private transcripts, unrestricted environment dumps, or unrelated user data. A content digest proves byte identity, not truth, authorship, or safe content. Those properties require provenance, authentication, evidence, review, and permission controls.
Rejected. Candidate scoring is useful evidence, but installation-wide authority is too consequential to infer. The user selects; the core records and fences.
Rejected. Model and runtime behavior is jagged. The leader coordinates, while deterministic code enforces messages, revisions, evidence, review, approvals, and failures.
Rejected. Declared, installed, callable, and task-verified capabilities answer different questions and decay at different rates.
Rejected. Direct writes make provider behavior part of protocol truth and make failure replay non-deterministic. Plugins propose; the core validates and commits.
Rejected. In-place rewriting destroys historical evidence and makes rollback ambiguous. Legacy bytes remain immutable; imports create new wrapper records.
Rejected. Those tests prove implementation components, not deployment reliability. Stable requires negative tests, restart, migration, independent review, adapter proof, and a real non-HERDR E2E.
This RFC pull request is complete when:
0.2.0 normative files, schemas, examples, CLI behavior, and release
claims are unchanged;git diff --check and scripts/verify-examples.sh pass;Acceptance of this document authorizes later reviewed implementation work. It
does not authorize migration, plugin activation, release, or publication of
0.3.0.