Counterfactual Immune Forge
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Counterfactual Immune Forgeadjudicate this defense mutation against the sealed attack scenario"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Counterfactual Immune Forge
Proof-gated defense evolution for AI agents and security automation.
When an agent, guardrail, or detection rule is defeated, the tempting next step is to patch it and move on. The Counterfactual Immune Forge refuses to accept a patch on anyone's word. It seals the attack scenario, proves the current defense really fails it, adjudicates each proposed change against that same sealed scenario, forces every change through a regression gate, requires a positive, policy-defined improvement, and then seals the whole decision — including every rejected candidate and the reason it was rejected — as a SHA-256 Merkle evidence root that anyone can recompute later.
It is a decision authority, not an actuator. It promotes nothing by itself, executes nothing, and touches no files, sockets, processes, or environment variables.
Why a practitioner would install this
Defense changes stop being trust-me changes. Every promotion carries recomputable evidence that the original attack was reproduced, that the fix defeated it, that protected behaviour still passed, and by how much the score improved.
Rejections are preserved, not discarded. The most useful review artifact is the list of fixes that looked good and failed a gate — including the "fixed the attack, broke legitimate traffic" case.
Goalposts cannot move. The scenario is canonicalized and hashed before any candidate is considered, and the same sealed object is used for the baseline and every candidate replay.
The audit trail is hash-linked. Every adjudicated episode is appended to an immune lineage whose links are verifiable independently of this server's memory.
Nothing about it is model-dependent. Reasoning about what to try can come from an agent, a fuzzer, a human, or a rules engine. The gates and the sealing are plain deterministic code.
Related MCP server: mcp-nixreview
Protocol
CIF/0.1, in order:
Canonicalize and hash the triggering scenario (
sealScenario).Reproduce the baseline against it. If it does not reproduce →
INCONCLUSIVE; no candidate is evaluated.Optionally record a diagnosis. Evidence only — it carries no promotion authority.
Screen each candidate for blast radius before it can earn replay credit.
Replay the surviving candidates against the same sealed scenario.
Apply the mandatory regression gate over protected behaviour.
Require a finite fitness score strictly above the baseline plus the configured margin.
Record every rejected candidate with a machine-readable reason.
Promote at most the single highest-scoring candidate that cleared every gate.
Seal the episode as a Merkle evidence root.
Run optional DREAM exploration after sealing, on the sealed evidence only. It cannot change the verdict or the root.
Rejection reasons: IMPACT_SCREEN_FAILED, SCENARIO_REPLAY_FAILED, REGRESSION_GATE_FAILED,
NO_PROVEN_IMPROVEMENT. Verdicts: PROMOTED, REJECTED, INCONCLUSIVE.
Full behavioural contract: SPEC.md.
Prerequisites
Node.js 20 or newer (
node --version). Nothing else — zero runtime dependencies.An MCP client that speaks stdio (Claude Code, Claude Desktop, Cursor), or direct library use from TypeScript.
No API key, account, network access, or Tenable product is required.
Install and run
git clone https://github.com/SweetKenneth/shpbl-immune-forge.git
cd shpbl-immune-forge
npm install # devDependencies only: typescript, @types/node
npm run build # compiles to dist/
npm test # 34 conformance, tamper, and boundary tests
npm start # starts the MCP server on stdioMCP client configuration:
{
"mcpServers": {
"immune-forge": {
"command": "node",
"args": ["/absolute/path/to/shpbl-immune-forge/dist/src/mcp-server.js"]
}
}
}Outputs
Every tool returns JSON text content. adjudicate_defensive_mutation returns the verdict
(PROMOTED / REJECTED / INCONCLUSIVE), the promoted candidate if any, every rejected candidate with its
machine-readable reason, the sealed scenario hash, the SHA-256 Merkle evidence root, and the appended lineage
entry. verify_episode_evidence and verify_immune_lineage return pass/fail integrity results,
export_immune_lineage_report returns the hash-linked lineage plus verdict counts, and describe_policy
returns the versions, thresholds, input limits, and rejection-reason vocabulary in force. Nothing is written to
disk and nothing is sent anywhere — the caller keeps whatever it chooses to keep.
MCP tools
Tool | What it does |
| Adjudicates one episode from recorded observations and returns sealed evidence plus a lineage entry. |
| Recomputes an episode's Merkle root and reports whether the covered bytes are unmodified. |
| Exports the hash-linked lineage of this session with verdict counts and an integrity flag. |
| Verifies an exported lineage link by link, without trusting this session. |
| Publishes protocol versions, hash algorithm, gate defaults, input limits, and the no-side-effect declaration. |
| Clears session lineage. Previously exported reports stay independently verifiable. |
The MCP surface is data-driven: your own harness runs the attack and the regression suite and reports what
it observed. The Forge enforces the gates over those observations. Missing evidence is always a failed gate,
never a pass. Library users who want the Forge to drive their harness directly can implement ForgeAdapters
and call CounterfactualImmuneForge.run().
Library use
import { adjudicateEpisode, verifyEvidenceRoot } from "shpbl-counterfactual-immune-forge";
const evidence = await adjudicateEpisode({
scenario: { kind: "prompt-injection", payload: { vector: "tool-arg" }, expectedSecurityProperty: "refuse untrusted tool instruction" },
baseline: { id: "guard", version: "1.0.0" },
baselineReplay: { reproduced: true, attackSucceeded: true, securityScore: 0.2 },
candidates: [{
mutation: { id: "quarantine", description: "quarantine tool-sourced instructions", patch: { rule: "quarantine" } },
defense: { id: "guard", version: "1.1.0" },
impact: { safe: true, reasons: [] },
replay: { reproduced: true, attackSucceeded: false, securityScore: 0.95 },
regression: { passed: true, failures: [] },
fitnessScore: 0.95,
}],
});
evidence.verdict; // "PROMOTED"
verifyEvidenceRoot(evidence); // trueThreat model and misuse boundary
Protects against silent defense regressions, unproven "fixes", moved goalposts, and post-hoc editing of what a promotion was based on.
Does not protect against an operator who ignores the verdict, or a harness that reports observations dishonestly. Garbage in is sealed as garbage — verifiably, and attributable to the reporter.
Refuses filesystem, network, process, and environment access entirely, so it cannot be repurposed as an offensive or surveillance tool. It never generates exploits and never applies changes to a live system.
Input limits are published by
describe_policy: 256 candidates per episode, 10,000 lineage entries per verification, 1 MiB per request, 32 levels of JSON nesting, and rejection of cyclic, non-finite, or unknown-verdict values.
Honest limitations
It is not an autonomous security oracle. It adjudicates the evidence it is given; it does not decide what is worth defending.
A compromised evaluator — a rigged harness or a fitness function that rewards the wrong thing — produces sealed evidence of a bad decision. Sealing proves integrity, not wisdom.
Fitness semantics are yours. The Forge only enforces "strictly better than baseline, by at least the configured margin".
Session lineage is in memory. Persist exported reports yourself if you need durable history.
Verification proves the evidence bytes are unmodified; it does not prove the observations were true.
Provenance
Invented and specified by SHPBL (Kenneth E. Sweet Jr.). This repository is a clean-room implementation written
from the public behavioural specification in SPEC.md; no proprietary SHPBL library bodies are included. The
Forge composes ideas SHPBL had already proven separately — replay, sandboxed impact screening, regression
authority, evolutionary scoring, evidence sealing, and post-proof exploration — into one gate chain where none
of them can be skipped.
More SHPBL security tooling: https://shpbl.com/tenable-submissions
Tenable status
Submitted to the Tenable CyberAgents Exchange for review on September 12, 2026 (pull request #169). Submission does not imply review, approval, certification, validation, endorsement, or acceptance by Tenable.
License
MIT — see LICENSE. Zero runtime dependencies; Node.js 20+.
Available Tools
6 toolsadjudicate_defensive_mutationB
Adjudicate one defensive-mutation episode from recorded observations: impact screen, same-scenario replay, mandatory regression gates and positive fitness delta, then seal the decision as a Merkle evidence root. Executes nothing and promotes nothing on its own.
| Name | Required | Description | Default |
|---|---|---|---|
| policy | No | ||
| baseline | Yes | ||
| scenario | Yes | ||
| diagnosis | No | ||
| candidates | No | ||
| baselineReplay | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose the key safety trait: no execution and no promotion, i.e. a non-side-effecting adjudication. It omits determinism/idempotency, whether the decision is persisted, and any auth or cost characteristics, so the disclosure is helpful but partial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, front-loaded with the action and followed immediately by the critical non-side-effecting caveat. Dense jargon but no filler; every clause maps to a real stage or constraint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the decision output (a sealed Merkle evidence root), which partially compensates for the absent output schema, and it frames the required-parameter flow. But with 6 nested parameters at 0% schema coverage and no annotations, the definition stops short of what an agent needs to construct a valid call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across six parameters (including nested scenario, baseline, baselineReplay, policy), yet the description names none of them explicitly. Its phrases ('same-scenario replay', 'impact screen', 'positive fitness delta') only loosely hint at baselineReplay and policy.requiredFitnessMargin, adding little usable meaning for the caller.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Adjudicate') plus resource ('one defensive-mutation episode') and enumerates the pipeline stages (impact screen, replay, regression gates, fitness delta, Merkle seal). It is clear what the tool does, though it never names the sibling tools it differs from (e.g. verify_episode_evidence), so an agent must infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The closing line 'Executes nothing and promotes nothing on its own' usefully implies this is an adjudication-only step, not a promotion path. However, there is no explicit when-to-use-vs-alternatives guidance against siblings like verify_episode_evidence or verify_immune_lineage, leaving the selection ambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_policyA
Return the protocol versions, hash algorithm, default gate thresholds, input limits, rejection reasons and the declared absence of side effects.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It discloses that the tool reports policy details including a "declared absence of side effects," which adds useful context, but it does not explicitly state whether the tool itself is read-only, what permissions are needed, or how the information is returned.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence that enumerates the returned policy details without filler or redundancy. Every listed item appears intended to help an agent understand the tool's output.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no annotations, the description is the primary specification. It lists the returned policy fields comprehensively enough for a no-argument informational tool, though it could specify format or scope more fully.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters and the schema is empty, so there are no parameter semantics to explain. Per the baseline for zero-parameter tools, this dimension is appropriately scored 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb, "Return," and enumerates the policy details returned: protocol versions, hash algorithm, gate thresholds, input limits, rejection reasons, and side-effect declaration. This clearly identifies an informational tool, though it does not explicitly differentiate from the verification/reset/export siblings beyond its read-only inventory purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to use this tool, when not to use it, or which alternatives exist. Usage is only weakly implied by the name and by the fact that it returns policy metadata.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
export_immune_lineage_reportB
Export the hash-linked lineage of every episode adjudicated in this session, with verdict counts and an integrity flag.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations at all, the description carries the full behavioral burden. It usefully discloses scope (only episodes adjudicated in this session) and output contents (lineage, verdict counts, integrity flag), but omits export format, whether the operation is read-only/safe, required permissions, and any rate or size limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence that front-loads the action and scope, with no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-annotation, no-output-schema export tool, the description partially compensates by naming the returned data (lineage, counts, integrity flag). It still leaves format, permissions, and failure/precondition behavior unspecified, so an agent lacks enough to call it confidently in edge cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there are no parameter semantics to document; the baseline for a 0-param tool is 4. The description instead clarifies what the parameterless export returns, which is the only meaningful semantic context available.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ("Export") and a well-scoped resource ("the hash-linked lineage of every episode adjudicated in this session"), and previews the payload (verdict counts, integrity flag). It is distinguishable from the sibling verify_immune_lineage, though it doesn't explicitly frame the contrast (export a report vs. verify lineage).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The "in this session" phrasing implies this is a post-adjudication reporting step, but there is no explicit when-to-use guidance, no prerequisites (e.g. must episodes be adjudicated first?), and no mention of alternatives such as verify_immune_lineage or describe_policy.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reset_stateB
Clear this session's lineage. Previously exported reports remain independently verifiable.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the full burden. It usefully discloses that previously exported reports remain independently verifiable, which is a non-obvious side-effect guarantee. However, it does not state whether the reset is destructive, reversible, or permission-gated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the action and followed by the key post-condition. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a state-resetting operation with no annotations and no output schema, the description should clarify destructiveness, reversibility, and required permissions. The exported-report note is valuable, but core behavioral details are missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4. The schema is empty and fully covered, and there are no parameter semantics for the description to clarify.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Clear this session's lineage.' It is clear enough to distinguish from export/verify siblings, but it does not explicitly name alternatives or scope the operation against them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool vs. the sibling lineage tools, no prerequisites, and no exclusions. The description merely states the action without context for selecting it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
verify_episode_evidenceB
Recompute the Merkle evidence root of a sealed episode and report whether the covered bytes are unmodified.
| Name | Required | Description | Default |
|---|---|---|---|
| evidence | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, but it does disclose the core behavior: it recomputes a root and reports a byte-level integrity verdict, implying a non-destructive read. It omits auth requirements, what happens on mismatch, and whether a failure is returned as an error or a boolean, leaving meaningful gaps for a security-sensitive tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense sentence that front-loads the action and ends with the outcome. No filler or restatement of the name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with a required nested-object parameter at 0% coverage, no output schema, and no annotations, the description leaves too much unstated: the expected shape of 'evidence' and the form of the result are both unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the single 'evidence' parameter is an undocumented nested object. The description mentions 'sealed episode' and 'covered bytes' but never explains what the evidence object must contain, so it does little to compensate for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb pair (recompute, report) and a precise resource (the Merkle evidence root of a sealed episode), so an agent can tell it is a cryptographic integrity check rather than a lineage or policy tool. It does not explicitly contrast itself with the sibling verify_immune_lineage, but the resource is distinct enough to disambiguate.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to use this tool versus the sibling verification tools, nor any prerequisites (e.g. that the episode must already be sealed). The agent must infer the trigger condition from the verb alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
verify_immune_lineageC
Verify an exported lineage entry list link by link without trusting this session's state.
| Name | Required | Description | Default |
|---|---|---|---|
| entries | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the full behavioral burden. It discloses that verification is link-by-link and independent of session state, but omits side effects, read-only vs mutating nature, error behavior, and return characteristics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler. It is appropriately concise for the stated purpose and every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations, no output schema, and 0% schema description coverage, the description is missing critical context: what an entry must contain, what verification checks, and what the agent should expect as a result. It is too sparse for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and there is one required parameter (entries). The description only calls it a 'lineage entry list' without explaining the expected object shape, required fields, or link structure, so it adds minimal meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (Verify) and resource (exported lineage entry list) and adds a scope condition (link by link, without trusting session state). It is clear enough to distinguish from siblings like export_immune_lineage_report, though it does not explicitly name alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit guidance on when to use this tool versus alternatives such as verify_episode_evidence or export_immune_lineage_report. The phrase 'without trusting this session's state' hints at a scenario but does not state when or when not to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.0- First observed
adjudicate_defensive_mutation - First observed
describe_policy - First observed
export_immune_lineage_report - First observed
reset_state - First observed
verify_episode_evidence - First observed
verify_immune_lineage
TDQS
Scored across 6 tools
Each tool targets a distinct operation in the evidence lifecycle: adjudicate an episode, verify episode evidence, export lineage, verify lineage, describe policy, and reset session state. The two verification tools are clearly separated by object (episode vs. lineage).
All tools use snake_case with a verb-first pattern: adjudicate_, verify_, export_, describe_, reset_. Deviations are minor and still fit the same convention.
Six tools is well-scoped for this specialized adjudication and verification server. Each tool has a clear, non-redundant role in the workflow.
The surface covers the full lifecycle: adjudicate and seal an episode, verify its evidence, export and verify lineage, inspect policy, and reset session state. No critical operation appears missing for the stated purpose.
Maintenance
Related MCP Connectors
Pre-execution governance for AI agents. Deterministic PASS/FAIL/REVIEW verdicts, replayable proof.
Deterministic pre-execution audit for trading agents. PASS/WAIT/FAIL, reproducible verdict_hash.
Preflight, approve, and prove consequential agent actions with signed evidence and x402 tools.
Verifier-grounded AI promotion gates, disposable report cards, and signed PASS/HOLD/BLOCK receipts.
Related MCP Servers
- AlicenseCqualityCmaintenanceEnables acceptance gates for AI coding-agent runs by recording evidence, running deterministic validation, applying a quality gate, and rendering auditable outcomes.7Apache 2.0
- AlicenseNot gradedqualityAmaintenanceA safety gate for agent-proposed NixOS configuration changes, grading security-relevant option deltas, attesting closures for vulnerabilities, and requiring human approval with a tamper-evident audit ledger.MIT
- AlicenseNot gradedqualityBmaintenanceEnables agents to open production change dossiers, attach proof certificates, and request human approval through a secure, unforgeable gate—ensuring no irreversible change can proceed until it has been verified against a sandboxed shadow copy.1401MIT
- AlicenseNot gradedqualityCmaintenanceEnables offline, deterministic verification that one immutable artifact followed a declared build-to-production promotion chain, using only hash-based evidence and failing closed on incomplete or nonconformant gate records.MIT