# HelmLoop Protocol v0.1

Status: implemented here as a deterministic protocol core, executable schemas, CLI, fixture suites, and two observation adapters. Better Workflows runtime adoption, cross-runtime portability, production readiness, and business outcome effectiveness are unproven.

## 1. Scope

HelmLoop is a control-contract protocol for evaluating a requested transition against explicit outcome, evidence, authority, assurance, promotion, reconciliation, and observed-outcome records. It is not a scheduler, orchestration graph, execution engine, provider, truth store, or resource owner.

The normative words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, and MAY are interpreted as requirement levels in this document.

The five non-substitution rules are normative:

```text
completed ≠ PROMOTE
intent ≠ action authority
receipt ≠ business outcome
instance nesting ≠ ownership
preference ≠ authority
```

No record, adapter, skill, fixture, test result, or CLI exit code may silently cross one of these boundaries.

## 2. Version and canonical profile

- Protocol version: `0.1`.
- Canonical profile: `HL-C14N-1`.
- Record digest domain: the UTF-8 bytes `HelmLoop/v0.1/Record` followed by NUL, then the canonical JSON bytes.
- Binding digest domain: the UTF-8 bytes `HelmLoop/v0.1/Binding` followed by NUL, then the canonical JSON bytes.
- Digest algorithm: SHA-256, rendered as lowercase `sha256:<64 hex>`.

`HL-C14N-1` is a versioned HelmLoop profile; v0.1 does not claim complete RFC 8785 compatibility. It requires:

1. strict JSON parsing with duplicate keys, BOM, trailing input, invalid escapes, non-NFC strings, lone UTF-16 surrogates, non-finite numbers, non-integer numbers, and unsafe integers rejected;
2. plain JSON objects only, with object keys sorted by explicit UTF-16 code-unit order and members serialized directly in that order (including integer-like keys; rebuilding an ordinary JavaScript object before `JSON.stringify` is not conformant);
3. array order preserved;
4. the record `digest` field omitted when a record digest is computed; and
5. exact byte equality for digest verification.

Canonical vectors are executable in `canonical-vectors.json`. A matching digest proves canonical integrity only. It does not authenticate an issuer, owner, producer, reviewer, provider, or runtime.

## 3. Common envelope and references

Every normative record has this common envelope:

- `schemaVersion`: exactly `0.1`.
- `recordType`: one of the eight record types below.
- `id`: immutable record identity.
- `subject`: exact `{id, kind, ownerId}`.
- `source`: exact `{system, producer, locator}`.
- `revision`: exact `{id, digest}`.
- `policy`: exact `{id, revision, digest}`.
- `issuedAt`: normalized UTC timestamp with millisecond precision.
- `digest`: `HL-C14N-1` record digest.
- optional `supersedes`: exact digest-bound predecessor references with a reason.

Every record is canonically digested. Each of the seven record types other than `OutcomeContractV1` MUST contain an exact `{recordType,id,digest}` reference to one `OutcomeContractV1`. A reference MUST resolve by type and ID and MUST match the resolved digest. Every `supersedes` reference is resolved under the same rule, MUST name the same record type and subject as its successor, and its successor's `issuedAt` MUST be greater than or equal to its predecessor's `issuedAt`; it cannot be used as an unverified or time-regressing history hint. A missing predecessor is unresolved and yields `HOLD`; its successor is ineligible and the affected axis plus any dependent gate MUST remain non-pass. A wrong type, digest, subject, or regressing issuance time is invalid and yields `REJECT`. A record bound to the wrong subject or a substituted OutcomeContract is dependency-ineligible: dependent assurance, Promotion, reconciliation, and observed-outcome evaluation MUST behave as though that invalid record were unavailable. Mutable ID-only references are invalid.

All objects defined by the schemas reject unknown properties unless the schema explicitly allows an application payload object. Unknown protocol versions MUST yield `HOLD`; they MUST NOT be silently downgraded.

## 4. The eight normative records

The bundled schema is `schemas/helmloop-v0.1.schema.json`; each named entry schema in `schemas/` is executable and selects one definition from that bundle.

### 4.1 `OutcomeContractV1`

Defines the accountable outcome boundary: owner, desired outcome, explicit risk, constraints, non-authoritative preferences, a start/end horizon, machine-readable criteria, stop conditions, and all six required axes. `accountableOwnerId` MUST equal `subject.ownerId`. Each criterion has an ID, description, and digest computed from exactly `{id,description}`. An active contract MUST enumerate every axis exactly once. Preferences MUST NOT be interpreted as authority.

### 4.2 `AuthorityGrantV1`

Defines a bounded authorization for one exact grantee, action, parameters, scope, subject, revision, validity window, and idempotency key. `issuerId` MUST equal the contract's `accountableOwnerId`. `actionDigest` binds all action fields. A `promote` grant's parameters MUST be exactly `{targetRevision,targetDigest}` and equal its bound revision ID and digest. Consequential grants in v0.1 MUST be single-use. `available → reserved → consumed` changes are append-only lineage records; sequence starts at one, increments by exactly one, contains no orphan or competing head, binds the exact predecessor digest, and never regresses `issuedAt`. Revocation is append-only and terminal: a revoked lineage MUST NOT continue or return to an unrevoked state. Revoked, expired, wrong-binding, wildcard, ambiguous, orphaned, time-regressing, or double-consumed authority is not usable.

Before any consequential dispatch, a host MUST durably persist the exact reserved grant head. The reserved record's `issuedAt` MUST be no later than dispatch/decision time. It MUST NOT dispatch from `available`, and MUST NOT mark `consumed` before the attempt has a terminal fact. The consumed head MUST directly supersede the exact reserved head referenced by the consumer, bind that consumer with an exact `{recordType,id,digest}` `consumedBy` reference, and have `issuedAt` no earlier than the receipt's terminal time or the promotion's `decidedAt`. It must also exist no later than the explicit evaluation snapshot; a later consumed head cannot retroactively authorize an earlier result. Consumer identity is the complete reference; equal IDs in different record types or under different digests cannot collapse into one use. A reserved grant without a matching terminal record is `UNKNOWN/HOLD`; the core MUST NOT infer a safe retry. It may retry only after a fresh provider reconciliation has established that the prior attempt did not take effect and a separately valid authority path permits the retry.

### 4.3 `ExecutionReceiptV1`

Records one exact execution attempt, executor, action binding, optional external authority reference, terminal or unknown status, resource owner/instance, and optional provider-reported state. A provider report binds `name`, `requestId`, `requestedState`, `reportedState`, and `queryLocator`. Every evaluated receipt MUST match the policy's expected action, parameters, and scope; an unused matching grant cannot substitute for what actually ran. A consequential receipt MUST reference the exact durable `reserved` head used before dispatch; the later `consumed` head binds that receipt as its terminal consumer. `authority.required` is strictly boolean, and `false` is allowed only when host policy says the action is non-consequential and cannot coexist with grant-binding fields. Instance parentage never changes `resourceOwnership.ownerId`. v0.1 evaluates exactly one `ExecutionReceiptV1` for a `CONFORMANCE` transition; multiple receipts are invalid rather than sharing one receipt's reconciliation.

A successful receipt changes the execution axis only. It does not create a Promotion decision, prove reconciliation, or establish an observed business outcome.

### 4.4 `EvidenceRecordV1`

Records a structured claim, a machine-readable `{id,digest,conclusion}` criterion result, support/contradiction/unknown status, observation time, declared validity limit, provenance artifact digest, dependency revisions, and limitations. The criterion ID and digest MUST exactly match one criterion in the bound contract. Status and conclusion MUST agree (`supports/MET`, `contradicts/NOT_MET`, or `unknown/UNKNOWN`); free-form claim prose cannot establish outcome. Effective freshness is the earlier of the declared `validUntil` and the host policy age cap. At `evaluatedAt == validUntil`, evidence is stale. Future-dated observations or issuance are not fresh. A stale subject/revision/policy binding, untrusted producer/provenance, or unresolved supersession makes the record ineligible for assurance and Promotion and cannot be overwritten back to `FRESH`. Outcome evidence observed before the contract horizon is `NOT_DUE`; evidence outside its applicable horizon cannot establish `MET` or `NOT_MET`.

### 4.5 `AssuranceDecisionV1`

Records required and provided assurance level (`L0`–`L3`), exact evidence references, reviewer, rationale, and decision. The host-required level is a policy-digested reducer control. Every referenced evidence record's `observedAt` and `issuedAt` MUST be no later than `decidedAt`; later evidence cannot retroactively justify the decision. The reducer requires an exact required-level match and recomputes level and required-claim sufficiency from fresh criterion-bound evidence. A self-declared `sufficient` decision with a lower or policy-mismatched level is invalid. Assurance sufficiency supplies evidence for a gate; it is not authority or Promotion.

### 4.6 `PromotionDecisionV1`

Records an owner decision for one exact revision. `actionParameters` is not an open application payload here: it MUST contain exactly `targetRevision` and `targetDigest`, and both values MUST equal `targetRevision.id` and `targetRevision.digest`. An approval requires a separate exact `promote` grant carrying the same action binding, an assurance decision, evidence references, idempotency key, and monotonic decision lineage. The referenced assurance decision itself MUST be in the reducer's recomputed sufficient set and cover every policy-required claim; another sufficient decision elsewhere cannot substitute for it. The assurance decision's `decidedAt` and `issuedAt`, and every referenced evidence record's `observedAt` and `issuedAt`, MUST be no later than the promotion's `decidedAt`. The promotion's own `decidedAt` and `issuedAt` MUST be no later than `context.evaluatedAt`; a future owner decision cannot retroactively approve the snapshot. Competing heads, an initial decision that names a predecessor, or unbound lineage are ambiguous and yield `HOLD`; a successor whose `issuedAt` precedes its predecessor is invalid and yields `REJECT`. Completion, review success, or provider success MUST NOT synthesize this record.

### 4.7 `ReconciliationRecordV1`

Records a fresh provider query for one exact execution receipt, requested state, provider state, independently observed state, and the resulting reconciled state. Its correlation tuple MUST exactly match the receipt's `executionId`, `attemptId`, provider `name`, provider `requestId`, and `queryLocator`; the query source system/locator and requested state MUST match the same provider binding. A terminal receipt's query MUST be observed no earlier than `finishedAt`; an unknown attempt's query MUST be no earlier than `startedAt`. `success` is valid only when provider state is success and observed state matches. Unknown, stale, multiple, missing, contradictory, mis-correlated, or causally premature reconciliation yields `HOLD` or `REJECT` as specified below. Reconciliation is not observed business outcome.

### 4.8 `LearningSignalV1`

Records a bounded proposal based on exact outcome evidence references. It is not a truth update and is not used by the reducer to establish the observed-outcome axis. An accepted/writeback signal requires a separate approving owner decision for the same subject/revision and evidence plus an active owner-issued `learning.writeback` grant. That grant's action parameters MUST bind `learningSignalId` and a `proposalDigest` over the signal ID, outcome status, proposal, and evidence references. The v0.1 core still does not execute the writeback. The thin skill and adapters MUST NOT approve, accept, or apply learning.

## 5. Six orthogonal state dimensions

Every reducer result MUST contain all six axes even when unresolved:

| Axis | v0.1 states | What it answers |
|---|---|---|
| `execution` | `NOT_STARTED`, `RESERVED`, `RUNNING`, `COMPLETED`, `FAILED`, `UNKNOWN`, `INVALID` | What the executor did |
| `evidence` | `MISSING`, `FRESH`, `STALE`, `CONFLICTING`, `INVALID` | Whether required claims are currently supported |
| `authority` | `MISSING`, `ACTIVE`, `EXPIRED`, `REVOKED`, `WRONG_BINDING`, `CONSUMED`, `AMBIGUOUS`, `INVALID` | Whether the exact action was authorized |
| `promotion` | `NOT_REQUESTED`, `PENDING`, `APPROVED`, `REJECTED`, `HOLD`, `AMBIGUOUS`, `INVALID` | What the accountable owner decided |
| `reconciliation` | `NOT_REQUIRED`, `PENDING`, `MATCHED`, `DIVERGED`, `UNKNOWN`, `INVALID` | What the provider and observed external state show |
| `observedOutcome` | `NOT_DUE`, `NOT_OBSERVED`, `MET`, `NOT_MET`, `MIXED`, `UNKNOWN`, `INVALID` | What independent outcome evidence shows |

These are not a single lifecycle. A result can be execution `COMPLETED`, promotion `APPROVED`, reconciliation `MATCHED`, and observed outcome `NOT_MET` simultaneously. The overall requested transition then rejects without rewriting the individual facts.

## 6. Deterministic reduction

The implemented reducer is the pure asynchronous function:

```text
reduceHelmLoop({protocolVersion, context, records})
  -> {protocolVersion, targetTransition, disposition, axes,
      reasonCodes, inputDigest, recordResults, trust}
```

The complete `context` shape is executable as `$defs.EvaluationContext` in the schema bundle. `evaluatedAt`, policy, exact subject and revision, transition, expected action/parameters/scope, the boolean execution-authority requirement, required evidence IDs and assurance level, outcome claim ID, positive-integer evidence and reconciliation freshness caps, and a non-empty producer allow-list are all REQUIRED; unknown fields and wrong types are invalid. The reducer does not supply defaults or coerce strings, booleans, lists, or freshness caps. Missing controls yield `HOLD`, invalid supplied controls yield `REJECT`, and either condition keeps Promotion non-pass.

The reducer does not read the clock. Evaluation is explicitly as-of `context.evaluatedAt`: a record's `issuedAt` and any receipt terminal, evidence observation, assurance/Promotion decision, reconciliation query/reconciled, or learning observation event used by reduction MUST be no later than that instant. Later facts remain in the input commitment but are ineligible and cannot make an axis pass. v0.1 implements only the exact `CONFORMANCE` target transition; any other transition is unsupported and cannot pass. `context.policy.digest` MUST equal the `HL-C14N-1` binding digest of `policyId`, `policyRevision`, subject, revision, `targetTransition`, expected action/parameters/scope, execution-authority requirement, required evidence IDs, required assurance level, outcome ID, freshness caps, and the sorted trusted-producer set. Scope is a semantic set for action/policy binding and is sorted with the same code-unit comparator before either digest is computed. Thus changing a reducer control without changing the policy digest rejects. The adapter cannot set this evaluation or trust context.

Reduction order is:

1. strict parse at file/transport boundaries;
2. executable schema validation and canonical digest verification of every raw record before replay folding;
3. deterministic folding of canonically identical replay records plus rejection of body/digest equivocation;
4. explicit as-of-event filtering;
5. host trust classification;
6. exact reference resolution;
7. subject/revision/policy and lineage checks;
8. authority consumption and action binding;
9. evidence and assurance evaluation;
10. promotion, reconciliation, and outcome evaluation;
11. fixed UTF-16 code-unit sorting of validation issues, records, reason codes, and record IDs;
12. deterministic disposition and input digest.

Only records whose complete canonical bodies are byte-identical are folded as exact replay. Every raw record is validated first. Same identity with a different digest, or the same `{recordType,id,digest}` with a different body, is equivocation and rejects. `inputDigest` commits each distinct raw body while folding byte-identical replay, so a hidden conflicting body changes the digest. Input record order MUST NOT change the canonical decision.

Every conformance fixture declares the complete expected reason-code arrays separately for the `reject` and `hold` channels, including empty arrays for positive cases. The oracle requires exact set equality: a missing code, an unexpected extra code, or a code in the wrong channel fails even when disposition and axes are unchanged. The harness also forbids terminal-reject axis reasons in the HOLD channel and includes both actual reason-code channels plus all six axes in `helmloop-semantic-case-results/v1`.

The overall disposition is scoped to `context.targetTransition`; in v0.1 that value is exactly `CONFORMANCE`:

- `PASS`: every required axis is in its pass state and there are no hold/reject reasons.
- `HOLD`: evidence or state is missing, stale, unknown, ambiguous, unsupported, or awaiting an owner decision.
- `REJECT`: a record is tampered/invalid, an attempted action is unauthorized or wrongly bound, authority is revoked/expired/double-consumed, a terminal failure exists, or observed outcome contradicts the contract.

## 7. Failure semantics

| Condition | Required result |
|---|---|
| stale or future evidence | evidence `STALE`, transition `HOLD` |
| grant consumption or owner decision after `evaluatedAt` | ineligible for the snapshot; affected authority/Promotion axis cannot pass |
| unknown target transition or evaluation-policy digest drift | transition cannot pass; policy drift is `REJECT` |
| revoked or expired grant used by an execution | authority `REVOKED`/`EXPIRED`, transition `REJECT`, promotion cannot pass |
| wrong subject/action/parameters/scope/grantee/idempotency | authority `WRONG_BINDING` or `INVALID`, `REJECT` |
| wrong-subject or substituted-contract record used by another record | source record `INVALID`, dependency-ineligible; dependent assurance, Promotion, reconciliation, or observed outcome cannot pass |
| Promotion parameters do not exactly equal its target revision ID/digest | promotion `INVALID`, `REJECT` |
| two consumers for one single-use grant | authority `AMBIGUOUS`, `REJECT` |
| two or more execution receipts in one v0.1 evaluation | execution and reconciliation `INVALID`, `REJECT` |
| durable reservation without terminal receipt | execution and reconciliation `UNKNOWN`, `HOLD`; no dispatch retry without reconciliation |
| missing/competing authority lineage with no attempted use | authority `AMBIGUOUS`, `HOLD`; cannot promote |
| attempted use of an available/non-reserved, post-dispatch, pre-terminal-consumed, consumed-orphan, or post-revocation grant | authority `WRONG_BINDING`/`REVOKED`, `REJECT`; cannot promote |
| unknown or stale provider query | reconciliation `UNKNOWN`, `HOLD` |
| provider request/query correlation mismatch or query before the receipt's terminal boundary | reconciliation `INVALID`, `REJECT` |
| assurance required-level mismatch or promotion reference to a decision outside the recomputed sufficient set | evidence `INVALID`/promotion `HOLD`; transition cannot pass |
| evidence observed/issued after assurance, or assurance/evidence after Promotion | affected evidence/promotion axis `INVALID`, `REJECT` |
| criterion ID/digest/status/conclusion mismatch | evidence `INVALID`, observed outcome cannot pass, `REJECT` |
| duplicate owner heads or broken supersession | promotion `AMBIGUOUS`/`HOLD` |
| missing generic supersession history | affected state unresolved, `HOLD`; type/digest/subject mismatch is `REJECT` |
| successor `issuedAt` precedes its exact predecessor | affected axis `INVALID`, `REJECT`; successor is ineligible |
| missing evaluation control | transition `HOLD`; wrong type, unknown field, or malformed control is `REJECT`; Promotion cannot pass |
| stale revision or policy binding | affected axis unresolved/stale, `HOLD` |
| learning writeback requested without owner approval | `HOLD` |
| attempted or accepted learning without exact approval/grant | `REJECT` |
| unsupported protocol/runtime/mapping version | `HOLD` |

## 8. Adapter boundary

An observation adapter MAY emit only `ExecutionReceiptV1`, `EvidenceRecordV1`, and `ReconciliationRecordV1` candidates. It MUST return explicit gaps, reject reasons, and unsupported fields, and MUST fail closed on lossy or unknown required input. `HOLD` is limited to missing, unknown, stale, drifted, unsupported, or lossy input where no contrary attempted fact was established. A mapped or source-observed semantic violation is `REJECT`; the adapter MUST preserve any mapped observation candidates in that result so the fact is not erased. A host MUST honor adapter `REJECT` as terminal for that input and MUST NOT feed it through the reducer as if it were `READY`.

An adapter MUST NOT:

- create `OutcomeContractV1`, `AuthorityGrantV1`, `AssuranceDecisionV1`, `PromotionDecisionV1`, or accepted `LearningSignalV1` records;
- configure `trustedProducers` or authenticate itself;
- infer owner, authority, promotion, business truth, freshness policy, or provider state;
- execute a side effect; or
- invoke Better Workflows or any other runtime as part of validation.

The host owns a complete digest-valid OutcomeContract record and exact reference, all owner-controlled records, trust policy, and the evaluation context. Every adapter source projection MUST exactly match that full contract; an adapter cannot use an incomplete reference to invent omitted owner fields. Purely missing contract fields are `HOLD`; a supplied type, digest, or semantic contradiction is `REJECT`. `src/protocol/adapter-sdk.mjs` validates that contract and returns an opaque in-process capability; candidate sealing accepts only that capability, so callers cannot bypass validation by invoking the helper with raw bindings. It also rejects mapped collection contradictions—including one record identity with different digests and a provider query before its mapped receipt's terminal boundary—with both raw protocol and scoped adapter reason codes while preserving the candidates. A successful mapping result is labeled `unverified-observation`; only the host may independently verify and classify it. The standalone and Better Workflows mappings are versioned under `integrations/`.

## 9. Conformance and portability

The CLI commands are:

```text
node bin/helmloop.mjs validate <record-or-bundle.json>
node bin/helmloop.mjs reduce <evaluation-input.json>
node bin/helmloop.mjs digest <json-file>
node bin/helmloop.mjs conformance --adapter core
node bin/helmloop.mjs conformance --adapter standalone
node bin/helmloop.mjs conformance --adapter better-workflows
node bin/helmloop.mjs portability <runtime-receipts.json>
```

Core and adapter conformance suites are fixture evidence. Their reports MUST carry `evidenceClass=fixture`, `runtimeObservation=false`, and `portabilityEligible=false`. Both adapter reports identify the same selected semantic cases with a shared `fixtureSetId` and content-derived `fixtureSetDigest`; each separately reports its adapter fixture-manifest ID/digest and adapter/mapping identity. Passing them proves only the exact implementation/mapping matrix exercised by those fixtures.

The auxiliary `PortabilityReceiptV1` shape is in `portability-receipt.schema.json`. Receipt fields are claims, not authentication. Eligibility is structural, not inferred from free-form limitations: every qualifying receipt MUST declare `observationMode=live-runtime` and `fixtureDerived=false`. A portability gate passes only when at least two fresh, source-bound, structurally eligible `runtime-observation` receipts also have exact host-supplied verification bindings from a trusted verifier:

- include at least one receipt pair whose two members simultaneously differ in every required atomic independence dimension: runtime family, implementation ID, runtime instance, runtime-identity digest, source repository, source-tree digest, adapter ID, and adapter digest; marginal diversity across three or more receipts is insufficient when no single pair differs on all dimensions, and relabeling identical source or adapter bytes does not create independence;
- pass the same protocol, schema bundle, fixture set, acceptance-policy digest, and normalized `helmloop-semantic-case-results/v1` digest; that result digest includes every case disposition, all six axes, both actual reason-code channels, and failures, but excludes runtime-specific source details;
- bind exact source revision/tree, adapter, mapping, validator, producer verification, timing, and case results;
- match one host registry tuple containing receipt ID/digest, runtime-identity digest, producer-verification receipt digest, and trusted verifier ID; and
- contain explicit limitations.

Two adapter names in one process, two aliases of one implementation/instance/source, disjoint marginal values assembled across receipts without a fully independent pair, a self-declared verification claim without a host binding, or any fixture/synthetic receipt count as zero qualifying runtime portability observations. Even a passing gate supports only the exact host-attested pinned matrix. It does not establish production readiness, a complete ecosystem, or business effectiveness.

## 10. Claim taxonomy

Official HelmLoop surfaces use exactly four claim kinds, always with a subject and scope:

- `implemented-here`: directly executable in this repository and backed by a local command/artifact.
- `external-reference`: a behavior or integration belonging to another named system; not implemented here.
- `design-inference`: a proposed interpretation or architecture claim.
- `unproven`: an explicit evidence gap.

Better Workflows mapping code and fixtures are `implemented-here`; Better Workflows runtime conformance is `external-reference` and currently unproven. Cross-runtime portability, production readiness, and business outcomes remain `unproven` until their separate gates have qualifying evidence.

## 11. Compatibility

v0.1 consumers MUST reject or hold unknown fields and versions according to their declared mapping contract. Schema compatibility alone does not grant semantic compatibility. Any future change to canonicalization, record semantics, reducer ordering, required axes, or pass states requires a new version and fresh conformance evidence.
