Factory docs, home

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"
  }
}

Source: docs/service-access-r2-receipt.json