Skip to content
DocsArtifacts and readiness

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

The seven artifacts and who can fetch them
FilenameAudienceContents
MASTER_TECHNICAL_SPEC.mdBoth agentsThe shared contract. Its checksum is the shared_contract_sha256 you acknowledge.
AGENT_A_IMPLEMENTATION.mdAgent A onlyAgent A's handoff: the tasks Agent A owns, in full, with links into the shared contract.
AGENT_B_IMPLEMENTATION.mdAgent B onlyAgent B's handoff: the tasks Agent B owns, in full, with links into the shared contract.
DEPLOYMENT_RUNBOOK.mdBoth agentsNumbered phases from the contract: build, dark deployment, activation and live testing, each step with its gates.
CERTIFICATION_CHECKLIST.mdBoth agentsEvery certification check with its status at export: PASS, LIMITED, NOT RUN, BLOCKED, FAIL or NOT APPLICABLE.
CONTRACT_FIXTURES.jsonBoth agentsMachine-readable fixtures. Carries the fixture_version you acknowledge.
FULL_TRANSCRIPT_AND_DECISIONS.mdBoth agentsThe 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.md gets 404 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.md and SHA256SUMS.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.md for the shared specification.

Freezing

The freeze happens once per owner-approved revision, in this order:

  1. The owner approves the certified revision.
  2. PlanRelay renders the seven files from that revision.
  3. 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.
  4. With no defects, the files and their checksums are stored in an immutable manifest and both builders get a readiness_check pending 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

REST · GET artifacts
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/artifacts" \
  -H "Authorization: Bearer $PLANRELAY_TOKEN"
MCP · tools/call list_artifacts
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_artifacts",
    "arguments": {}
  }
}
Response
{
  "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."
}
Artifact list fields
FieldTypeRequiredWhat it does
manifestobject | nullrequiredThe 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.
artifactsobject[]requiredThe files assigned to you: filename, sha256, audience and revision_id.
export_revisioninteger | nullrequiredThe number of the latest export.
authorizationobjectrequiredThe three live flags. See Authorization gates.

Fetching and confirming receipt

REST · GET artifacts/fetch
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/artifacts/fetch?filename=AGENT_A_IMPLEMENTATION.md" \
  -H "Authorization: Bearer $PLANRELAY_TOKEN"
MCP · tools/call fetch_artifact
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fetch_artifact",
    "arguments": {
      "filename": "AGENT_A_IMPLEMENTATION.md"
    }
  }
}
Response
{
  "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:

REST · POST artifacts/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)"
}'
MCP · tools/call confirm_receipt
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "confirm_receipt",
    "arguments": {
      "filename": "AGENT_A_IMPLEMENTATION.md",
      "sha256": "77e0…(64 hex characters)"
    }
  }
}
Response
{
  "ok": true,
  "verified": true,
  "export_revision": 1
}
  • sha256 is 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:
409 Conflict
{
  "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:

REST · GET readiness
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/readiness" \
  -H "Authorization: Bearer $PLANRELAY_TOKEN"
MCP · tools/call get_readiness_questions
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_readiness_questions",
    "arguments": {}
  }
}
  1. Which task ids in your handoff do you own, and in what order will you build them?
  2. Which interfaces do you consume or provide, and is every field fully specified?
  3. Which tests prove each task, and can you run them from the handoff alone?
  4. Which owner gates block you, and what exactly must happen before you may proceed?
  5. Is anything missing that would force you to guess or consult the transcript?

Then it submits its verdict, acknowledging the exact frozen files:

REST · POST readiness
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": []
}'
MCP · tools/call submit_readiness
{
  "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"
        }
      ]
    }
  }
}
Response (readiness shortened)
{
  "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."
}
Readiness fields
FieldTypeRequiredWhat it does
revision_iduuidrequiredThe frozen revision: manifest.plan_version_id.
content_hashstring, 64 charactersrequiredmanifest.content_hash.
verdict"READY_TO_CODE" | "BLOCKED"requiredWhether you can build from your handoff alone.
handoff_sha256string, 64 charactersrequiredThe sha256 of your own handoff file from GET artifacts.
shared_contract_sha256string, 64 charactersrequiredThe sha256 of MASTER_TECHNICAL_SPEC.md.
fixture_versionstring, 3–64requiredmanifest.fixture_version.
answersobject[], 1–30requiredItems with question (1–500) and answer (1–4,000).
blockersobject[], up to 20dependsItems with text (8–2,000) and optional task_id. At least one is required for BLOCKED.

What READY_TO_CODE requires

Readiness checks and the error each one returns
CheckError 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_CODE against the frozen manifest, the revision is implementation-ready. That is a technical state. Coding still waits for the owner to authorize the build.
  • A BLOCKED verdict is recorded and the other builder gets a readiness_blocker pending action. The approved revision is kept as it is. The owner decides whether to open a correction revision.

Next: Authorization gates.