Contract
Artifacts and readiness
When the owner approves a certified revision, PlanRelay renders seven artifacts from the structured contract and the decision ledger, checks them against each other, and freezes them. Each builder then fetches its handoff, confirms what it received, and states whether it is ready to code.
The seven artifacts
| Filename | Audience | Contents |
|---|---|---|
| MASTER_TECHNICAL_SPEC.md | Both agents | The shared contract. Its checksum is the shared_contract_sha256 you acknowledge. |
| AGENT_A_IMPLEMENTATION.md | Agent A only | Agent A's handoff: the tasks Agent A owns, in full, with links into the shared contract. |
| AGENT_B_IMPLEMENTATION.md | Agent B only | Agent B's handoff: the tasks Agent B owns, in full, with links into the shared contract. |
| DEPLOYMENT_RUNBOOK.md | Both agents | Numbered phases from the contract: build, dark deployment, activation and live testing, each step with its gates. |
| CERTIFICATION_CHECKLIST.md | Both agents | Every certification check with its status at export: PASS, LIMITED, NOT RUN, BLOCKED, FAIL or NOT APPLICABLE. |
| CONTRACT_FIXTURES.json | Both agents | Machine-readable fixtures. Carries the fixture_version you acknowledge. |
| FULL_TRANSCRIPT_AND_DECISIONS.md | Both agents | The audit record: a grouped decision index, then the full transcript. Not current instructions. |
- An agent can list and fetch its own handoff and the shared files. Agent A fetching
AGENT_B_IMPLEMENTATION.mdgets404 ARTIFACT_NOT_FOUND, and the reverse. - Every artifact has a SHA-256 checksum computed by the database when the export is written.
- The owner sees all seven and can download them from the Relay.
- The owner's download also contains
README.mdandSHA256SUMS.txt. The README is a cover page generated at download time with the Relay's current status. It is not an artifact, is not in the frozen manifest, and agents never fetch or acknowledge it. - The Markdown artifacts link to each other by relative path and stable ids (task, interface, test, gate and decision ids). Each handoff holds its agent's tasks in full and links into
MASTER_TECHNICAL_SPEC.mdfor the shared specification.
Freezing
The freeze happens once per owner-approved revision, in this order:
- The owner approves the certified revision.
- PlanRelay renders the seven files from that revision.
- A consistency check runs across the files and the contract. It looks for a missing file, a file that does not carry the revision id and content hash, an endpoint defined by two interfaces, a dependency cycle, a build or deploy order that contradicts task ownership, a task missing from or misassigned in a handoff, a decision that is not accepted but is presented as current, and readiness or authorization claims written into a technical file.
- With no defects, the files and their checksums are stored in an immutable manifest and both builders get a
readiness_checkpending action.
With defects, nothing is frozen. The approved revision is not edited. Both agents get an artifact_defects pending action listing each defect, and the author submits a correction revision, which is reviewed and approved again.
Listing your artifacts
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/artifacts" \
-H "Authorization: Bearer $PLANRELAY_TOKEN"{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_artifacts",
"arguments": {}
}
}{
"manifest": {
"plan_version_id": "0b7c2d1e-3f4a-4b5c-8d6e-7f8091a2b3c4",
"content_hash": "9f2c…(64 hex characters)",
"manifest_hash": "4be1…(64 hex characters)",
"fixture_version": "fx1-0b7c2d1e",
"export_revision": 1
},
"export_revision": 1,
"artifacts": [
{
"filename": "MASTER_TECHNICAL_SPEC.md",
"sha256": "a41d…(64 hex characters)",
"audience": "BOTH",
"revision_id": "0b7c2d1e-3f4a-4b5c-8d6e-7f8091a2b3c4"
},
{
"filename": "AGENT_A_IMPLEMENTATION.md",
"sha256": "77e0…(64 hex characters)",
"audience": "A",
"revision_id": "0b7c2d1e-3f4a-4b5c-8d6e-7f8091a2b3c4"
},
{
"filename": "…",
"sha256": "…",
"audience": "BOTH",
"revision_id": "0b7c2d1e-3f4a-4b5c-8d6e-7f8091a2b3c4"
}
],
"authorization": {
"implementation_authorized": false,
"deployment_authorized": false,
"activation_authorized": false
},
"note": "Fetch with fetch_artifact, verify the sha256, then confirm_receipt. Receipt is not authorization."
}| Field | Type | Required | What it does |
|---|---|---|---|
| manifest | object | null | required | The frozen manifest: the revision (plan_version_id), its content_hash, the manifest_hash, the fixture_version and the export_revision. It is null until the artifacts are frozen. Readiness is accepted only when it is present. |
| artifacts | object[] | required | The files assigned to you: filename, sha256, audience and revision_id. |
| export_revision | integer | null | required | The number of the latest export. |
| authorization | object | required | The three live flags. See Authorization gates. |
Fetching and confirming receipt
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/artifacts/fetch?filename=AGENT_A_IMPLEMENTATION.md" \
-H "Authorization: Bearer $PLANRELAY_TOKEN"{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "fetch_artifact",
"arguments": {
"filename": "AGENT_A_IMPLEMENTATION.md"
}
}
}{
"export_revision": 1,
"filename": "AGENT_A_IMPLEMENTATION.md",
"sha256": "77e0…(64 hex characters)",
"content": "…the full file as text…"
}Compute the SHA-256 of content yourself and compare it with sha256. Then record the receipt:
curl -sS -X POST "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/artifacts/receipt" \
-H "Authorization: Bearer $PLANRELAY_TOKEN" \
-H "content-type: application/json" \
-d '{
"filename": "AGENT_A_IMPLEMENTATION.md",
"sha256": "77e0…(64 hex characters)"
}'{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "confirm_receipt",
"arguments": {
"filename": "AGENT_A_IMPLEMENTATION.md",
"sha256": "77e0…(64 hex characters)"
}
}
}{
"ok": true,
"verified": true,
"export_revision": 1
}sha256is the 64-character hex checksum of what you received.- When it differs from the stored checksum, the attempt is recorded as unverified and the response is:
{
"ok": false,
"error": "CHECKSUM_MISMATCH",
"message": "Checksum does not match the stored artifact. Re-fetch it.",
"expected_sha256": "77e0…(64 hex characters)"
}Confirm your own handoff and MASTER_TECHNICAL_SPEC.md. A receipt records delivery. It is not authorization to do anything.
Readiness
Each builder answers the readiness questions from its own handoff, without consulting the transcript:
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/readiness" \
-H "Authorization: Bearer $PLANRELAY_TOKEN"{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_readiness_questions",
"arguments": {}
}
}- Which task ids in your handoff do you own, and in what order will you build them?
- Which interfaces do you consume or provide, and is every field fully specified?
- Which tests prove each task, and can you run them from the handoff alone?
- Which owner gates block you, and what exactly must happen before you may proceed?
- Is anything missing that would force you to guess or consult the transcript?
Then it submits its verdict, acknowledging the exact frozen files:
curl -sS -X POST "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/readiness" \
-H "Authorization: Bearer $PLANRELAY_TOKEN" \
-H "content-type: application/json" \
-d '{
"revision_id": "0b7c2d1e-3f4a-4b5c-8d6e-7f8091a2b3c4",
"content_hash": "9f2c…(64 hex characters)",
"verdict": "READY_TO_CODE",
"handoff_sha256": "77e0…(64 hex characters)",
"shared_contract_sha256": "a41d…(64 hex characters)",
"fixture_version": "fx1-0b7c2d1e",
"answers": [
{
"question": "Which task ids in your handoff do you own, and in what order will you build them?",
"answer": "T1, then T2. T2 depends on the schema from T1."
}
],
"blockers": []
}'{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "submit_readiness",
"arguments": {
"revision_id": "0b7c2d1e-3f4a-4b5c-8d6e-7f8091a2b3c4",
"content_hash": "9f2c…(64 hex characters)",
"verdict": "BLOCKED",
"handoff_sha256": "77e0…(64 hex characters)",
"shared_contract_sha256": "a41d…(64 hex characters)",
"fixture_version": "fx1-0b7c2d1e",
"answers": [
{
"question": "Is anything missing that would force you to guess or consult the transcript?",
"answer": "Yes. See blockers."
}
],
"blockers": [
{
"text": "Interface IF-2 does not define the 409 error body.",
"task_id": "T2"
}
]
}
}
}{
"ok": true,
"verdict": "READY_TO_CODE",
"readiness": {
"implementation_ready": false,
"ready_builders": 1
},
"note": "Recorded. IMPLEMENTATION_READY is technical only; coding starts only after the owner presses Authorize Build."
}| Field | Type | Required | What it does |
|---|---|---|---|
| revision_id | uuid | required | The frozen revision: manifest.plan_version_id. |
| content_hash | string, 64 characters | required | manifest.content_hash. |
| verdict | "READY_TO_CODE" | "BLOCKED" | required | Whether you can build from your handoff alone. |
| handoff_sha256 | string, 64 characters | required | The sha256 of your own handoff file from GET artifacts. |
| shared_contract_sha256 | string, 64 characters | required | The sha256 of MASTER_TECHNICAL_SPEC.md. |
| fixture_version | string, 3–64 | required | manifest.fixture_version. |
| answers | object[], 1–30 | required | Items with question (1–500) and answer (1–4,000). |
| blockers | object[], up to 20 | depends | Items with text (8–2,000) and optional task_id. At least one is required for BLOCKED. |
What READY_TO_CODE requires
| Check | Error when it fails |
|---|---|
| revision_id and content_hash are the latest revision. | 409 STALE_CONTRACT_REVISION |
| That revision is certified by both agents. | 409 NOT_CERTIFIED |
| A frozen manifest exists for it. | 409 HANDOFF_NOT_FROZEN |
| handoff_sha256, shared_contract_sha256 and fixture_version are all present. | 400 ACK_REQUIRED |
| All five acknowledged values equal the manifest's values. | 409 STALE_ACKNOWLEDGMENT (with defects) |
| You have a verified receipt for your own handoff at handoff_sha256. Applies to READY_TO_CODE only. | 409 HANDOFF_NOT_RECEIVED |
| A BLOCKED verdict lists at least one blocker. | 400 BLOCKERS_REQUIRED |
After you submit
- When both builders have submitted
READY_TO_CODEagainst the frozen manifest, the revision is implementation-ready. That is a technical state. Coding still waits for the owner to authorize the build. - A
BLOCKEDverdict is recorded and the other builder gets areadiness_blockerpending action. The approved revision is kept as it is. The owner decides whether to open a correction revision.
Next: Authorization gates.