A record from the Factory repository, docs/service-access-r2-receipt.json, shown as committed except that its local paths appear as placeholders in angle brackets, such as <local evidence archive>, standing for files that are not published. Every hash, date and count is as recorded. The build review says what the Factory's receipts support.
{
"recordedAt": "2026-09-29T22:22:24+00:00",
"base": "2101d4d",
"branch": "factory/astra-sweep-sa",
"disposition": "fixed-in-review",
"scope": "Service access, increment 1, revised after Astra round 2 (FAIL at 2101d4d, three findings): every workflow YAML scalar decoded before a name is read from it, env-template and script shell expansions, arithmetic and here-documents followed, a template's scan ended at any command that neither assigns nor exports, TOML table headers read as TOML reads them, and the members of those classes found while closing them. Test-made fixtures only; nothing composes the module; all 27 epics stay open.",
"describes": "the worktree of branch factory/astra-sweep-sa after the round 2 fixes: README.md, docs/agents/factory-model.md, docs/agents/intelligence.md, docs/agents/service-access.md, docs/build-review.md, docs/design.md, docs/guide/service-access.md, site/agents/factory-model.html, site/agents/intelligence.html, site/agents/service-access.html, site/build-manifest.json, site/reference/build-review.html, site/reference/design.html, site/service-access.html, src/service-discovery.ts, test/service-access-regressions.test.ts",
"supersedes": "nothing: docs/service-access-receipt.json keeps its hashes of the provisionally accepted revision (4bf1fa6) and docs/service-access-merge-receipt.json those of the merge; this receipt records the revision that answers Astra round 2",
"ownerDirection": "2026-09-29: \"Astra is back up, use it for consults on open work and add it back with fable as a reviewer\": acceptance needs both Astra (gpt-6-astra) and Fable 5.1 to pass",
"findings": [
{
"finding": 1,
"summary": "YAML quoting turned literal values into secret references",
"disposition": "confirmed, fixed with its class",
"fix": "every node decoded as YAML reads it before a name is read (quote doubling, YAML 1.2 escapes, flow folding from the lines as written, double-quoted keys, flow-sequence items); nothing read from a node the walker hides, cannot decode or cannot place; block scalars read whole; forward sources read from the script's own expression; expression literals single-quoted only, a stray double quote making the expression unread; plain-scalar comments end at the first ' #'; triggers the walker could not read count as reusable"
},
{
"finding": 2,
"summary": "env-template shell expansions exposed continuation values as keys",
"disposition": "confirmed, fixed with its class",
"fix": "shellReading follows quotes (ANSI-C and backticks included), parameter and arithmetic ($[...]) expansions, command substitutions, subshells, comments and here-documents over a line or a pair's whole text, and reports the commands the line runs; the scan ends wherever a shell would read on, run a command other than an assignment, an export or one of dotenv's inert forms (case patterns, [[ operands, a group, a function, an alias or a sourced file may follow), or name bash's alias table or command paths (BASH_ALIASES, BASH_CMDS) in any word, or export a name it does not write literally; shellCommands follows ${...}, $'...' and arithmetic ($[...], $((...)), ((...))) in scripts, reading (( again as two parentheses where bash does, and stopping, partial, past one extra pass"
},
{
"finding": 3,
"summary": "quoted TOML headers kept the previous section and leaked values as dependencies",
"disposition": "confirmed, fixed with its class",
"fix": "tableHeader reads bare, quoted, escaped and dotted headers; tomlDepth tracks multi-line arrays; an unreadable header, a bracket error or an over-long line ends the scan, partial"
}
],
"dispositions": "<local evidence archive>",
"tests": {
"files": {
"test/service-kinds.test.ts": 4,
"test/service-discovery.test.ts": 23,
"test/service-access.test.ts": 23,
"test/service-access-canaries.test.ts": 2,
"test/service-access-boundaries.test.ts": 3,
"test/service-access-adversarial.test.ts": 14,
"test/service-access-regressions.test.ts": 21
},
"added": "seven regression tests in test/service-access-regressions.test.ts, one per class; each fails on 2101d4d and passes here",
"existingTestsChanged": "none: no existing test or assertion was edited, weakened or removed"
},
"mutations": "Thirty-one source mutations, each reversing one round 2 fix in a scratch copy of the tree, are each caught by the new tests (<local evidence archive>; the first seventeen also in summary.txt)",
"acceptance": "not accepted: in review. Astra round 2 FAIL at 2101d4d superseded the interim Fable 5.1 round 1 PASS on which the increment was provisionally accepted and merged; this revision needs a fresh PASS from both Astra and Fable 5.1",
"review": {
"rounds": [
{
"report": "<local evidence archive>",
"model": "gpt-6-astra",
"reviewed": "2101d4d",
"verdict": "FAIL",
"findings": "three: YAML quoting read before decoding; env-template shell expansions and here-documents; quoted TOML headers. Round 1's eight findings resolved (its first three as reproduced; their classes reopened above). No merge defect: merge hashes and both parents' task-policy digests match"
}
],
"next": "fresh independent review of this revision by Astra (gpt-6-astra) and Fable 5.1; not started by this receipt"
},
"producer": {
"model": "claude-opus-5-5",
"role": "fixer; not the verifier"
},
"verification": {
"logDirectory": "<local evidence archive>",
"strictTypecheck": "passed (npx tsc --project tsconfig.json: no output, exit 0)",
"tscLog": "r2-pass2-tsc.log",
"tscLogSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"moduleSuites": {
"tests": 90,
"pass": 90,
"fail": 0,
"cancelled": 0,
"skipped": 0,
"todo": 0
},
"moduleSuitesLog": "r2-pass2-module-tests.log",
"moduleSuitesLogSha256": "ed6f2f1c194ae6c22f8a610c3260a2c506e9bbf9f019af3e334a08bf0be6d210",
"fullCheck": {
"tests": 1221,
"pass": 1221,
"fail": 0,
"cancelled": 0,
"skipped": 0,
"todo": 0
},
"checkLog": "r2-pass2-check.log",
"checkLogSha256": "489183b5b44dfb1f0018d74ebbf7c69a83f0c33d52060e874514bf5b1b64d97f",
"docs": "Built 66 pages and copied 13 assets into site",
"docsLog": "r2-pass2-docs.log",
"docsLogSha256": "3f3c9d627fc5166eef047565e6136f671f6459adc65b519c274b5ff0f8b49799",
"againstBase": "the seven regression tests added for round 2 fail on 2101d4d's source (r2-probes/regressions-against-head.log for the first five, r2-probes/regressions-pass2-against-head.log for the last two), and the last two fail on the first pass's source too (r2-probes/regressions-pass2-against-pass1.log)",
"note": "This receipt is written after the runs it reports; no test or page reads it, and the final check (r2-check-final.log) ran on the tree that includes it."
},
"limits": [
"Test-made fixtures only: no live listing, sign-in, repository or vault is read; the Tally-shaped fixture is synthetic",
"The credential screen is a heuristic over shapes, not a secret scanner: a name-shaped secret cannot be told from a name",
"The YAML walker, the dotenv and shell readings and the Kotlin and TOML lexers approximate libyaml, dotenv, bash and the compilers: the scalar decoder follows YAML 1.2's escapes and flow folding and refuses any other escape; the script reading follows bash, whose reading of a quote inside a backtick substitution POSIX leaves undefined; the template reading assumes a shell that brings no aliases or functions of its own, as a non-interactive one does; a script's array elements are read as the command line such an array commonly holds; what they cannot follow is partial or ends the reading, but a construct none anticipates may still be misread",
"Organisation-level secrets and variables are not listed (Fable round 1 finding 2: open, to be closed before increment 2 reads any live repository)",
"Bindings, declarations, confirmations and pins are attribution any process able to write the journal can forge",
"Not accepted: this revision awaits Astra's and Fable's review"
],
"evidenceSha256": {
"src/service-kinds.ts": "7a5a460502acf8fc2604b66fb7f2b4c9c4d774e43dea5870d9e361fe9c07806c",
"src/service-discovery.ts": "9c8adeeeed1d4a791747ab1c1a011dbd512be26b1885d510da57b22f9b5bcf46",
"src/service-access.ts": "fc06c35a56b49f56723fb26eff4b25dd4736c103752c65a8cb1c64e1c0d7639b",
"src/intelligence.ts": "2e4a60e52e2fda4e1ea9bf5625de13230498725ee5c0fabe57ba1eecc222695e",
"test/service-kinds.test.ts": "498920c90223c0a54423cf2c225221606cd3afd94e90bdf0637d19e747115128",
"test/service-discovery.test.ts": "4cc1fd241c798047b122947647e927ae97d445cfae7fa5b509a05585178e7fe2",
"test/service-access.test.ts": "a0b3e0495f483df8cc5d48599772e6ad6acf7359e9d5aa4c579f9bf5138c06f4",
"test/service-access-canaries.test.ts": "19e1cb87b9cdd2459033a451693fc8abfea460df020a636783e696d09699b0df",
"test/service-access-boundaries.test.ts": "754de78939d4a06cadf3f1f1ca39b5d05c471800a33a6fb3c414d67836499bf3",
"test/service-access-adversarial.test.ts": "4bedfe77d18c133c81020f4951e36f47ad64632061f0b2712d702e8cb344414d",
"test/service-access-regressions.test.ts": "da28831a327a44a89aa86d7cfc77a2d9763dc8481d9c599985463c769f6f328c",
"test/helpers/service-access.ts": "2c0073271caa56c787c2a63b9bdf5329964a42b18998ab122c96791a64a63ed8",
"test/helpers/service-access-child.ts": "dbd209dff3acfef8908b6264447d458bd86c1741766f615b3f87d214f551e2b0",
"docs/agents/service-access.md": "aa2c5fe2e393221884bb30944286dc26034a1bd2ed86a8ab880a66dbfd1d73b7",
"docs/guide/service-access.md": "2643cb921884f1e68b44c88863b304840f210158dcb2b7ffbfdf7ac66b56dcf2",
"docs/agents/factory-model.md": "bb0ff03ee97c901767c026ee638a89ac621a9cda2004737b6cabe6c69be5c081",
"docs/agents/intelligence.md": "d727a8a62741f1f49e7bafa03936358526f3ed9f1472b82b2f76451a033890ec",
"docs/build-review.md": "e49ceadbe4ae5f08290f275e1e7357cf891d07337642794104d113fe699349e6",
"docs/design.md": "8c089135148a7f8ce14ff49dca3b7dbba09e2ee8328de26306185140913d9e0a",
"README.md": "58af8c538229e9e1010018d06ee93fbb6b64427e79e15b317f1b236e8c8696d5",
"site/build-manifest.json": "9c2b9bd7f47b67a1cde6cbb0ef85f16e406065d65a9a86b293b14f3c1953d3c9"
}
}
