Summary
packages/cli/templates/receipts/collect.mjs:214 sets source: value.self_reported === false ? "platform" : "agent" straight from whatever is in .agent-sdlc/checks.jsonl. That file lives inside github.workspace, the same working tree the agent has full write access to for the whole run. FACILITY_RECEIPT_CHECKS_FILE points there in all six generated workflows (facility-crew.yml:328, facility-codex.yml:304, facility-review.yml:112, facility-address-review.yml:131, facility-doctor.yml:151, facility-security-sweep.yml:152), and protect-files.mjs only blocks writes to .env* (packages/cli/templates/claude/hooks/protect-files.mjs:29). Nothing guards .agent-sdlc/.
So an agent whose bash tool appends a line with "self_reported": false to that file gets its own check reported with source: "platform" in the sealed receipt.
Where
packages/cli/templates/receipts/collect.mjs:202-220 (parseChecks)
packages/cli/templates/claude/hooks/protect-files.mjs:1-31 (only .env* is protected, .agent-sdlc/ is not)
- Workflows pointing
FACILITY_RECEIPT_CHECKS_FILE at the writable workspace: facility-crew.yml:328, facility-codex.yml:304, facility-review.yml:112, facility-address-review.yml:131, facility-doctor.yml:151, facility-security-sweep.yml:152
- Schema:
packages/core/src/receipts.ts:4-8 (ReceiptCheckSchema, source: "platform" | "agent")
Repro
- In a repo lane run, have the agent's bash tool append
{"name":"tests","status":"passed","self_reported":false} to .agent-sdlc/checks.jsonl before the receipt-collect step runs.
collect.mjs reads that line and emits source: "platform" in the sealed receipt.
Why it matters
This is the same class of problem engineEnv() already solved on the other side. runner/src/index.ts:2777-2785 strips RUNNER_TOKEN from the agent's process specifically so it "could [not] call /internal/runs/:id/{events,result} for its own run to forge platform-provenance check events or post a fake succeeded result." Good call. But in the repo lane an agent doesn't need the internal API to forge platform provenance, it can just write a line to a file it already owns. The overall run result (succeeded/failed) still comes from the real GitHub Actions step outcome and can't be faked this way, but checks[], the part of the receipt meant to answer "did this actually get verified", can be.
Suggested fix
Either stop trusting self_reported from this file at all (report every line from checks.jsonl as source: "agent", full stop), or do in collect.mjs what the runner already does in parseSelfReportedChecks (runner/src/index.ts:1573-1584), force self_reported: true on any line coming from the agent-writable file, and only let a dedicated workflow step running outside the agent's turn mark something as platform-verified.
Summary
packages/cli/templates/receipts/collect.mjs:214setssource: value.self_reported === false ? "platform" : "agent"straight from whatever is in.agent-sdlc/checks.jsonl. That file lives insidegithub.workspace, the same working tree the agent has full write access to for the whole run.FACILITY_RECEIPT_CHECKS_FILEpoints there in all six generated workflows (facility-crew.yml:328,facility-codex.yml:304,facility-review.yml:112,facility-address-review.yml:131,facility-doctor.yml:151,facility-security-sweep.yml:152), andprotect-files.mjsonly blocks writes to.env*(packages/cli/templates/claude/hooks/protect-files.mjs:29). Nothing guards.agent-sdlc/.So an agent whose bash tool appends a line with
"self_reported": falseto that file gets its own check reported withsource: "platform"in the sealed receipt.Where
packages/cli/templates/receipts/collect.mjs:202-220(parseChecks)packages/cli/templates/claude/hooks/protect-files.mjs:1-31(only.env*is protected,.agent-sdlc/is not)FACILITY_RECEIPT_CHECKS_FILEat the writable workspace:facility-crew.yml:328,facility-codex.yml:304,facility-review.yml:112,facility-address-review.yml:131,facility-doctor.yml:151,facility-security-sweep.yml:152packages/core/src/receipts.ts:4-8(ReceiptCheckSchema,source: "platform" | "agent")Repro
{"name":"tests","status":"passed","self_reported":false}to.agent-sdlc/checks.jsonlbefore the receipt-collect step runs.collect.mjsreads that line and emitssource: "platform"in the sealed receipt.Why it matters
This is the same class of problem
engineEnv()already solved on the other side.runner/src/index.ts:2777-2785stripsRUNNER_TOKENfrom the agent's process specifically so it "could [not] call/internal/runs/:id/{events,result}for its own run to forge platform-provenance check events or post a fakesucceededresult." Good call. But in the repo lane an agent doesn't need the internal API to forge platform provenance, it can just write a line to a file it already owns. The overall run result (succeeded/failed) still comes from the real GitHub Actions step outcome and can't be faked this way, butchecks[], the part of the receipt meant to answer "did this actually get verified", can be.Suggested fix
Either stop trusting
self_reportedfrom this file at all (report every line fromchecks.jsonlassource: "agent", full stop), or do incollect.mjswhat the runner already does inparseSelfReportedChecks(runner/src/index.ts:1573-1584), forceself_reported: trueon any line coming from the agent-writable file, and only let a dedicated workflow step running outside the agent's turn mark something as platform-verified.