Skip to content

Reconcile PR38 with the salvage line: merged history, AGPL executed, sweettest unblock - #41

Open
kalisam wants to merge 164 commits into
mainfrom
reconcile/pr38-salvage-20260817
Open

Reconcile PR38 with the salvage line: merged history, AGPL executed, sweettest unblock#41
kalisam wants to merge 164 commits into
mainfrom
reconcile/pr38-salvage-20260817

Conversation

@kalisam

@kalisam kalisam commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Rejoins six weeks of parallel work and lands the corrections found along the way.
origin/main is fully contained in this branch (0 ahead, 124 behind as of
2026-08-24), so there is no content to reconcile here — only review.

PR #38 has since merged to main on its own (merge commit 873cc0c,
2026-08-18). It is an ancestor of both main and this branch, so the merge
described below is now history rather than the point of the PR. What remains
unique here is the salvage line.

Merge

pr/38 (65 commits) merged into the wip/salvage-20260804 line (93 commits).
Merge-base 3e82a33, 2026-07-05. 13 conflicts, each resolved on its merits.

Both sides won on different files, and one conflict caught a real bug:

  • PR38 won workers/commons-gateway/* — our manifest.ts used FLOSS/-prefixed
    paths, producing a doubled URL segment, and listed INDEX.md, which is not in this
    repo.
  • Both won on voters.py — PR38's _PERSONA_SHARED_GATE_SYSTEM hardening ("a
    persona may never waive or narrow the shared checklist") prefixed onto our
    clean-room persona text, on both personas.
  • Both won on provenance.py_is_ancestor and _ignored_chain_position are
    independent features, not rival versions.

Licence

  • ADR-7 executed. Accepted 2026-04-15, propagated to FLOSSI_U/ and the reuse
    ledger, but FLOSS/LICENSE still held GPL-3.0 text. Now the AGPL SPDX line,
    byte-identical to FLOSSI_U/LICENSE. GPL text archived, ADR-7 → Verified.
  • SUL-1.0 removed. MOMUS_PERSONA_SYSTEM was adapted from oh-my-opencode
    (SUL-1.0, incompatible with AGPL). Replaced by a clean-room
    EXECUTABILITY_REVIEWER_SYSTEM written from this repo's own consensus schema.

ADR-12

The stated unblock — "pin compatible client+tryorama versions for the 0.6 line" —
cannot be made to exist: no Tryorama version pairs with hc 0.6.1, and Tryorama is
retired upstream in favour of Rust sweettest. Verified holochain 0.6.3 ships
sweettest, so consent_gate.test.ts can be ported without a substrate
migration. Sequencing recorded deliberately: tests first, 0.7 migration second.

Also separates the two blockers the ADR had conflated — the sweettest port proves
the zome; the consent anchor is what blocks ADR-19. entry_has_consent() only
checks decision_action_hash is a non-empty string and never resolves it.

Provenance spec

v1.5 delta items D2 (multisig headroom) and D3 (evidence types file/log/
activity/source_chain) applied to spec, schema and EVIDENCE_TYPES. D1 held —
blocked on ADR-12.

ADR-20 — provenance validator reconciliation

Added 2026-08-23. The provenance spine has run since 2026-08-10 and landed zero
claims: hooks fire, packets are signed, and the validator rejects 100% of them.
Two causes, both reproduced at head, both documented and neither yet fixed —
ADR-20 is Proposed and carries no code.

  • D3 missed a fourth allow-list. _EVIDENCE_REF_TYPES in provenance.py:564
    still holds the original six values and is the set validate_packet actually
    enforces, so a schema-valid packet is rejected downstream. Second occurrence of
    this pattern after spec_gate.GATED_SURFACES.
  • Ancestor artifact validation is fatal on hash mismatch. Editing a file twice
    permanently invalidates every earlier packet naming it, and those stay in the p
    chain. The spec's Audit Disposition section defines a superseded status for
    exactly this; it was never built.

An adversarial ensemble audit (6 voters, 5 provider surfaces, 6 model families)
rewrote the proposed remedy and is recorded in the ADR, including the finding that
the synthesizer's "6/6 unanimous" label was wrong — one voter answered nothing, one
dissented on blast radius, and the sequencing question split three ways.

Verification

  • 262 tests pass (this line alone before the merge: 162; pr/38 alone: 223)
  • spec_gate --check: 108 registered, 0 missing, 0 reuse violations, 7 stale
    (non-fatal; 5 are pre-existing FLOSS/scripts/hook_*.py path drift — those files
    live in FLOSS/hooks/)
  • No conflict markers; no SUL-1.0-derived text remains
  • origin/main merged in 2026-08-24 (CI and dependabot fixes only, clean)

Not included

The preservation spine is not in this branch. It ships via #43
(feat/preservation-spine-standalone) with #59 stacked on top; that line is ahead
of the local spine work, which has been abandoned.

kalisam and others added 30 commits July 6, 2026 18:41
…s read gateway

Source-only (inner .gitignore keeps node_modules/dist/.wrangler out): src (index,
manifest, responses, security) + tests + wrangler.jsonc + tsconfig. The read-only
public edge that fronts the commons; the local gateway/Holochain remains the truth
substrate (ADR-16 boundary). npm scripts already wired in root package.json.

Verified: tsc --noEmit clean; 7/7 vitest tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… projections

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Digests the externally produced "Holo-RBI Recursive Event Swarm 2.0"
chat-paste intake against the layer stack. Reaffirms ADR-10
router-not-controller (coordinator may only propose; assignments must be
Claims subject to Votes, gated on a future ADR); logs four deltas incl.
valid_context/known_failures/expiry_or_retest_date fields for the ADR-17
KnowledgeTriple review. Trading/RBI vertical out of scope. No adoption.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root cause of the 2026-06-16/17 1.23M-event intake storm: default_watch_specs()
registers overlapping specs (canon→FLOSS/docs AND shared-surface→FLOSS/),
so every FLOSS/docs file was fingerprinted twice per scan with different
watch_domain values; state oscillated and emitted a spurious 'modified'
event per overlapped file per scan, indefinitely.

- scan_once: first-spec-wins dedup on abs_path (kills the oscillation)
- MAX_INCOMING_QUEUE_DEPTH=5000 backpressure guard (early-exit scandir count;
  suppresses emission + prints drain hint when queue is flooded)
- tests/test_watch_intake.py: 4 pytest tests covering dedup, real-modify
  signal preservation, and backpressure (4/4 green, isolated tmp workspaces)

Docs reconciled same session:
- OPERATOR_PRIMER: heartbeat row (resumed 2026-05-26, user-session process,
  LocalSystem prohibition re-verified 2026-07-07); STOP/LocalSystem/double-loop
  rules in What-Not-To-Do
- CLAUDE.md Inference Posture: Cowork 2x window ended; Fable-5 durable access
  via Pioneer.ai subscription; pullback note superseded
- working-todo A.00000: full repair record + canonical heartbeat invocation
Hermes Agent is now a first-class skill-projection target alongside
codex/claude/gemini/opencode. install_path: Hermes skills dir.
All 9 corpus skills materialized 2026-07-07 (--check OK).

Also added .hermes.md project-context at workspace root (auto-loads
FLOSSI0ULLK rules into every Hermes session in C:\~shit\).
Approved Agent C patch from 2026-07-07-doc-drift-sweep.md Part 1.2.
ticks_today was incremented at L711 but only saved at L757 (end of tick),
so a SIGKILL/OOM/crash mid-tick lost the count — and a frozen daily_state
date was an observed symptom (date stuck 2026-06-14 while ticks continued).

Adds save_daily_state() at two points:
- immediately after the ticks_today increment (L711) — covers any hard kill
- after each rounds_today accumulation in the work loop — preserves partial
  round-cap accounting if a later item crashes

End-of-tick save at L757 remains as the final authoritative write.
save_daily_state() is a ~200-byte JSON write already wrapped in try/except;
2+N extra writes per tick is negligible I/O.

Verified: test_heartbeat_budget.py 7/7 green; targeted ad-hoc test confirms
ticks_today=1 persists through a simulated mid-tick crash (old behavior lost
it). Date-rollover reset confirmed correct (both rounds_today + ticks_today
reset to 0 on date change).
Per 2026-07-07-doc-drift-sweep.md (Agent C report), approved by Anthony 2026-07-08.

OPERATOR_PRIMER.md (0.1.0 -> 0.2.0, updated 2026-05-19 -> 2026-07-08):
- Phase 0 row: 'Tryorama' -> 'Rust Sweettest' (directive 2026-07-03)
- Orchestration bridge row: note ADR-17 as active Phase 1 focus
- Consent gate row: Tryorama -> Sweettest
- Decision history: ADR-Suite v2.0 -> INDEX v2.1.0 (supersedes for ADR-13..17)
- Current Best Next Moves: refresh from 2026-05-19 set to 2026-07-08 set
  (ADR-17, ADR-12 action-time, spec_gate adoption, Sweettest migration,
  synthesis budget decision)

RUNTIME_SURFACES.md (0.1.0 -> 0.2.0, updated 2026-05-24 -> 2026-07-08):
- Add 4 missing Runtime Inventory rows: watch_intake (with 2026-07-07
  overlap-dedup + backpressure guard note), process_intake_events,
  autonomous_synthesis, spec_gate

No remaining 'Tryorama suite unvalidated' / 'current focus is Phase 0'
contradictions across the 4 orientation files (grep-verified).
- autonomous_synthesis timeout 900s → 1800s (pathologically large files
  like Holistic_Vision.md = 932KB → ~78 chunks need more than 900s)
- test_heartbeat_persist.py: 3 new pytest tests for the 2026-07-08
  save_daily_state mid-tick persistence patch (test_mid_tick_crash_persists
  _tick_count, test_date_rollover_resets_both_counters, test_rounds_accum
  ulation_persists_per_item). 10/10 green with existing budget suite.

Also: 2 ACCEPT drafts committed to source chain (foss-ai-personal-
assistant-landscape 69febf3, HI_ROI_NAO bb231a2); 14 root intake files
relocated to intake_raw/2026-07-07-root/ (sha256 verified, move log at
.agent-surface/intake/root-intake-moves-2026-07-07.json).
… + drift reports

- 2026-07-07-root-intake-digestion.md: status flipped from read-only to
  'Relocation completed 2026-07-08 (14 files moved, sha256 verified)'
- TRIAGE-2026-07-07.md: Agent A triage of 17 synthesis staging drafts
  (2 ACCEPT / 2 REVISE / 8 REJECT / 2 MERGE / 3 DEFER)
- 2026-07-07-doc-drift-sweep.md: Agent C drift sweep + heartbeat patch proposal
- APPEND_ONLY_KNOWLEDGE_LOG.md: regenerated after 2 ACCEPT draft commits
  (foss-ai-personal-assistant-landscape 69febf3, HI_ROI_NAO bb231a2)
- 14 root intake files relocated to intake_raw/2026-07-07-root/{reports,reference}/
  (sha256 verified; move log .agent-surface/intake/root-intake-moves-2026-07-07.json)
9 reports + 5 reference PDFs moved from workspace root with sha256
pre/post verification. Covers Lovable synthesis cluster, agent-coordination
cluster, memory-infra survey, and external research PDFs.
Move log: .agent-surface/intake/root-intake-moves-2026-07-07.json
Add MAX_CHUNKS_PER_FILE=20 to autonomous_synthesis_loop.py. Files exceeding
the cap (e.g. Holistic_Vision.md=932KB→78 chunks) are skipped with a logged
deferral reason, not processed. This prevents pathologically large files from
monopolizing the heartbeat tick budget.

Consensus claim 019f4a9f APPROVED mean +0.617 variance 0.0006 (3/3 voters:
groq-gpt-oss-20b +0.6, groq-qwen3-32b +0.6, cerebras-gpt-oss-120b +0.65).
First claim 019f4a9e was REJECTED (-0.43) for missing evidence — resubmitted
with spec/commit/ADR/test provenance and approved.

Option E (FLOSS_HEARTBEAT_DISABLE_SYNTHESIS=1) remains Anthony's call — the
cap alone bounds the per-file cost; disabling stops automatic spend entirely
while 15 staged drafts await triage.

14/14 tests green (budget + persist + watch_intake suites).
Two models in voter_registry.json were dead (verified against live providers):
- mistral/devstral-small-2507 -> "Invalid model" (used in 6 profiles).
  Replaced with mistral/devstral-small-latest so future version bumps don't
  re-break it.
- openai/gpt-4.1-mini -> OpenAI quota exhausted (no billing). Replaced with
  openrouter/openai/gpt-4o-mini (OpenRouter key present).

Updated the three gateway tests that pinned the old IDs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ensemble was Ollama-only and reliably degraded (0-of-4 embeddings) under
GPU serialization on 16GB hardware, then gave up in the degraded branch.

Add transport.py: online-primary voter pool resolved from the consensus-gateway
roster (reusing resolve_default_voter_specs + Flowith helpers), transport
routing (litellm/flowith/ollama), and a single-embedder-per-run resolver (local
mxbai preferred, cloud fallback) so the ensemble runs with Ollama fully down.

synthesizer.py resolves the pool from FLOSS_ENSEMBLE_VOTER_MODE (default
online), threads the resolved embedder through dispatch, and emits honest
per-provider labels. 7 new unit tests; live deliberation reaches tier1 with 6
cross-provider voters where the local path degraded. Spec + .env.example updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two non-canon intake/design notes:
- Levin-corpus K-metric intake: defers ADR on the chat-synthesis proposal to
  adopt K=log10(tau_blind/tau_agent) as primary benchmark; figures unverified;
  records the ensemble-unavailable + consensus-REJECTED adjudication trail.
- Hermes MCP cross-harness design sketch: grounded in the verified .hermes.md
  (Hermes is an active harness consuming project MCP servers today); separates
  safe Shape B (premium voter lane) from ADR-gated Shape A (CLI dispatch).

No adoption; no wiring; not canon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- packages/mcp_daemon.py: claim_singleton(), audit_appender(), run_http_daemon()
- Windows-safe _pid_alive() (WinError 87 on impossible PIDs)
- 4/4 tests passing, 43/43 existing tests still green
- Purely additive: no existing files modified
- Part of omniroute-inference-plane-migration Stage 1 Task 1.1
- server.py: run_http_daemon(consensus.pid, :7331) replaces bare mcp.run()
- mcp_server.py: run_http_daemon(reasoning_ensemble.pid, :7332) replaces bare mcp.run()
- Both carry lens instruction injection via FastMCP(instructions=...)
- Windows-native _pid_alive() via ctypes.OpenProcess (os.kill unreliable)
- Verified: both daemons start, respond to initialize + tools/list, singleton guard works
- Part of omniroute-inference-plane-migration Stage 1 Task 1.2
Idempotent PowerShell launcher for consensus (:7331) + ensemble (:7332).
Register: schtasks /Create /TN FLOSS-MCP-Daemons /TR ... /SC ONLOGON /RU MSI\kalis
Part of omniroute-inference-plane-migration Stage 1 Task 1.4
- packages/omniroute_client.py: completion() + embedding() via httpx
- 3/3 tests passing (monkeypatched, no live daemon needed)
- Part of omniroute-inference-plane-migration Stage 3 Task 3.3
Groq decommissioned qwen3-32b (verified via live API: only qwen/qwen3.6-27b
remains). Updated all 8 profile entries + 3 test files + docstring.
27/27 tests passing.
…OSS_MODEL_BACKEND)

All 8 litellm call sites now branch on FLOSS_MODEL_BACKEND=litellm|omniroute:
- voters.py: 3 voter closures (make_litellm_voter, make_omo_momus_voter, make_omo_critic_voter) via shared _model_completion()
- transport.py: _litellm_generate + _cloud_embed_fn
- autonomous_synthesis_loop.py: chunk extraction
- major_consolidation_sweep.py: chunk extraction

Default remains litellm. OmniRoute is opt-in via env var.
All parsing logic (_strip_thinking, _parse_weight, _parse_rationale) unchanged.
70/70 tests passing.
System blast radius. Operator-consented 2026-07-17.
Stages 0-3.4 implemented + 70/70 tests passing.
Consensus claim submission pending.
…OmniRoute blocked on provider config

Litellm baseline: 3/3 voters parseable, consensus gate LIVE.
OmniRoute: daemon running but needs provider config via dashboard + model-ID mapping.
FLOSS_MODEL_BACKEND stays litellm (correct per plan Task 3.2 gate).
…se fix

- OmniRoute 3.8.48 with 1249 models across 23 providers configured
- All 3 voter models resolve with identical litellm-style IDs (no mapping needed)
- Consensus round via OmniRoute: 3/3 voters parseable, identical weights vs litellm
- Token compression active: articlesx1 strategy, ~21 tokens/request (~4%)
- Fix: added stream:false to omniroute_client.py (OmniRoute defaults to SSE)
- 3/3 TDD tests still passing
- start_mcp_daemons.ps1: now starts OmniRoute (:20128) alongside FLOSS daemons
- stop_mcp_daemons.ps1: clean shutdown of all daemons + orphan cleanup
- Fixes the 'million processes never killed' problem
Design for repairing materialize_shared_agent_surface.py (crashing on
HTTP-transport servers since 01bdeb8, which is why every harness config
drifted and needed manual repair), adding Codex + Hermes targets, and
adding a single runner over all six materializers.

Also lands the execution notes for the 2026-07-24 MCP migration fixup:
8 fixes applied/verified, root causes recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
11 TDD tasks: extract classify_transport, fix the OpenCode HTTP crash,
add Codex (tomlkit) + Hermes (ruamel) merge-preserving writers with a
gateway liveness guard, user-scope gating, and refresh_agent_surfaces.py.

Task 9 is the acceptance gate: the propagator must reproduce the
2026-07-24 verified harness configs, not revert them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unblocks materialize_shared_agent_surface.py, which has crashed on every
run since 01bdeb8 moved consensus/ensemble to HTTP daemon URLs.

Also folds in code-review follow-ups from Task 1 (classify_transport):
document the both-transports precedence (stdio wins, url ignored) and
add coverage tests for its non-dict/blank-command/non-string-env/
non-string-headers branches.
Four in the spec gate, two in the ensemble. All five verified against the code
before fixing; all five real.

SPEC GATE

1. A malformed tier exempted the entry instead of failing it. `if tier not in
   (1, 2): return [], []` treated a hand-edited "2" or 3 exactly like an absent
   tier, so a typo in the field that decides whether the fail-closed gate
   applies opened the gate. Absent tier is still the grandfather exemption --
   100 of the 104 registry entries predate ADR-18 and carry no tier -- but a
   PRESENT tier outside integer 1/2 is now a violation.

2. `entry.get("emergency")` was a truthiness test, so the string "false" (the
   most likely way a human writes it wrong) read as an active emergency and
   downgraded a wholly missing reuse block to a warning that --check exits 0 on.
   Only the boolean True waives the gate; any non-boolean is a violation.

3. Future dates were accepted for reviewer.date and probe.date, so a tier-2
   entry could claim a review or a passing probe concluded in 2099. search_date
   already rejected the future and its two siblings did not -- the fourth
   instance of one structure fixed at one reader and not the others, which is
   the failure mode _iso_date_problems() exists to prevent. The rule now lives
   in the helper and every caller inherits it. search_date opts out with
   allow_future=True solely to keep its more specific freshness message, and
   still rejects the future at its own call site.

ENSEMBLE

4. A failed generation was attributed to the wrong provider. voter_transport
   was resolved inside the try, and the except branch built its VoterResponse
   without it, so the field fell back to its "litellm" default: every failed
   ollama or flowith call was recorded as a litellm failure in the staged
   artifact and in _log_synthesis_action() -- exactly the data a provider
   failure-rate audit reads. Resolved before the try and passed through.

5. The cloud embedder never received the run's budget. resolve_embedder()'s
   docstring already promised the resolved embedder does real work "under the
   normal timeout", but the cloud wrapper passed no timeout, so
   omniroute_client.embedding() applied its own 60s default against the
   ensemble's 90s budget. An embedding finishing between 60s and 90s was
   recorded as failed and its voter dropped, which can push a run inside its
   configured budget to DEGRADED. Threaded through both branches.

Found while writing the tests: one of them made a live HTTP request during the
suite, because FLOSS_MODEL_BACKEND was read from the ambient environment. Both
branches now pin it explicitly.

19 new tests. The tier and emergency cases are parametrized over the malformed
values rather than the one value reported, and the date rule is asserted on the
helper so a fifth date field inherits it. 718 pass on the CI green set;
spec_gate --check stays green at 104 registered, 0 violations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9eb1f31557

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/activity_log/provenance.py Outdated
Comment thread packages/mcp_daemon.py Outdated
Comment thread docs/specs/provenance-packet.spec.md Outdated
All three verified against the code before fixing. All three real, and the
first two are the same failure mode this repository keeps producing.

1. A root-level decoy won the first hop of the chain walk. The loop's later
   hops select a candidate that VALIDATES out of chain_index, but the initial
   cursor called _find_packet_by_digest, which probes
   `provenance_root/<digest>.json` before any glob and returns it
   deterministically. A decoy dropped there under the parent's digest therefore
   won every time, and an otherwise valid child inherited the decoy's errors.

   This is the third time in this module that one reader of a structure was
   fixed and its sibling was not -- and the second time in the same walk: the
   sequence index was taught to keep every occupant, then chain_index, and now
   the entry point into both. The earlier decoy regression test even documents
   the gap, aiming itself at genesis because the first hop was known not to go
   through chain_index. So the fix is the shared `_cursor_for()` helper, not a
   third copy of the selection logic. The old test now points at the new one.

   New test aims at the immediate prior -- the hop the earlier test had to
   avoid -- and was confirmed to fail against the previous code
   (E_PROVENANCE_SIGNATURE_INVALID) before the fix landed.

2. A reused PID blocked the daemon slot forever. A PID alone does not identify
   a process: after a crash or reboot the PID file survives and the OS
   reassigns the number, so _pid_alive() reported the daemon as running while
   its port went unserved, the launcher exited successfully, and the stop
   script was aimed at an innocent process.

   claim_singleton now records the holder's process-creation time and compares
   it on the next claim -- GetProcessTimes on Windows, /proc/<pid>/stat field
   22 on Linux, no new dependency. It is stored in a sidecar rather than by
   changing the PID-file format, because scripts/start_mcp_daemons.ps1 and
   existing callers read that file as a bare integer.

   Deliberately asymmetric: the slot is released only when the holder is
   PROVABLY a different process. A legacy file with no sidecar, an unsupported
   platform, or an unopenable process all keep the old blocking behaviour --
   a false "stale" verdict starts a second daemon on a bound port, while a
   false "live" verdict is a file a human can delete.

3. docs/specs/provenance-packet.spec.md contained a literal 0x00 byte. It was
   written while documenting CESR's mid-padding, where the example needed the
   text \x00 and got the byte, and it made file(1) classify the canonical
   provenance specification as `data`. ripgrep reports a binary match instead
   of matching lines; Markdown and indexing pipelines are entitled to truncate
   or reject it. The spec was unsearchable by the tools an auditor reaches for.

   Fixed, plus a generic guard: no tracked text-suffixed file may contain a NUL
   byte. Written over `git ls-files` rather than pinned to the one file that
   broke, and it asserts the file list is non-empty so it cannot pass vacuously.
   It was the only tracked text file affected; the other 60-odd hits are PDFs,
   zips and images.

11 new tests. 727 pass on the CI green set; spec_gate --check green at 104
registered, 0 violations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The re-tally of all six stored syntheses found two failures the similarity-floor
guard does not touch, both confirmed independently against the corpus here.

1. Two of six voters were non-functional across the entire 2026-08-24 campaign
   and were counted as converged voters in every run. groq-qwen3-27b emitted a
   bare <think> restatement of the prompt in 5 of 5 runs; groq-gpt-oss-120b was
   truncated mid-sentence in 5 of 5, at 212 / 359 / 727 / 1290 / 1466 chars
   against 2000-3200 for peers. The "at least 3 provider surfaces and 4 model
   families" diversity policy was therefore satisfied on paper by voters that
   produced no positions.

   Length alone does not catch this: the restating voter was the SECOND-LONGEST
   response in one run. degenerate_voters() is shape-based instead -- an
   unclosed reasoning block, or an unfinished sentence below the median length.
   It flags rather than excludes, because a wrong exclusion loses a real vote
   while a wrong flag costs a line of output. Run against the stored corpus it
   reproduces exactly the two voters and exactly the five runs the re-tally
   named, with matching character counts.

2. Timed-out voters vanished from the denominator. write_synthesis printed
   len(responses) after synthesize() had already filtered to voters that
   produced an embedding, so a file could read "Voters: 5" while its own
   voter_count field said 6, and no line anywhere said a voter had been lost.
   The dispatched set is now passed through and the missing voters are named
   with their errors.

Neither of these is the whole repair. The re-tally's larger finding -- that four
of six runs concealed a substantive position-level split, including one where
two of five answering voters returned the opposite verdict token from the one
printed as unanimous -- is a property of clustering whole responses at all, and
needs claim-level extraction rather than a better filter. This commit stops two
specific ways a non-answer was being counted as agreement.

Also unfixed here, deliberately: the six stored drafts predate every guard and
are not re-rendered, and the ensemble directory is gitignored while
activity.jsonl records 16 synthesis events against 6 surviving drafts.

11 new tests. 734 pass on the CI green set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 75d181295b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1763 to +1765
hermes_path = resolve_manifest_path(
workspace_root, str(hermes_cfg["config_path"])
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip platform-specific Hermes targets before path resolution

On POSIX, the production shared-agent-surface.json declares hermes_user.config_path as %LOCALAPPDATA%/hermes/config.yaml, and os.path.expandvars() leaves that Windows reference unchanged. This call therefore raises SharedSurfaceError before the later user-scope skip runs, so a normal repo-scope materialize_shared_agent_surface.py or unified refresh fails on Linux/macOS even though the Hermes user target was not requested; skip unsupported out-of-scope targets before resolving their paths, or tolerate the unresolved path for this read-only guard.

Useful? React with 👍 / 👎.

if os.environ.get("FLOSS_MODEL_BACKEND", "litellm") == "omniroute":
from packages.omniroute_client import completion as _omni

return _omni(model, [{"role": "user", "content": prompt}], max_tokens=600, temperature=0.4)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward the voter timeout to OmniRoute

When FLOSS_MODEL_BACKEND=omniroute, this branch ignores the supplied voter timeout and falls back to omniroute_client.completion()'s 60-second default, whereas the ensemble explicitly budgets 180 seconds per voter. An OmniRoute generation completing between 60 and 180 seconds is therefore recorded as a failed voter and can make the synthesis degraded despite staying within the configured voter budget; pass timeout=timeout as the LiteLLM branch already does.

Useful? React with 👍 / 👎.

# the file.
$stopped = $false
try {
Stop-Process -Id $daemonPid -Force -ErrorAction Stop

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify daemon identity before forcing termination

Fresh evidence after the PID-identity fix is that the new .identity sidecar is checked only by claim_singleton(); this stop path never reads it. After a daemon crash or reboot, if the stale PID has been reassigned, Stop-Process -Force terminates the unrelated process and then deletes the PID file, so the stop command still has the user-impacting half of the previously reported PID-reuse defect; compare the recorded creation token before stopping anything.

Useful? React with 👍 / 👎.

Comment thread packages/mcp_daemon.py
# rather than overwriting, so a concurrent launcher that wins the
# retry still blocks us instead of both proceeding.
try:
pid_path.unlink(missing_ok=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear stale identity sidecars before reopening the PID slot

When a stale PID file has an identity sidecar and two launchers race, this branch removes only the PID file. The first launcher can create and write its new PID while the old identity token is still present; the second launcher then sees a live PID paired with the stale token, classifies the first launcher's valid claim as stale, unlinks it, and claims the slot too, allowing both processes to return success and attempt the same port bind. Remove the old identity before making the PID pathname available again, or update the PID/token pair atomically.

Useful? React with 👍 / 👎.

kalisam and others added 2 commits August 26, 2026 04:28
…R-20's Q1 tally

TWO PIECES.

--- 1. The anchor ---

Four external review layers rated wholesale head truncation Critical. Fourteen
commits of validator hardening did not move it, because no amount of validation
inside a store can attest to what the store no longer contains.

Sequence gaps make INTERIOR deletion undeniable and nothing else. Head
truncation leaves no gap -- the chain just ends sooner. Deleting a single-packet
identity leaves nothing to gap against. Measured on the live store: 96 of 99
identities are single-packet chains, so the existing mechanism covers about 3%
of identities and zero head truncations.

packages/activity_log/anchor.py commits to the global SET of packets and to each
identity's HEAD. Leaf preimage is JCS over {d, i, s} -- binding identity and
sequence, not just the digest, so an inclusion proof proves chain POSITION,
which is the property truncation attacks. RFC 6962-shaped BLAKE3 tree with
domain-separated leaf/interior tags and odd-node promotion. Leaves sorted, so
the root is a set commitment independent of filesystem enumeration order. BLAKE3
and jcs are already hard dependencies; this adds no package.

The anchor is Ed25519-signed with the existing identity, and the anchor series
is itself a hash chain via prev_root, so dropping an old anchor breaks linkage
visibly. Known damage -- the four holes on DkuY at 3/36/37/39, the duplicate
slots on DVpMe -- is recorded IN the anchor, which freezes it as pre-existing so
later damage cannot be laundered as old.

Verified against a copy of the live 253-packet store:

  unchanged                          VERIFIED
  head truncation (5 packets)        TRUNCATION_DETECTED, 1 head regression
  single-packet identity wiped       TRUNCATION_DETECTED, 1 vanished identity
  growth only                        ANCHOR_STALE, delta +1
  interior deletion                  ANCHOR_MISMATCH
  head removed + packet added        TRUNCATION_DETECTED, delta 0
  packet moved to store root         VERIFIED (rglob, not fixed depth)
  anchor field edited                ANCHOR_UNAVAILABLE
  wrong pinned signer                ANCHOR_UNAVAILABLE

The STALE/TRUNCATION split is what makes it usable as a routine gate: honest
growth must not cry wolf, loss must be loud. ANCHOR_STALE is deliberately
neither a pass nor a failure. ANCHOR_UNAVAILABLE emits "NOT a pass. Store is
unverifiable, not verified."

Three deviations from the prototype, each a real hole: rglob instead of a fixed
*/*.json glob, without which a packet MOVED to the root silently leaves the leaf
set and shrinks the commitment with nothing deleted; Ed25519 signing, without
which anyone who can write the repository can write the commitment meant to
constrain them; and a separate ANCHOR_MISMATCH status so an interior change is
not reported as a truncation the tool cannot demonstrate.

scripts/provenance_anchor.py prints the publish commands and runs none of them.
The root goes in the tag NAME and the commit MESSAGE, not only in file contents,
because that is the one place a fact outside the operator's control gets
created: a public push emits events carrying ref names and message text to
mirrors with no write path back. Pushing is an operator decision.

The spec states the limits in full, including that it does not stop the
operator -- it converts undetectable deletion into detectable equivocation, and
only for a party who retained a prior root -- and that third-party mirror
retention is Specified, not Verified, until one anchor is published and found
externally.

ADR-18: registered tier 2 with emergency set, so spec_gate emits
"retrospective audit required" rather than passing silently. The prior-art
survey was conducted by a single model family and does not meet the >=3
surfaces / >=4 families bar. The reuse review is owed, not done.

--- 2. ADR-20's Q1 tally was wrong ---

Operator-authorized. A hand re-tally that reads a synthesis for verdict tokens
reproduces the same failure at one remove, and this ADR did exactly that.

It recorded "4 System, 1 Substrate, 1 no-answer" with gpt-oss-120b as "the lone
dissenter". The raw responses say otherwise: mistral/devstral-small opens
"**1. BLAST RADIUS: Substrate (0.85, override forbidden).**" and was filed on
the System side; nvidia/nemotron labelled itself System while its rationale
argues Substrate; openrouter/gpt-4o-mini never uses the vocabulary at all; and
groq/qwen3-27b did not answer -- a bare unclosed <think> restatement counted as
a converged voter.

Honest tally: 1 explicit System, 1 explicit Substrate, four unclassifiable.
There was never a 4-1 majority to overrule.

The correction is appended rather than substituted, because the original figure
was cited and a silent edit leaves citations pointing at something that never
existed. The Substrate reclassification is unaffected and better supported than
when it was written.

34 new tests. 759 pass on the CI green set; spec_gate --check green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…_cZVCDGTUlYi0 [packets=257 identities=99 prev=genesis]

Genesis anchor. First commitment to the global packet set and to each identity's
head, so that from this point wholesale head truncation is detectable by anyone
holding this root.

Verified at write time: VERIFIED, 257 packets, 99 identities, 0 unreadable.

The tag carrying this root is not created here. Pushing a tag to a public
repository is an operator action, and the spec says so; the command is printed by
scripts/provenance_anchor.py publish --print-tag. This commit message carries the
root, so pushing this branch already places it in the public event record.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbaae97b74

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +395 to +399
elif len(leaves) > (anchor.get("packet_count") or 0):
# Growth only, nothing anchored has gone. The common case between runs.
# Distinct from VERIFIED on purpose -- a stale anchor is not a passing
# anchor -- and distinct from TRUNCATION so it does not cry wolf.
result["status"] = ANCHOR_STALE

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Detect anchored loss even when the store also grows

When an anchored interior packet is deleted and two or more new packets are added, none of the head-specific checks fire and this count comparison labels the result ANCHOR_STALE. The current packet set is not a strict superset—the documented meaning of that status—and routine handling can therefore treat actual anchored loss as ordinary growth instead of the ANCHOR_MISMATCH alarm; determine whether every anchored leaf remains present rather than inferring that from the aggregate count.

Useful? React with 👍 / 👎.

Comment on lines +87 to +90
previous = anchor_lib.load_anchor(args.anchor)
built = anchor_lib.build_anchor(args.provenance_root, previous)
identity = load_or_create_identity(args.identity_dir)
signed = anchor_lib.sign_anchor(built, identity)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refuse to publish over a detected loss

When any anchored packet has disappeared, invoking the normal publish command immediately builds and writes a replacement anchor without first checking the store against previous. The next verify then reports VERIFIED, laundering an accidental truncation or interior deletion into the new baseline; verify the previous signed anchor first and refuse publication on truncation, mismatch, or an unavailable predecessor.

Useful? React with 👍 / 👎.

Comment on lines +129 to +132
stored = anchor_lib.load_anchor(args.anchor)
result = anchor_lib.verify_anchor(
args.provenance_root, stored, expected_signer=args.expect_signer
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify the anchor's predecessor chain

When the latest anchor and store agree but an older anchor was dropped or prev_root points to an unknown root, this command still returns VERIFIED because it loads only the current file and never walks or checks the predecessor series. This contradicts the specified verification step and makes the claimed hash-chain protection ineffective; accept retained predecessor anchors or repository history and verify linkage back to a trusted root.

Useful? React with 👍 / 👎.

…ate never asked for

The ensemble had a measured defect and the response was to design a replacement
from scratch -- per-question ballots, admission gate, dissent-preserving tally --
in a repository whose ADR-18 gate exists specifically to stop that, in the same
session that gate was hardened four times.

This is the search that should have come first. It changes the design, and it
contradicts two beliefs this repository operates on.

FINDING 1 -- model-family diversity does not buy independence.

The voter policy ("at least 3 provider surfaces and 4 model families; same-family
endpoints don't count as independence") rests on the premise that different
families make different mistakes. Kohli, arXiv:2605.29800, measured it on 9
frontier judges from 7 families with 100 human annotations per item:

  - n_eff = 2.18. Nine judges carry about two independent votes.
  - The best single judge matches or beats the full panel on every dataset.
  - Same-family correlation is barely above cross-family: 0.437 vs 0.389. The
    three most-correlated pairs in the panel are all CROSS-family.
  - One judge per family made it WORSE: n_eff falls to 1.93. The paper's words:
    "Family diversity alone does not recover independence."
  - Unanimous items still carry a 9.1% error rate, against ~0.02% predicted
    under independence.
  - Dawid-Skene and accuracy-weighted voting close at most 11% of the gap even
    with oracle access to gold labels. The bottleneck is the inputs.

FINDING 2 -- free-form prose is the worst case, and pairwise correlation cannot
see it.

Chen, arXiv:2606.27288, across 67 models from 21 providers: accuracy is capped
at 1 - beta, where beta is the rate at which every model is wrong on the same
query. Mean pairwise correlation provably cannot identify beta. Re-asking the
same questions in free-response rather than multiple-choice reopens the tail --
beta 0.127 -- locating co-failure in answer FORMAT rather than subject. "On
checkable tasks, combining models rarely beats the single best model without a
strong query-level routing signal."

Every poll here is free-response prose on unresolvable architecture questions.

WHAT IT CHANGES

Keep: structured per-question elicitation. It is the right direction and is what
makes Universal Self-Consistency, Optimal Weight / Inverse Surprisingly Popular,
and surprisingly-popular voting applicable at all -- but it is adopted now, not
invented.

Drop: the hand-rolled agreement metric. separation_diagnostics() detects a real
defect but is a metric nobody else reports. The field's diagnostic is Kish n_eff
(k / (1 + (k-1) * phi_bar), threshold n_eff/k < 0.5), and the honest reading is
that even that is insufficient -- beta bounds the achievable gain.

Add: a diagnostic that can conclude DO NOT RUN A PANEL. Both papers point the
same way and nothing in the current design can ever return "one strong model
would be better here."

Add: surprisingly-popular elicitation -- one extra field, "what do you predict
the others will say" -- which recovers a correct minority. Not hypothetical
here: ADR-20's blast radius was decided against a minority that was right, and
the correction had to be made twice.

Survey limits stated in the document: search not systematic review, abstract-level
for most entries, and transfer unproven because both headline papers measure
classification tasks with gold labels while this repository has neither.

CF-7 records the process failure: a reuse gate that inspects the registry at
registration time catches the artifact and misses the decision. The reuse
question belongs at the moment a design starts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e21f3f2858

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
)
continue
leaves.append(PacketLeaf(identity=identity, sequence=slot, said=said))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Commit packet content rather than self-declared headers

When an anchored packet is replaced by a header-only JSON object carrying the same t, i, s, and d values, this code recreates the exact same Merkle leaf without checking the SAID, signature, or payload. verify_anchor() therefore returns VERIFIED at lines 363-365 even though the actual signed packet and all of its evidence have been deleted, defeating the anchor's wholesale-truncation guarantee. Validate each packet before admitting its leaf, or bind the leaf to the canonical packet bytes rather than trusting copied header fields.

Useful? React with 👍 / 👎.

Comment on lines +398 to +405
if insights.startswith(DEFERRED_PREFIX):
# Deferral is NOT completion. Staging this would exclude the file
# from later pending runs and let --commit record it as a finished
# distillation, permanently marking it processed with nothing
# extracted. Leave it pending and say so.
deferred.append(file_path)
print(f" DEFERRED (still pending): {insights[len(DEFERRED_PREFIX):].strip()}")
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove deferred files from subsequent limited batches

When an oversized file reaches this branch, leaving it pending keeps it at the same position in the stable sorted list from _get_files_to_process(). Because each run selects pending_files[:args.limit], every later scheduled run selects and defers that file again; once the first limit pending files exceed the cap (the heartbeat uses 3), no later eligible document is ever processed. Record deferred files separately or continue scanning until the batch contains limit files that can actually be processed.

Useful? React with 👍 / 👎.

Both are earned from this session's own record rather than asserted, and both
are small memory files in the existing convention rather than new philosophy
docs -- the doc-budget rule and finite-steps-share-the-flow both say to encode
into surfaces that already exist.

scale-mismatch-is-the-recurring-defect

Every defect found this session has one shape: the property lives at one scale
and the check runs at another. Seven instances, all fixed, all with commits --
KERI (bytes vs field names), ensemble unanimity (claim vs whole response), the
reuse gate (design vs registration), FM-4 (structure vs instance), the consent
gate (resolved record vs non-empty string), head truncation (set vs chain), and
the CI break I caused (full green set vs the subset I ran).

The reason it keeps happening is that a mismatched measurement is invisible from
inside itself. It does not return "unknown" -- it returns a confident wrong
answer. Whole-response cosine similarity never reported low confidence; it
reported 100% consensus, six times, on prompts written to provoke dissent.

Rule: name both scales for every check. Where the property lives, where you are
measuring. If they differ the check is decorative however rigorous it looks.

This also states the anchor's design argument in one line: gap enumeration runs
at chain scale, truncation attacks set scale, so no amount of gap enumeration
could ever see it. The anchor was not a better version of the same check -- it
was the first check at the right scale.

correlated-selves-need-mechanisms-not-opinions

personal-meta-harness-v1.0 already records that multi-agent coordination and
multi-operator-across-time coordination are structurally identical. If that
holds, this session's ensemble research transfers, and it is unflattering:
9 judges across 7 families carry ~2 independent votes, and family diversity does
not recover independence.

Applied to sequential selves: me-now and me-after-compaction are near-perfectly
correlated voters, so "have a fresh session double-check it" buys close to
n_eff = 1. It feels like a second opinion and is not one. The same argument makes
the Consensus row of the personal harness its weakest row, for a measurable
reason rather than a stylistic one.

What actually decorrelates is a different MECHANISM -- a test, a gate, an
anchor, a third-party mirror -- because those fail for reasons unrelated to any
agent's weights. This session is its own evidence: every real defect was found by
external review or by a mechanism, none by a fresh agent reasoning again over the
same code, including when that agent was told to be adversarial.

The memory corollary, also from this session: across the compaction boundary what
transferred was self-enforcing (failing tests, blocking gates, named failure
modes with counts, commit messages carrying the why). What did not transfer was
prose needing a reader -- intentions and next-step lists had to be re-derived.
So "save everything" is the wrong rule and the repository's own doc-explosion
history already said so. Sharper: save what enforces itself, index what does not,
prune the rest. A test is a memory that reads itself; a doc is a memory that
needs a volunteer.

MEMORY.md index updated. No new top-level doc, no new framework.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if tn in {"write", "edit", "multiedit"}:
return "claude-code"
if tn in {"write_file", "replace"} or event_name == "AfterTool":
return "gemini-cli"
return "agent-tool"

P2 Badge Attribute Hermes edits to the Hermes surface

When the configured Hermes hook invokes write_file, this branch labels it gemini-cli; when it invokes patch, it falls through to agent-tool. Hermes installs the hook under the native post_tool_call event, so every Hermes write is submitted and permanently recorded with the wrong proposer and source_systems attribution. Use the native event name to distinguish Hermes from Gemini before classifying the shared tool names.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

print(
json.dumps(
{
"wrote": str(args.anchor.relative_to(REPO_ROOT).as_posix()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Support anchor output paths outside the repository

When publish is called with the supported --anchor option pointing outside REPO_ROOT, the anchor is successfully written and then this relative_to() call raises ValueError. The command therefore exits as a failure after modifying the requested file, and external anchor destinations cannot be published reliably; format an absolute path when it is not repository-relative.

Useful? React with 👍 / 👎.

…at measures it

The manual review process found every real defect in this session. The local
ensemble reported unanimity it never measured. Same operator, same questions,
opposite outcomes -- that gap is the most valuable unexploited signal here, and
it deserves to be understood rather than admired.

THE HYPOTHESIS UNDER TEST

The operator's account is that the reviewers have FLOSSI0ULLK context, GitHub
MCP, deep research, frontier models, and a harness that lets them iterate
critically on previous reviews with structured output.

That is consistent with the literature AND with this repository's own memory.
project-correlated-selves-need-mechanisms records that what decorrelates
reviewers is a different MECHANISM, not a different mind. A model with repository
access is running retrieval, whose failures are independent of its weights. A
model without tools answers from priors -- and priors are exactly what frontier
models share.

So the prediction is falsifiable: tool access should raise effective independence
MORE than model-family diversity does. If it holds, the diversity policy needs
rewriting around retrieval grounding rather than provider surfaces, and the local
ensemble is not underperforming because it has the wrong models -- it is
underperforming because its voters are blind.

The protocol includes a four-reviewer experiment that varies exactly one thing at
a time (two tooled, two bare-chat in different families) to separate those two
effects. Payload is the ADR-18 tier-2 reuse review this project already owes on
the provenance anchor, currently registered emergency pending exactly that.

WHAT THE PROTOCOL FIXES ABOUT WHAT WE WERE ALREADY DOING

It names the distinction that makes sequential review work: each pass must be
told to REFUTE the prior, not to build on or synthesize it. Adversarial cascade
breaks correlation deliberately; consensus cascade is social reinforcement and
makes agreement worthless as evidence (arXiv:2604.07667). The prompts are written
adversarially and the doc says that if a future version starts asking for
synthesis, it has become the thing it replaced.

Three schema fields carry most of the weight: evidence must be retrieval rather
than recollection (the field that converts a correlated prior into an
uncorrelated observation -- and CF-5 in the register is what happens without it);
falsifier, which converts an opinion into a check; and predicted_majority /
where_you_expect_to_be_alone, which implement surprisingly-popular voting and
recover a correct minority. Not hypothetical here -- ADR-20's blast radius was
decided against a minority that was right and the tally needed correcting twice.

THE MEASUREMENT

scripts/review_independence.py computes Kish n_eff over the findings-overlap
matrix, the eigenvalue variant as a cross-check, per-reviewer solo-find counts,
and the pairwise phi table ordered so the most-independent pair is visible at a
glance.

Deviation from the source method, stated in the docstring, the registry reuse
block and the protocol doc: the paper builds vectors from errors against gold
labels; we have none, so the item set is the union of findings raised. That
measures REDUNDANCY, not accuracy -- "how many perspectives did I pay for", never
"how often were they right". It must never be cited as the paper's own result.

Reuse block records three real probes: the Kish formula (adopted verbatim),
numpy/scipy (rejected -- undeclared dependency to find the largest eigenvalue of
a matrix with k under ten, so a 12-line power iteration instead), and
krippendorff/statsmodels (rejected for a cited reason -- arXiv:2605.29800 states
kappa "conflates prevalence with dependence" while phi isolates it, so a kappa
library computes the wrong number).

A BUG THE FIXTURE FOUND IN MY OWN TOOL

A reviewer with zero findings has no variance, contributes no pairwise phi, and
still counts toward k -- which appears in the numerator. A null reviewer
therefore INFLATES n_eff and flatters the panel. The docstring named that risk
and did not guard it, which is how every other defect in this repository started.
Now excluded, with the reason printed to stderr.

759 pass on the CI green set; spec_gate --check green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Grok landed the computer-use lease router and Playwright MCP, so automating the
manual context-shuttling is now tractable. Recording the constraint before it is
built rather than after.

The right first target is transport, not decision: paste packet, wait, copy JSON,
paste onward with priors attached, collect. Mechanical, high-friction, no
judgment -- and it stays on the router-not-controller line the computer-use spec
already draws.

Two constraints fall out of what exists. Web reviewers are automatable today and
desktop/TUI reviewers are not, because sendinput and screenshot are default-deny
even under an exclusive lease and uia.invoke is unwired pending a probe; the spec
names Playwright MCP as the sanctioned web actuator, so web flows go through
Playwright and never through input injection. And prefer an API over driving a UI
wherever one exists -- more reliable than DOM scraping, and many services
restrict automated access to their web interfaces.

The part worth writing down before building:

Automation tends toward homogenization -- one pipeline, one packet template, one
browser profile, one session state. Every one of those pushes reviewers toward
each other, and reviewer DIFFERENCE is the entire source of value. An
orchestrator that makes four harnesses uniform would be faster and worse, and
nothing in the output would look wrong. A homogenized panel still returns
confident, well-formatted, unanimous findings.

That is the scale-mismatch signature again: the property lives in the diversity
of retrieval and nothing on the output surface measures it. So the automation
gets a falsifier like any other artifact here -- baseline n_eff on manual runs,
re-measure after automating, and if n_eff falls the automation consumed the
independence it was built to scale. review_independence.py already computes it.

Corollary: the JSON schema is what makes this automatable at all. It was adopted
because it made reviews better and turns out to be the same property that makes
the transport mechanical. It is now load-bearing twice; keep it stable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ADR-18 tier-2 review this project owes on the anchor, which is registered
emergency pending exactly this. Also the first run of the manual review protocol,
so it doubles as the experiment that tests whether tool access or model-family
diversity does more for effective independence.

Reviewer assignment varies one thing at a time: A1 web/deep-research and no repo,
A2 repo and no web, A3 bare chat, A4 bare chat in a different family. A3 vs A4
isolates family with tools held constant; A3 vs A1/A2 isolates tools against the
bare-chat baseline. Then review_independence.py over the four outputs.

lane-a-prompt.txt is the packet extracted verbatim for pasting -- ~2.6k tokens,
identical to every reviewer, because any variation between them makes n_eff
uninterpretable.

Two things the packet does deliberately.

It quarantines the author's reasoning into a block labelled as a target rather
than as context, and tells reviewers to attack it. The packet author wrote the
artifact, so that reasoning is a shared prior injected into all four reviewers at
once -- the one correlation the protocol cannot measure out. The contamination is
declared in the packet header so a later reader can discount the run, not because
declaring it makes it safe.

It lists the eight weaknesses already accepted, so reviewers do not spend passes
rediscovering them, and says explicitly that finding a NEW problem with any of
them, or showing one is worse than stated, is in scope. Question 6 makes "this
should not exist in this form" an admissible finding.

DEFECT FOUND WHILE SITING THIS

docs/reviews/ exists rather than .agent-surface/ because the reuse gate requires
reviewer.record to be a repository-relative file that exists, and
.agent-surface/reasoning/ensemble/ is gitignored at .gitignore:75. A reuse block
pointing there passes the gate on the machine that ran the poll and resolves to
nothing in a fresh clone.

ADR-20 currently has exactly that: its reviewer record names
20260824T023542Z_97e6b32c78072e8b_synthesis.json, which exists locally and is
untracked. Flagged in docs/reviews/README.md, not fixed -- re-pointing an ADR's
evidence is an operator decision, and the honest fix is copying the synthesis
into a tracked record rather than quietly changing the path.

Evidence only the author can resolve is not evidence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c425978d5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -1229,9 +1945,19 @@ def main() -> int:
manifest_path,
check=args.check,
dry_run=args.dry_run,
include_user_scope=args.include_user_scope,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward user scope into nested skill materialization

When materialize_shared_agent_surface.py --include-user-scope is invoked directly, this flag reaches materialize() but the nested materialize_skill_surface() call still uses its default include_user_scope=False. The command therefore updates user-scoped MCP and hook targets while silently skipping the Codex and Hermes user skill targets declared in shared-skill-surface.json, despite reporting success; forward the flag to that nested call as is already done for the hook materializer.

Useful? React with 👍 / 👎.

# Envelope was well-formed and not an explicit error -- treat
# presence of a result as success even if we can't parse the
# inner text payload.
return "error" not in response and isinstance(response.get("result"), dict)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return failure for MCP tool error results

When agentmemory responds with a valid MCP tool-error envelope such as result.isError: true, _extract_text() deliberately returns None, but this fallback then returns True merely because a result object exists. Callers of the documented save() -> bool API are consequently told that rejected memory was durably saved; check isError here, or distinguish an absent text payload from an explicit tool error.

Useful? React with 👍 / 👎.

kalisam and others added 3 commits August 26, 2026 08:04
ADR-20 cited its reuse reviewer as
.agent-surface/reasoning/ensemble/20260824T023542Z_97e6b32c78072e8b_synthesis.json.
That path was unresolvable from this repository twice over: .agent-surface sits
at the WORKSPACE root, one level above FLOSS, and the ensemble directory is
gitignored at .gitignore:75. Outside the repo, and absent from any clone.

The synthesis is copied to
docs/reviews/2026-08-24-adr20-adversarial-audit/synthesis.json, byte-identical --
sha256 94f419aa3ca545885c21f33da151ad475dc0067e39274b9cc09994034bd05c93, 200,104
bytes, verified before and after. The ADR's two body references and the registry
entry are repointed. The original is removed in the following commit, once this
copy is committed, so there is never a window with no durable copy and never two
copies that can drift.

WHY NOBODY NOTICED, WHICH IS THE LARGER FINDING

The record was never checked. _reviewer_problems -- and the record-resolution
guard inside it -- runs only for tier == 2 (spec_gate.py:581). ADR-20 is tier 1,
so its `reviewer` field was freeform prose that happened to contain a path.

Verified by running _reviewer_problems directly against the old value, which
returns "does not exist -- an unresolvable record is not evidence", and against
the new value, which passes. The gate would have caught this the moment it
applied. It never applied.

Generalised in docs/reviews/README.md: a tier-1 entry's reuse block is RECORDED
but not VALIDATED. Any evidence claim living in a tier-1 block is unchecked prose
and should be read that way until the entry is promoted. Not changing that rule
here -- widening tier-1 enforcement is a separate decision with its own blast
radius, and this session has already learned what happens when a validator is
tightened against existing history without enumerating what breaks.

The reviewer field is now the structured object shape rather than prose, with an
HONEST roster. The poll dispatched six voters across five surfaces and six
families; two produced no position -- an unclosed <think> restatement and a
mid-sentence truncation -- so surfaces and families are recorded as the four that
actually answered (huggingface/mistral/nvidia/openrouter; deepseek/mistral/llama/
gpt). Still meets the >=3 surfaces / >=4 families bar, on voters that voted.

The record README states what the file does NOT support: its own
tier1 / 100% / "unanimous" labels are false, its minimum pairwise similarity is
0.844 against a 0.75 threshold so no pair could have been separated, and the Q1
tally derived from it was wrong until corrected in the ADR on 2026-08-26. The
value of the file is the raw voter_responses[], which is what made the correction
possible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ed hash was wrong

Caught by verifying the commit rather than trusting it. .gitattributes carried
`*.json text eol=lf`, so committing the ADR-20 review synthesis rewrote it from
200,104 CRLF bytes to 193,821 LF bytes. The stored blob hashed
92f3aecce54f6d2a37cf13077c2e3597f2c590e0f6161b481e68f45d4875c0eb, while the
record README published
94f419aa3ca545885c21f33da151ad475dc0067e39274b9cc09994034bd05c93 as the file's
integrity claim.

So the previous commit produced exactly the failure it was fixing, one layer
down. The old defect was a record path that only resolved on the author's
machine. The new one was a record HASH that only verified on the author's
machine. Both are evidence that works for the person who does not need it.

docs/reviews/**/*.json, docs/reviews/**/*.txt and .agent-surface/provenance/**/*.json
are now `-text`, so git applies no transform. Re-staged; the blob now hashes
94f419aa... at 200,104 bytes with CRLF preserved, matching the published value
byte for byte.

The provenance packets are included because they are signed over their exact
bytes: any eol transform on a packet would invalidate its signature and its SAID.
They are gitignored today, so nothing was broken -- but the attribute is the
cheap guard against the day one of them is tracked, and it costs a line.

General shape, for the register: line-ending normalization is a content
transform applied by default to everything a repository calls text. That is
correct for source and silently wrong for anything whose bytes ARE the claim --
signed material, hashed evidence, fixtures pinned by digest. `text=auto` is a
convenience default sitting underneath every integrity claim in the repository,
and it is invisible in a diff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two instances an hour apart, both found by verification rather than review: a
reuse record whose path resolved only on this machine, and then -- while fixing
it -- a published sha256 that verified only on this machine, because
.gitattributes rewrote the file on commit.

Same defect at two layers. A record resolves for the author; a hash verifies for
the author; neither does anything for the auditor, who is the only reader that
matters.

Rules recorded: verify the committed BLOB not the worktree file after publishing
a hash; mark anything whose bytes are the claim as -text; and treat a tier-1
reuse block as unchecked prose, because the record-resolution guard only runs at
tier 2.

Widening tier-1 enforcement deliberately not done -- tightening a validator
against existing history without enumerating what breaks is CF-1 in this same
register.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants