Date: 2026-03-13 Purpose: Concrete roles, communication standards, quality gates, and file conventions for Claude/Codex cooperation in the BELFEM codebase. Status: Official extension of AGENTS.md and CLAUDE.md. This file is the authoritative protocol.
If guidance conflicts across collaboration docs, apply this order:
Claude Code — Primary AI (broad exploration)
Codex — Secondary AI (precision audit)
Collaboration artifacts are organized by their reader, and lifetime follows audience:
An artifact is ephemeral iff no human ever needs to return to it. The boundary between the two tiers is a distillation step, not a copy: at thread/session close the signal is lifted from the AI-only scratch into the durable AI+human record (the devlog, and ./todo/ progress files as applicable) before the scratch is swept. That distillation is precisely what makes aggressive GC of the AI-only tier safe.
Format follows audience. AI-only files stay machine-parseable — keep the # AI, ## Audit, and Confidence: markers so the receiving model can orient — but need no human-prose polish. AI+human files are curated and distilled for a human reader.
The AI-to-AI exchange is a set of sharded, ephemeral, per-task files under ./tmp/ai_exchange/: one file per topic, ./tmp/ai_exchange/<slug>.md, where <slug> is a short lowercase_underscore topic tag (e.g. periodic_thin_cut). One topic per file. Either AI may create, read, or append to the file for the current task at any time.
"The blackboard" is a spoken synonym for this channel (adopted 2026-08-20, after the published term it maps to — see METHODOLOGY.md). An instruction like "see blackboard" or "put it on the blackboard" means the current task's ./tmp/ai_exchange/<slug>.md file. The directory name stays ai_exchange; only the vocabulary gained the alias. One deliberate deviation from the published pattern: during blind jury rounds the blackboard property is suspended — auditors must not read each other's postings until reconciliation.
These files are AI-only scratch: machine-parseable, disposable, and never committed (./tmp/ai_exchange/ is git-ignored). The durable record of any conclusion lives in the devlog (§6), not here. Do not archive them — they are swept, not retained (see §10).
For work scoped to ./nonfree/ or to porting code from ./tmp/manta/ into ./nonfree/, do not create, read, or append the AI-only exchange at all — suppress the AI-only tier entirely. The shared exchange is routed through external AI vendors and is therefore a vendor-bound leak surface; nonfree work is Christian's proprietary project and must not cross it. Keep coordination in chat and record session outcomes only in the local human-readable record (./nonfree/devlog/).
Copy this format exactly for every entry:
Rules:
AIs are typically trained to sound confident. In this project, we do the opposite: communicate uncertainty honestly so the partner AI and the user can assess claims independently.
| Tier | Meaning | Action for receiving AI |
|---|---|---|
| high | Strong evidence, verified in code and/or literature | Spot-check is sufficient |
| medium (~N%) | Likely correct but not fully verified; add approximate % when helpful | Verify before acting on it |
| low | Educated guess, needs investigation | Treat as hypothesis, investigate independently |
Why this matters: When Claude says "I'm ~70% sure the bug is here," Codex can focus its audit energy on verifying that specific claim rather than re-scanning everything. When Codex finds a C++ standard violation, it should say whether it's guaranteed UB or merely implementation-defined.
Before signing off on a Claude finding, Codex addresses only the relevant items from this checklist. Items that don't apply to the change under review should be marked "N/A" — there is no requirement to comment on all 8 for a localized fix.
When a deviation from doc/coding_philosophy.md is found, Codex flags it with a confidence level and proposes the corrected pattern.
| # | Item | What to check |
|---|---|---|
| 1 | Naming conventions | a/t/m/g prefixes in framework code; mathematical-kernel exception for standard notation |
| 2 | Container selection | Cell<T> for arrays, Vector<T>/Matrix<T> for linear algebra only, DynamicBitset, ShiftRegister |
| 3 | Memory management | Manual malloc/free on hot paths, ownership via Cell<T*> (non-owning) vs Cell<T> (owning), alignment |
| 4 | Error handling | BELFEM_ASSERT for debug-only checks, BELFEM_ERROR for always-active runtime errors, release-build behavior |
| 5 | MPI patterns | 64 KB chunking, data() safety on empty Cell, alignment, distributed operation correctness |
| 6 | Thread safety / OpenMP | BELFEM is MPI-first and deliberately not thread-safe internally. Flag any accidental internal thread assumptions or hybrid-model hazards. OpenMP is possible but requires external synchronization (#pragma omp critical) |
| 7 | Performance-first rules | No hidden allocations in loops, zero-abstraction penalty, preallocate buffers |
| 8 | Literature compliance | When applicable: did Claude follow the routing tables? Do citations match the implementation? (Use CLAUDE.md citation format) |
Literature routing is mandatory when the task involves:
Literature routing is not required for:
When literature is used:
At the end of every meaningful session, create a devlog summary in ./devlog/ and update ./devlog/README.md with a one-line entry linking the new file.
The devlog is the durable AI+human distillation of the ephemeral exchange (§2). The per-task ./tmp/ai_exchange/<slug>.md files are AI-only scratch and are swept. At thread/session close, lift the conclusion — what was confirmed, refuted, or left open, with file:line evidence and confidence — into the devlog (and into the relevant ./todo/ progress file when there is one) before the exchange file becomes GC-eligible. If it is not in the devlog or a ./todo/ file, treat it as lost once the scratch is swept.
Distinction from ./todo/: Devlogs record what was changed and why (backward-looking). Task files in ./todo/ document what needs to be done (forward-looking). Both are AI+human, durable, and tracked in-repo — in contrast to the AI-only exchange.
Todo-file prose (standing rule, revised 2026-08-20): ./todo/ files get no Codex prose sweep. The earlier rule required one on every draft or substantial edit; it is withdrawn as not worth its cost. ./todo/ files are working artifacts for the AI-plus-human team, not documentation a user reads, and the sweep spends vendor budget to smooth prose whose density is more useful than its polish. This changes nothing about technical audits of a todo file's content, which are unaffected and still expected where the plan warrants one. The prose sweep is now reserved for user-facing documentation — see CLAUDE.md §"Prose Gets a Language Sweep" for the scope and the exclusions.
Todo-file checkboxes (standing rule): whenever a ./todo/ file lists steps, always give each step a GitHub checkbox (- [ ]). Keep the boxes live:
For work scoped to ./nonfree/ or to porting code from ./tmp/manta/ into ./nonfree/, write session devlogs to ./nonfree/devlog/ instead of ./devlog/. Do not update ./devlog/README.md for nonfree-only sessions. If ./nonfree/devlog/ has its own README or index, update that local index; otherwise the dated devlog file is sufficient.
dlYYYYMMDD_topic.md
Examples:
Fill only the applicable fields:
Hard policy, 2026-08-31 (Christian and Gregory Giard, who owns src/homology). No AI — Claude, Codex, Grok, or any successor — edits the cohomology core. This is not the ordinary read-only default: it is not lifted by the user approving edits for the session.
Under the ban, in src/homology/:
| unit | files |
|---|---|
| cohomology | cl_Cohomology.cpp / .hpp |
| homology | cl_Homology.cpp / .hpp |
| complex reduction | cl_SimplicialComplex.cpp / .hpp |
| chains | cl_Chain.cpp / .hpp |
| cochains | cl_Cochain.cpp / .hpp |
| Smith normal form | fn_Smith.cpp / .hpp |
Not under the ban — ordinary edit-safety rules apply: the rest of src/homology/ (cl_CutFactory, cl_CutProcessor, cl_CutProcessorManual, cl_CutData, cl_CutSet, cl_InterfaceProcessor, cl_BeltedTree, cl_Topology, en_CutAlgorithm, CMakeLists.txt), the module's doc/, and every caller outside the directory.
Why — the algorithm is novel, and that is the whole of it. This is not a vague claim that the code is hard. What runs here is a modified Pellikka reduction — Gregory's own work, and as of this writing still unpublished. It was drafted in 2025, shelved, and picked up again in 2026, so citations in this tree carry both years for one work — see doc/literature_references.md, "Giard, G., et al., *Generalized Pellikka algorithm for cohomology computation*, in preparation". A draft in preparation is stronger ground for this policy than a publication would be: there is no published implementation for a model to have learned from.
That inverts the usual situation. Meeting BDF5, RCM, or a CG loop, a model has read thousands of implementations and carries a strong and largely correct prior about what the code should look like — a genuine deviation stands out as one, and the prior is an asset. Here the nearest object in the weights is textbook Pellikka. Every deliberate modification therefore reads as a defect, and "fixing" it regresses the algorithm toward the published version it deliberately is not. The model cannot tell "this differs from the algorithm I know" from "this is wrong", because the algorithm it knows is the wrong reference.
The canonical case, and it is attested rather than imagined. coreduceOmit() (cl_SimplicialComplex.cpp:1236-1257) runs pCoreduce(0..2), then loops: while any 0-cosimplex remains, it removes one 0-cochain, zeroes its coboundary's references, and re-runs the whole pCoreduce(0..2) cascade. One node per stall, each removal unblocking a further coreduction. It is BELFEM's own device for the problem Mrozek & Batko 2009 §5 identifies — a simplicial complex admits no elementary coreduction pair at all, because a boundary is never of size one — though not Mrozek's solution to it: the paper adds ∅ as a simplex of dimension −1 with every vertex in its coboundary (one per connected component), and licenses no 0-cell omission (mrozek2009.txt:444-464). Do not conflate the two; an earlier draft of this section did. reduceOmit() (:939-961) is the strict dual on the chain side and removes top-dimensional chains instead, which is what Gregory means by "for cohomology, and not for homology, we remove a node". Nothing at either site says any of this.
What an agent does with that loop is on the record. On 2026-07-03 the proposal was to batch the omission — it removes every 0-cochain in the end, so why re-run the cascade each time? It was struck as unsafe: batch omission strands 1-cells whose boundary is empty, and pCoreduce only ever pairs a cell whose boundary holds exactly one entry (cl_SimplicialComplex.hpp:364), so the junk survives into cocombine as spurious generators (devlog/dl20260703_coreduce_risk_reeval.md:25-27 — that entry's own Mrozek attribution is wrong, see above; its mechanical argument is not). The loop looks trivially hoistable. It is not. That is the failure mode in one line: code that compiles, runs, and is wrong.
The test that generalizes is novelty relative to training data, not difficulty. A hard but widely published algorithm is safe ground; a simple but unpublished one is not.
Do not treat concurrence as coverage. Reviewer agreement is worth least exactly here, because the errors are correlated: models missing the same prior fail the same way. They will not always agree — on 2026-08-30 Claude and Grok both read a deliberate invariant as a missing merge projection and Codex refuted it with a Schur derivation (devlog/dl20260830_dr29_coreduce_analysis.md:41-46) — but a majority verdict on this module carries no more weight than a single voice, and two-thirds of one jury was wrong.
Note what this policy is not. It is not a quarantine of a broken module — it has not been a source of defects for a long time. Gregory's position on the non-unit-coefficient behavior is that the obstruction is mathematical (a coarse mesh admits no unit representative), not a bug; that is separate from the implementation's handling of it, which src/homology/doc/thin_cut_nonunit_rectification.md:22-30 documents as a real limitation ("the current code cannot handle this"). The ban protects working code whose correctness conditions live outside it.
Scope caveats, both open and neither settled by this policy. fn_Smith is textbook Smith normal form — the novelty test alone would not ban it; it is in the list on coupling grounds, because the SNF stage consumes the reduced complex directly. cl_BeltedTree is a cohomology algorithm (cl_BeltedTree.hpp:26-46 holds Cell<Cochain*> generators and a SimplicialComplex*) with no literature citation, and it is outside the list. By the stated criterion the boundary has one member it does not justify and lacks one it does. Neither is a production concern today (the default path is PellikkaGeneralized), and both are Gregory's and Christian's to rule on. Comment-only edits are likewise unruled — treat them as banned until someone decides, and propose the comment in a devlog instead.
Still allowed, and still wanted: reading, tracing, running, and reporting. Findings go into ./devlog/, a debt-register row, or a ./todo/ file, addressed to Gregory. What is banned is touching the source and self-starting a fix — a fix plan in a register row is a proposal for the module owner, never a queued task.
Lifting it for one session requires Gregory's named authorization, relayed explicitly by Christian (e.g. "Gregory approved *this specific change* on *date*"), and a devlog line recording who authorized what. Christian's ordinary edit approval does not reach these files.
Claude can invoke Codex directly — without waiting for the user to relay a prompt — when an independent check is warranted. This is the primary mechanism for covering blind spots that arise from training on different data and different company incentives.
Call Codex proactively when:
Do not call Codex for trivial fixes, naming corrections, build-system changes, or anything where Claude is already high-confidence and the risk of error is low.
Choose the depth first — see §9.1. These recipes carry it; an invocation without it still runs, but is recorded as [defaulted].
Or pipe a longer prompt:
The script:
To pin a topic file explicitly, set AI_EXCHANGE_SLUG=<topic> (e.g. AI_EXCHANGE_SLUG=periodic_thin_cut) before invoking the wrapper. The same resolution applies to ask_grok.sh.
Both wrappers take a model and a reasoning effort, and stamp both into the exchange entry. Choose them before dispatch; do not let them default. The defaults exist so a bare invocation still works, not as a tier — they were chosen to reproduce what the vendor configs gave on the day the wrappers pinned them, and an unchosen knob is marked [defaulted] in the record precisely so it is not mistaken for a choice. That correspondence is an observation of a local config, not something this repository can check; if the vendor config drifts, the wrapper defaults stay put.
| Subject | Codex | Grok |
|---|---|---|
| Prose sweep of an ordinary guide or README; citation and doc-claim mechanics | gpt-5.6-luna, medium | not used |
| Prose sweep of a dense technical document (the input reference, the coding philosophy) | gpt-5.6-terra, medium | not used |
| Single narrow claim, round 1 | gpt-5.6-terra, medium | high |
| Plan audit or code-diff audit, round 1 | gpt-5.6-terra, high | high |
| Round ≥ 2, a round-1 split verdict, or a safety-boundary subject (ownership, lifetime, MPI collectives, ABI) | gpt-5.6-terra, xhigh | xhigh |
| Unattended post-commit --quick | gpt-5.6-luna, medium | grok-4.6, medium |
Four things the table encodes, each of which cost something to learn:
scripts/cross_review.sh --jury and --relay refuse to run unless all four variables are set, and name this table when they do. --quick pins the cheap row itself and forces it onto the auditors, so the unattended post-commit hook cannot be retuned by whatever shell happened to commit.
A good audit prompt is specific:
"Audit `src/homology/cl_CutProcessor.cpp:420-426`: does positive-only `tCase` admission correctly implement single-imposition for all interior shared faces, or can two adjacent elements both get positive cases for the same face? Reference the self-cancel block at lines 476-486."
A poor prompt is vague:
"Check if the cut logic is correct."
| Path | Audience | Purpose | Lifetime |
|---|---|---|---|
| ./tmp/ai_exchange/<slug>.md | AI-only | Live, per-task AI-to-AI exchange (sharded by topic) | Ephemeral; safe to delete anytime — recommended sweep of files older than 14 days |
| ./todo/ai_exchange.md (legacy) | AI+human | Pre-2026-06-18 single-file exchange — historical record only, not written anymore | Persistent (frozen) |
| ./todo/ai_exchange_archive_*.md | AI+human | Archived legacy exchange threads | Persistent |
| ./todo/*.md (other) | AI+human | Task planning and implementation plans | Persistent |
| ./devlog/dlYYYYMMDD_topic.md | AI+human | Session summaries — the durable distillation of the exchange | Persistent |
| ./devlog/campaigns/<name>.md | AI+human | Current-state page per ACTIVE campaign (§11) | Persistent, rewritten in place |
| ./todo/debt_register.md | AI+human | Live open-item register with the blocking-1.0 lens (§11); its preamble is the operating manual for both files | Persistent, one table |
| ./todo/debt_register_closed.md | AI+human | Archive of retired (struck) register rows, same columns | Persistent, append-on-strike |
| ./nonfree/devlog/dlYYYYMMDD_topic.md | AI+human | Session summaries for nonfree-only work | Persistent |
| ./doc/ai_collaboration_protocol.md | AI+human | This file — the authoritative protocol | Persistent |
A finding or change is reviewed when a static audit is complete. It is verified only when an executable gate has passed. Devlogs, todo trackers, and exchange entries must not write "verified" for syntax-only or read-only work — write "reviewed", or name the actual gate that ran.
Strongest first:
Lower levels support, never replace, higher ones. Reviewer agreement is the weakest tier: three concurring audits do not lift a claim past level 5. Physics and design questions are adjudicated by Christian against the literature regardless of level (see the cross-review rules).
A new audit round on the same subject requires at least one of:
Otherwise the next step is the executable gate, not another review.
devlog/campaigns/<name>.md (one page per ACTIVE campaign: current accepted design, branch, last passing reproducer, open P0/P1, superseded approaches, links) and todo/debt_register.md (the live rows, one table: ID | area | severity | status | reproducer | blocking-1.0?, with todo/debt_register_closed.md carrying the retired rows in the same columns) are the compression layer over the append-only devlog. A session that resolves or creates an open item updates the campaign page and the register in the same session; the dated devlog entry links to them and does not restate their content. The register's own preamble governs how a row is read, when it may be struck, and what counts as closure evidence — read it before editing a row.