Pure judgement of repository-scope Evidence for one sealed Candidate against a Slice's frozen acceptance, recipe and environment. It is a sibling of the fixture judge (scope local-fixture), never a relabelling of it.
Status: Accepted as step 1, increment 1, of the step-1 core (handover), reviewed together with increment 2 (Product gate and delivery records). Independent implementation review by Astra (gpt-6-astra, high effort): rounds 1–5 FAIL (6, 4, 3, 2 and 1 findings), round 6 PASS on 29 September 2026 (<local evidence archive>). At that review the increment tests passed 73/73 and the full npm run check 428/428, none skipped, on the pre-merge base f5a8991. No receipt exists yet, and this reference has no separate reference-review report. Its src/ consumers are the Product gate, the delivery records and the Repository collector (step 1, increment 3), which writes its records; the collector is accepted too, and nothing composes any of them. Local development only: no release or customer claim, and all 27 epics remain open. Human page: guide.
- Source:
src/repository-assurance.ts - Tests:
test/repository-assurance.test.ts(16); its import boundary is also asserted intest/delivery-boundaries.test.ts, andrejudgeRepositoryis exercised throughderiveAssessmentintest/delivery-adversarial.test.ts. - Helper:
test/helpers/repository-proof.ts(genuine store-written records for a synthetic Candidate, withscopeandeditsoptions).
Intelligence: optional — repository-assurance/review via frontier/medium; repository-assurance/fresh-scenario via frontier/high
What it hides
- Candidate-as-program checks. Each process check runs one stage's tree (the base B or the Candidate M) as the executor program over a host-held fixture input. It passes only on the pinned exit code and stdout whose SHA-256 is the pinned
stdoutDigest. Expected stdout exists only as that digest. - Authentication before Evidence.
judgeRepositoryfirst requires the manifest to hash topins.candidateDigestand to name the pinned store, Product, base, commit, Slice and the three frozen-input digests. Its files must givepins.candidateSnapshot, and the base listing (same commit, same tree)pins.baseSnapshot, bylistingDigest(the executor's snapshot formula, computed from file identities). A mismatch is aTypeError, not a rejection. - Record qualification. A gathered record counts only if all of these hold: it is associated with a process check; it came from
EvidenceStore.getin this process (isStoreRecord); its bytes and metadata still re-encode to its id (so bytes or metadata replaced in memory fail); it carries this Attempt's eight pins; it decodes as ansf-repository-observation/1; and its facts agree with the check, the Candidate, the metadata observation, the recipe's time bound and the pinned program, input, runtime, profile and bounds. - Completeness. Every reference the expectation associates with a check must be supplied. A missing one is rejected as "the associated reference was not supplied", so leaving out a failing guardrail's record can never give
Verified.
Public interface
Runtime imports: node:buffer, node:crypto, canonicalJson from ./canonical-json.ts, SNAPSHOT_PROTOCOL from ./confined-executor.ts, PIN_FIELDS and isStoreRecord from ./evidence.ts, parseRepositoryIdentity from ./repository.ts; types from ./assurance.ts, ./confined-executor.ts, ./evidence.ts and ./repository.ts. No node:fs, node:child_process or node:http (asserted by test).
- Constants:
REPOSITORY_VERDICT_SCOPE = "local-repository",ACCEPTANCE_PROTOCOL = "sf-repository-acceptance/1",RECIPE_PROTOCOL = "sf-repository-verify/1",ENVIRONMENT_PROTOCOL = "sf-confined-environment/1",REPOSITORY_COLLECTOR = "sf-repository-scenario-collector/1",OBSERVATION_FORMAT = "sf-repository-observation/1",REPOSITORY_ASSURANCE_LIMITS = {maxGuardrails: 120, maxReviews: 6, maxArgs: 32}(frozen). - Frozen inputs:
parseAcceptance(beforeis stagebase,afteris stagecandidatewith a different expected exit code, guardrails are stagecandidate, check names are unique; an extra member, or a review among the process checks, is aTypeError),parseRecipe,parseEnvironment, andacceptanceDigest,recipeDigest,environmentDigest("sha256:"of the canonical record). listingDigest(files): equals the executor'ssnapshotDigest(200 random listings in test) and refuses, withTypeError, what the executor refuses (at most 1024 files, 16 MiB, 256-character paths of at most 16 segments, case-folded collisions, a path that is both file and directory).parseRepositoryPins,evidencePinsOf(pins):scenarioRevision = "sf-repository-scenario/1/" + sha256(canonical {productId, store, base, commit, slice, baseSnapshot, candidateSnapshot, acceptance}), wherestoreis the hex SHA-256 of the canonical store identity andacceptanceits digest. The recipe, environment and collector revisions are<protocol>/<hex>, the collector's hex being the recipe'scollector.closure. Every pin is at most 99 characters at the longest legal ids.parseStoreIdentitywrapsparseRepositoryIdentityin aTypeError.- Observations:
encodeObservation(facts, stdout, stderr)anddecodeObservation(bytes)(exact inverse, elseundefined; canonical facts at most 320 KiB),observationOf(facts)(theProcessObservationthe facts imply),classifyObservation(observation, expectedExitCode)(identical to the fixtureclassify, pinned by an exhaustive test). - Judgement:
judgeRepository(expectation, manifest, base, gathered): RepositoryVerdict, pure.rejudgeRepository(verdict, gathered): {verdict, facts}: the judgement the Verdict's own expectation gives these records now, without authenticating a manifest and base (the caller holds the Verdict's pins to its frozen records), plus the decoded facts of every qualifying record by reference. The Verdict is reproduced only when the result equals it.parseRepositoryVerdict(a fixture Verdict, or any other scope, is aTypeError) andverdictDigest("sha256:"of the canonical Verdict: the integration proof).
- Types:
ScenarioCheck,ReviewCheck(reserved),AcceptanceRecord,ExecutorBounds,RecipeRecord,EnvironmentRecord,ListingFile,RepositoryPins,BaseListing,RepositoryExpectation {pins, evidence: {ref, check}[]},RepositoryVerdict {result, scope, expectation, checks, rejected (each with its check), evidence, reason},ObservedProcess,ObservationFacts.
Invariants and guarantees
- Pure. No filesystem, process, network, journal or clock. The same inputs give a deep-equal Verdict with a stable digest.
- Result rules. Per check: no qualifying record gives inconclusive; among several, the first failed decides, else the first inconclusive, else the first passed. Any failed check gives
Failed(includingbeforeexiting with theaftercode). Otherwise any inconclusive check or any rejected entry (a repeated reference, an unassociated one, or an associated one not supplied) givesInconclusive. OtherwiseVerified. Review checks are always inconclusive. - Stdout is decisive. The pinned exit code with other stdout is
failed(test "stdout digests are decisive…"). - Disqualification (test rows): no association, a fault entry, a hand-built or copied record, the wrong id, bytes or metadata swapped in memory, another check, another collector, environment, Product, Attempt or fixture pin, undecodable bytes, facts that disagree with the check, stage, Candidate or metadata, a run that was not attributable, had problems, was stopped or killed, left pipes open or has no exit,
timeoutMs0 or above the recipe bound,endedAt < startedAtor pastdeadline, and any identity other than the pinned program, entry, args, input, runtime, profile or bounds. - A stored Verdict is internally consistent.
parseRepositoryVerdictaccepts only what the judge could have returned: checks in acceptance order, each reference judged at most once and only under its associated check, every associated reference either judged or rejected, rejected entries tagged with their associated check, review checks reserved, and a result that follows from the checks and rejections. - No relabelling. Fixture Verdicts do not parse here; fixture records never qualify here, and repository records never qualify in
judge(test "relabel guards…").judge,Verdict.scope,EvidencePins,PIN_FIELDSand the fixture sources are unchanged. - Envelope identity recomputed here equals
evidence.ts's record id (test "the envelope digest…").
Failure semantics
TypeError for malformed pins, expectations, frozen inputs, listings, facts or Verdicts, for more than 1024 gathered entries, and for a manifest or base that is not the pinned one. Every other problem is a rejection, with its reason and associated check, or a check outcome. decodeObservation returns undefined rather than throwing.
Trust scope
- Established locally: the rules above, over genuine records written to a real Evidence store and deliberately forged or mutated ones; macOS arm64, Node 26.8.1.
- Not established: a composed collection or a real Slice's judgement (the collector's tests judge real sandboxed runs of test Candidates here), or attribution of hostile Candidate code beyond what the collector records. Authentication is re-derivation over trusted local storage: a writer with store access can forge. Review checks are not implemented.
Composition
- Depends on: Command journal's canonical JSON, Confined executor's
SNAPSHOT_PROTOCOL, Evidence / Assurance'sPIN_FIELDS,isStoreRecordand types, and Repository'sparseRepositoryIdentityand manifest types, all unchanged. - Used by:
src/product-gate.ts(re-derivation at integration admission, frozen-input digests, Verdict parsing and digests),src/delivery-records.ts(record parsers,listingDigest,rejudgeRepositoryinderiveAssessment) andsrc/repository-collector.ts(parseRepositoryPins,evidencePinsOf,encodeObservation,observationOf,listingDigestand the protocol constants, to write the records). Nothing else insrc/orapps/imports it. - Caller contract: the Repository collector (step 1, increment 3: accepted) runs each check through the Confined executor and stores one
sf-repository-observation/1record per run underevidencePinsOf(pins); its tests judge those records here. The delivery composition (increment 4, not built) is to name each reference's check in the expectation, read the records back withEvidenceStore.getand pass them here.
Changing it safely
- Run
node --test test/repository-assurance.test.ts, then the gate and records tests (they re-derive Verdicts), thennpm run check. - Any change to a digest formula, pin formula, observation format or result rule invalidates every recorded repository Verdict and integration proof: bump the protocol constant.
- Keep
classifyObservationidentical toclassify, and keep the executor limits this module repeats in step withconfined-executor.ts(both pinned by tests).
