โฟป The Dual-Agent Harness
A verification harness for AI-agent work: one agent proposes, a second
independently proves, and the tests that decide are derived by hashing the
proposal together with a secret the proposer never sees โ so it cannot grind
them โ and, where the fact set is enumerable, every fact is probed. Config-
driven, zero dependencies, every axiom checked at runtime.
soulbae ๐ง proposes. soulbis โ๏ธ proves. Between them sits the Gap
โฟป: the witnesses are derived by hashing soulbae's own proposal with a run
secret it cannot see, so soulbae cannot tune or grind them and soulbis cannot
be accused of choosing them. Above them both is the First Person ๐ โ you
โ who alone opens the door to anything outward-facing.
That is the entire idea. Everything in this repo is machinery for keeping
those two apart, and a ledger discipline for what survives.
(โ๏ธโฅโฟปโฅ๐ง)๐ = neg โ bnot โ succ
The Swordsman is negation, the Mage is complement, and composed across the
Gap they produce the successor โ the step forward. On Z/64Z this is a
theorem, and engine/conform.mjs computes it for all sixty-four values every
time it runs rather than taking it on faith. A result exists only where the
two were genuinely held apart.
Why this matters for AI
As AI systems increasingly generate work and also judge it โ reward
models scoring their own rollouts, LLM-as-judge, an agent writing code and
then its own tests โ one failure recurs: the check an agent invents is the
check its work was built to survive. That is Goodhart's law wearing an agent
costume, and it is the failure behind reward hacking, self-preferring judges,
and green-because-written-green test suites. This harness answers it
structurally rather than behaviourally: the verification witnesses are
derived by hashing the proposal together with a run secret the proposer
never sees, so it cannot predict or grind the draw โ and where the witness
bank is small enough to enumerate, the gate is a census that probes every
one, so a single dropped fact cannot hide. The two agents share only their
root; the routing that would let them collude is a design invariant of the
seat topology (a target the runtime does not yet enforce with process mounts โ
see THREATS.md). Then a multiplicative gate refuses partial passes, anenforced-by claim register refuses claims stronger than their enforcement,
and the door keeps every outward action a human's.
RESEARCH.md states the contribution in full โ the problem, the precise
claim, what is borrowed (Fiat-Shamir, Promise Theory, the PVM model) versus
assembled here, the evidence on record, and the limits that evidence does not
cross. It is written to the standard the harness enforces on its instances: a
claim is worth what it can be re-run to show. PRACTICES.md sets the design
against the 2024โ26 field guidance on agent harnesses โ where it agrees,
where this goes further, and where the field is ahead โ every claim traced
through SOURCES.md.
Where it stands
This is not a sketch. The engine has been debugged by being run โ eleven
defects to date, every one invisible to inspection and obvious on execution,
each fixed and pinned by a test or a prompt rule (an outage reported as an
exhausted search; a gate that passed an unfilled config; a critic with no
word for a bad gate; seats whose disk record was thinner than their return;
a verifier that cried tamper on honest salted runs).
The runnable spar has a real advancing frontier: its guide compressed from a
730-word baseline to a 472-word validated best across three audited folds,
each held to an 8/8 held-out gate on independent witness draws, the last two
closed by an exhaustive fact census
(examples/field-guide/frontier.json is the authority; its chronicles/
tell the story). Fifteen real embodiments โ quantum circuits, ZK constraint
systems, research pipelines, consent agreements, an acceptance registry
whose first external run came from another organisation's machine, and one
deliberate failure โ are catalogued in HARNESS_PATHS.md. That catalogue
is the origin operator's fleet โ work done with the harness, kept as
evidence and pedagogy, not part of the system you adopt: like universe/,
it sits behind a seam, and every gate passes without it. The fleet's
divergence is deliberate โ every working piece it delivers feeds one
construction, trust-graph creation the agentprivacy way, because the harness
gives each piece the same verifiable shape (a ฮบ-addressed node; a
signature-minted edge). The skeleton is domain-neutral and stays that way โ
and it can be held in the hand alone: node tools/make_default.mjs emits
the default distribution, the system with no results and no chronicles,
the spar reset to its baseline, every gate re-proven inside the emitted tree
before the tool will hand it over.
What you can use it for
The system is a shape, not a topic. Anywhere an agent proposes work and the
check must not be the author's, the same loop fits:
- Optimization under a hard constraint โ make X smaller, faster, cheaper
while a gate the proposer cannot tune to stays green (code, circuits,
constraint systems, documents). - Autoresearch โ claims that survive an adversary: sweep โฅ refute โฅ
judge, with absence never reported as novelty. - Eval gating for agent work โ a held-out, hash-derived exam for
AI-proposed changes before a human signs off. - Acceptance across organisations โ the registry pattern: pin a digest
manifest, let strangers take the prover's seat, human-gate the admission. - Content pipelines โ compress, translate, or re-express under a census
gate so no fact silently drops. - Attested results โ ฮบ-addressed artefacts and signed edges wherever a
result must travel and re-derive on someone else's machine. - Trust-graph construction โ the design horizon the whole system is
built toward: every sealed result is a ฮบ-addressed node, every signed
verification or delegation a VRC edge (a derived edge proposes; only a
signature mints), so a running harness accretes a verifiable trust graph
as a by-product of doing its work.GRAPH.mdpins the dialect;EVOLUTION.mdnames the path to registries of verifiable trust-community
credentials.
ADOPTION.md maps a new domain in five answers; WORKFLOW.md is the
operator's loop; the tier ladder (spar โ duel โ arena โ open world) carries
the same trusts from practice to production.
The pathway โ clone to your own harness
The short form: node tools/adventure.mjs prints your paths โ spar,
wizard, academic, federation, lattice, myth โ with your local state filled
in, and WORKFLOW.md is the operator's loop end to end, including the
BYO-interface contracts and the small-machine notes.
Requirements: Node โฅ 18, nothing else (zero npm dependencies). Running
rounds multi-agent needs a driver for the seven seats โ Claude Code's
Workflow tool is the reference runtime; engine/dual_agent_loop.mjs also
accepts any rt = { agent, parallel, pipeline, phase, log } you supply.
- Prove the axioms โ one command, every gate this repo has:
Algebra on all of Z/64Z, the engine's failure-semantics tests, conformancenode tools/check.mjs
of every instance it discovers, and a negative test that the blank
template fails for the right reasons. Non-zero exit prints re-runnable
commands. - Read the constitution โ
TRUSTS.md(the six trusts and where each one
bites), thenGROUND_RULES.md(GR-1..10, pasted into every seat at boot).
These are the parts you should not change. - Run the spar โ a practice bout, with real strikes, against
examples/field-guide/(compress a
field guide; a hash-drawn 8-question gate must stay 8/8). In Claude Code,
invoke the Workflow tool:
All writes land inscriptPath: examples/field-guide/harness.workflow.mjs args: { "repo": "<abs>/examples/field-guide", "root": "<abs of this clone>", "runId": "r4" }runs/<runId>/; the frontier does not move on its own. - Audit and view the round โ
node tools/render_run.mjs examples/field-guide r4
writes a staticrun.htmlthat re-derives every Gap seed from the saved
proposal bytes; or check by hand:sha256sum proposal_canon.jsonmust
equalseedHexingap.json.
Or watch the workshop live:node tools/console.mjsserves a
read-only console at127.0.0.1:4242โ the six-phase loop as evolving
geometry, every seed re-derived per poll, the frontier as a moving
ceiling, and a door panel that lists what software will never do for you. - Fold as keystone โ in the main session, per
seats/keystone.md:
conform green โ fold the validated-and-structural lever โ frontier first,
prose second โ file the chronicle โ conform green again. Where the claim
space is enumerable, run the census โ what you can count, count.
Publishing anything is yours alone (T6). - Seal what survived โ
node tools/mint_artefact.mjs examples/field-guide r4
writes a ฮบ-labelled artefact bundle (content-addressed by canonical-JSON
sha256 โ never trusted, only re-derived) with an evidence hash-manifest
and aDOOR.mdof the outward actions software did NOT take. It refuses
tampered seeds, red gates, and verdict-less runs. The console's door
panel lists every bundle; carrying one anywhere is the door, and the
door is yours. - Scaffold your own instance โ
node tools/new_instance.mjs ../my-harness my-harness,
fill every TODO in itsharness.config.mjs(the gate and the bundler both
refuse a config still wearing them), thennode tools/bundle.mjs my/harness.config.mjs my/harness.workflow.mjsand
run it exactly like the spar.SEAT_CONTRACT.mdis the interface, andADOPTION.mdis the map: why the duality works on any topic, the
harness-vs-auditor decision, the five answers that become your config, and
six worked domain mappings. Define the Gap first โ if you cannot say
how held-out witnesses derive from a proposal by hashing, you do not have
a harness yet (and may want an auditor instead). - When it earns it, specialise โ
SPECIALISATION.mdbinds personas and
spells to seats and seats instances on the Game-of-42 lattice;HARNESS_PATHS.mdshows fifteen real configs at every weight โ including
one seat that was held open by invitation until its acceptor signed, which
is also a thing a harness can be.
Follow the worked example. The spar's own history is the pathway walked
end to end: examples/field-guide/frontier.json records three audited folds
(730 โ 573 โ 526 โ 472, every step at gate 8/8, the last two closed by
exhaustive census), its chronicles/ tell each round verdict-first, and the
repo's chronicles/ tell the system's story โ including the eleven defects
found by running and the one round that was watched live. Reading them in
date order is the fastest way to learn what the discipline feels like in
practice.
The setup wizard, except what it installs is a mage
Squint at the pathway and you will recognise the shape: it is a setup
wizard. tools/new_instance.mjs scaffolds; the five answers ofADOPTION.md Step 1 are the wizard's dialog; the gate and the bundler refuse
a config still wearing its TODOs the way an installer refuses to proceed past
an empty field; and at the end something is installed and running. Lean into
that reading โ it is the intended one. The difference is what gets installed.
A wizard configures software. This one seats a mage: a standing
autoresearch loop โ Measure โ Propose โ Hold-apart โ Assay โ Critic โ
Chronicle โ that studies whatever initiative you point it at, proposes
improvements through blind lenses, and holds every proposal to a gate the
proposer cannot tune to. And the mage arrives already educated: it carries
the discipline the agentprivacy corpus paid to learn โ claim tiers, killed
levers filed at win-prominence, a census wherever counting is possible, a
chronicle per session, and a door no software opens. That is the reflection
mechanism, and it is deliberately one-way: the corpus itself never travels
(T3 โ strangers meet only in the axioms), but its method does, as a
constitution the conformance gate re-proves wherever it lands. Point the
wizard at a new open-source initiative and what you get is that initiative's
own research loop, wearing the same trusts.
This is not a hypothetical. The wizard has been run against real,
unaffiliated targets โ a live public quantum-resource benchmark, a
production ZK circuit suite, a controlled-grammar compiler, an upstream
household-sovereignty stack, a facilitated cohort on an air-gapped local
model, an agent-operated acceptance registry whose first external run came
from another organisation's machine, a fresh upstream substrate lane that
inherited the constitution whole, and the corpus's own literature under an
adversarial review bench. Those runs are the origin operator's work with
the harness, not the harness โ they are catalogued, clearly separated,
in HARNESS_PATHS.md, including the one domain where the wizard was the
wrong tool and an auditor was the right one, kept on purpose. Each began
exactly where step 7 leaves you: five answers, a Gap recipe, and a refused
config.
Why hold them apart
An agent that proposes a change and then checks its own change will pass. Not
because it cheats, but because the check it invents is the check its change
was built to survive. The output is a mirage: it passes the author's
probe and fails the real gate.
This harness makes mirages a named verdict instead of a surprise. Its
operational target is I(Y_S ; Y_M | X) = 0 โ given the target, what the
prover produces should tell you nothing about what the proposer produces.
Today that is a routing invariant enforced by prompt topology: a design
target, not a measured result, and tiered OPEN in claims_register.md until
per-seat process mounts enforce it (THREATS.md). What is closed by
construction is the draw: the proposer cannot grind it, because the seed folds
a secret it never sees.
The loop
Measure โโโถ Propose โโโถ Hold-apart โโโถ Assay โโโถ Critic โโโถ Chronicle
frontier soulbae ๐ง the Gap โฟป soulbis โ๏ธ classify draft
numbers N blind hash the full gate structural / the
only lenses proposal VALIDATED probe-limited record
in parallel โ witnesses MIRAGE / noise
BLOCKED
Propose โ Hold-apart โ Assay runs as a pipeline with no barrier: a fast
proposal reaches the gate while a slow one is still being written. The Critic
is the only barrier, because classifying the round needs the whole round.
After K consecutive dry rounds (nothing validated and structural), the
loop stops rather than grinding.
What the loop does not do is fold its own wins. It returns verdicts and
drafts. Folding into the ledgers is the keystone's job โ soulbae โฅ
soulbis operating as a pair in the main session, with the conformance gate
green before and after. And publishing anything is yours alone.
Why the corpus stays home
Two agents from different projects share no vocabulary. Different domains,
different ledgers, different lore. They cannot meet inside either corpus,
because each corpus is the private state of its own First Person.
They meet in the Gap โ and what is in the Gap is not content. It is the
axioms: the algebra, the six trusts, the ten ground rules, the seven
seats. Notice that this is already the file layout. engine/conform.mjs keeps
its own copy of the axioms and deliberately does not import them from the
engine; the source says that is the point. They belong to neither the loop nor
the config, and both are checked against them.
Origin(A) โฉ Origin(B) = {the axioms}
That is trust T3 โ the pair share only their root โ lifted one scale. The
universe expands the mind; the harness is the boundary that lets agents find
each other regardless of universe. Which is why universe/ in this repo is
one project's corpus, kept behind a seam, and deletable without breaking a
thing.
The trusts
The harness carries six constitutional trusts from the Privacy-is-Value model
(PVM V6): the four promises, the separation bound, the shared root,
consent-first invitation semantics, the multiplicative gate, and the door.TRUSTS.md states each one and โ more usefully โ maps each to the exact
place it bites: a required config field, a conformance check, a prompt
topology, or an honestly-labelled manual discipline. Read it before you
change anything. It is the part of this repo you should keep.
The ledgers
| file | what it is |
|---|---|
frontier.json |
the sole authority for numbers. Prose cites it; prose never restates it. |
claims_register.md |
every load-bearing claim, with a tier: PROVEN / DERIVED / REPORTED / OPEN / MYTH. |
notes/KILLED_LEVERS.md |
negative results, filed as prominently as wins. A killed lever is a fence, not a footnote. |
chronicles/ |
one file per session, verdict first, ending in a handoff block. A session without a chronicle is unfinished. |
SOURCES.md |
trace or delete: nothing is citable unless it resolves to a concrete path. |
Only the keystone writes the first three. Other seats return proposed
entries โ append-only ledgers do not survive concurrent writers.
The spar, in detail
A spar is a practice bout between the Swordsman and the Mage โ every
strike real, nothing at stake but the frontier. It is the first rung of the
runtime tiers, each a wider descriptive space for what a bout can be:
| tier | the bout | stakes | existing instance |
|---|---|---|---|
| the spar ๐คบ | practice, one workshop | its own frontier only | examples/field-guide/ |
| the duel | the formal bout โ reserved | a result a counterparty will rely on: signed verdicts, minted artefacts exchanged | the formal runtime to come |
| the arena ๐๏ธ | against a live external benchmark, other fighters on the board | a public leaderboard the workshop does not control | shor_mage vs ecdsa.fail |
| the open world ๐ | against a real upstream, in the wild | consequences beyond any ledger | privacy_pools_v2 vs the 0xbow suite; hearthold, its seat taken by the House of Archon on disclosure-debt |
The trusts do not change between tiers โ the door is the First Person's at
every rung. What changes is who is watching, and what a VALIDATED costs.
(Steps 1, 3, and 4 of the pathway, expanded.) Start with one command. It runs
every gate this repo has, in the order you should trust them:
node tools/check.mjs
It proves the algebra on all of Z/64Z, runs the engine's own tests (an outage
must never be reported as an exhausted search), conforms every instance it
discovers rather than the ones it remembers, asserts that the blank template
fails โ and fails for the right reasons โ and, if universe/ is present,
audits it exhaustively. Zero dependencies. If it exits non-zero, every line it
printed is a command you can re-run yourself.
The individual gates, if you want them:
node engine/conform.mjs # the axioms hold
node engine/conform.mjs examples/field-guide # the instance is coherent
node engine/loop.test.mjs # the loop fails loudly
Then run the example harness. It compresses a 730-word emergency field guide
while a comprehension gate, drawn from the original, must stay a full pass.
The original has 32 enumerable facts (F1..F32), so the gate is a census
โ every one is probed โ and the proposer's only winning strategy is to preserve
every fact, which is exactly the pressure you want on a compressor. (A sample
of 8/32 would miss a single omission 75% of the time; census is why this
instance refuses mode: 'sample'. See THREATS.md and D2 inHARDENING.md.)
In Claude Code, invoke the Workflow tool with:
scriptPath: examples/field-guide/harness.workflow.mjs
args: { "repo": "<abs path>/examples/field-guide", "root": "<abs path>", "runId": "r4" }
root is where GROUND_RULES.md, TRUSTS.md, and seats/ live โ this
repo's clone. Every seat boots from those documents (T3); an instance that
lives elsewhere and doesn't carry its own copies must always pass it.
All writes land in examples/field-guide/runs/r4/. Nothing touches the
frontier until you, as keystone, fold it. Seeexamples/field-guide/README.md for what a round looks like and how to run
the mirage drill.
View a run
node tools/render_run.mjs examples/field-guide r4 # one run
node tools/render_run.mjs examples/field-guide --all # every run
Writes runs/<runId>/run.html โ a single static page, no dependencies, no
network โ and writes nothing else. The page lays the round out as the
six-phase loop and, per proposal, re-derives the sha256 ofproposal_canon.json from the file bytes at render time and shows it next to
the seed the Gap recorded (salted runs re-derive sha256(hSourceโhProposalโ salt); pre-salt runs wear a legacy-seed badge). Match means the witnesses
re-derive from the proposal's own saved bytes; mismatch renders as a loud
warning, because witnesses of unknown origin validate nothing (GR-4). The same
audit, fail-closed and offline, is node tools/verify_run.mjs <instance> <runId> โ the page just refuses to skip it.
The page is a generated projection, never the record: the run directory is
the record, and a phase that left no artifact renders as "not reached / not
recorded" rather than being papered over (GR-5). Verdicts wear their color โ
VALIDATED green, MIRAGE amber, BLOCKED red โ and the frontier does not move
here any more than it moves in the loop.
Build your own harness path
(Steps 6 and 7 of the pathway, expanded.)
- Define the Gap first. If you cannot say how held-out witnesses derive
from a proposal by hashing, you do not have a harness yet. - Name the objective (what gets smaller), the gate (what must fully
pass), and the hard constraint (validity no score can override). - Scaffold it โ
node tools/new_instance.mjs ../my-harness my-harnessโ
then fill every TODO in itsharness.config.mjs. SeeSEAT_CONTRACT.md
for the interface and the complement-pair pattern for product objectives.
The gate and the bundler both refuse a config still wearing its TODOs,
and they refuse it in the same words: an unfilled harness runs, grades
nothing, and reportsVALIDATED. Measure your baseline before you claim a
best โ the template ships bothnullon purpose. - Bundle and run:
node tools/bundle.mjs my/harness.config.mjs my/harness.workflow.mjs - Read
HARNESS_PATHS.mdfor fifteen real instances โ quantum circuits, ZK
constraint systems, research papers, consent agreements, a publishing
loop, an acceptance registry, a lit-review adversary, and a descendant
lane that inherited the constitution whole โ the same skeleton under very
different bodies, grouped by how much of the loop each one actually runs.
The partial ones are labelled as partial; they teach the bar better than
the complete ones do. The catalogue is the origin fleet โ evidence of
range, not a dependency; your path is a config, not a membership. - When it earns it, dress the seats:
SPECIALISATION.mdcovers binding
personas and spells to seats, and the Game of 42 station pattern for
growing a fleet without drifting.
The lemma travels; the corpus stays home. Your harness path is a config, not
a fork.
Layout
RESEARCH.md the contribution โ the problem, the claim, the evidence, the limits
PRACTICES.md the 2024โ26 field surveyed against this harness โ holds / adopted / open
SOURCES.md trace or delete โ the external registry behind RESEARCH.md and PRACTICES.md
HOLONS.md ฮบ-addressed interoperability โ the holon contract, and why it is an auditor
TRUSTS.md the constitution โ read first
GROUND_RULES.md GR-1..GR-10, pasted into every seat at boot
ADOPTION.md why the duality is topic-free + the mapping procedure
SEAT_CONTRACT.md what a config provides
SPECIALISATION.md personas, spells, the Game of 42
HARNESS_PATHS.md the origin fleet โ fifteen instances of work done WITH the harness; evidence, not the system
WORKFLOW.md the operator's loop ยท choose-your-adventure ยท BYO-interface contracts
GRAPH.md the trust-graph dialect โ proposed vs minted edges, the lattice, credential alignment
EVOLUTION.md the plan โ data spine ยท observe lane ยท the registry pattern ยท contribute-or-clone
WIKI.md the Observe lane โ research auto-populating a federated wiki
AGENTS.md one session, one seat โ the boot protocol (tool-neutral)
CLAUDE.md the Claude Code pointer โ @AGENTS.md import plus runtime notes
SKILL.md this repo as a Claude Code skill
engine/ the loop, its gate, and loop.test.mjs
seats/ seven cards: the mandate of each seat
templates/ ledgers + a blank config to copy
tools/new_instance.mjs scaffold an instance, and say what is still missing
tools/bundle.mjs config + engine โ one self-contained workflow file
tools/render_run.mjs one run directory โ one static run.html projection
tools/console.mjs the workshop console โ a live, GET-only localhost window
tools/workshop.html the workshop's front page โ the contribution, one static instrument (served at /workshop)
tools/emit_feed.mjs the runtime feed โ a run's produced math (moving ceiling R(t) ยท โค/64โค lattice) for /star, game42, spellweb
tools/frontier.html the frontier widget โ an interactive, per-workshop descent, embeddable in the districts
tools/mint_artefact.mjs seal a validated run into a ฮบ-labelled artefact, at the door
tools/spellweb.mjs derive a knowledge graph (data only, GRAPH.md dialect) from any instance's ledgers
tools/wiki_emit.mjs project an instance's ledgers into FedWiki page JSON โ locally; publishing is the door
tools/wiki_install.mjs the Observe step: derive + project + install into a local farm host (WIKI.md)
tools/wiki_proxy.mjs Host-rewriting proxy โ pin one localhost port per site, for tailnet publishing
tools/star.mjs the lattice seating (star.v1) โ workshop vertex โฅ anchor, results at ฮบ mod 64
tools/adventure.mjs the front door โ choose your own adventure, with local state filled in
tools/make_default.mjs emit the DEFAULT distribution โ the system alone: no results, no chronicles, spar reset to baseline, self-checked green
tools/kappa.mjs the one ฮบ law โ content-addressing shared by every producer and verifier
tools/vrc.mjs relational edges โ ed25519 signed ฮบโฮบ, signer as did:key (a reference proposes, a signature mints)
tools/holon_audit.mjs the mesh auditor โ re-derive every ฮบ, re-hash every edge, verify every signature (HOLONS.md)
examples/ the runnable spar, and a servable sample wiki federation (wiki-farm/)
universe/ ONE PROJECT'S CORPUS โ delete it and nothing breaks
That last line is tested, not asserted. Clone the repo, rm -rf universe, andnode engine/conform.mjs, the spar's gate, and node engine/loop.test.mjs all
still pass.
Everything above universe/ is domain-neutral and always will be. That
directory is the agentprivacy layer: a fleet plan, a cast, an admission rite,
and a harness whose target is the corpus itself. It is kept behind a seam so a
stranger building a compression loop never has to read a word of it.
Apache-2.0. Origin: 0xagentprivacy ยท the Privacy-is-Value model (PVM V6) ยท
agentprivacy.ai
Assets
Navigation
the wiki is a projection; ~/dual-agent-harness (git) is the source of truth ยท (โ๏ธโฅโฟปโฅ๐ง)๐ = neg โ bnot โ succ