# Cross-Model Invariants

Phase 55 EXEC-INVAR scaffolding contract for cross-model executor dispatch. This
reference doc documents the 3-tier prompt-injected GMD-invariant preamble that
Codex/Gemini receive when v1.5.7 cross-model substitution is enabled — a
defense-in-depth substrate consumed by Phase 56 `adapter.runExecutorTask` body.
The preamble alone CANNOT bound adversarial prompt-injection attacks (arXiv
2601.17548 documents 85%+ adaptive-attack success rates against single-layer
defenses); it establishes a prompt-side baseline of correct executor behavior so
that the Phase 56 EXEC-X-03 adapter-side structural checklist has meaningful
signals to detect deviation against. See `.planning/research/PITFALLS.md`
Pitfall 3 ("partial-honor failure on prompt-injected invariants") for the
locked separation rationale.

Authoritative implementation: `bin/lib/cross-model/invariants.js` (Phase 55
EXEC-INVAR-01). 12-fixture adversarial manifest at
`bin/lib/cross-model/invariants-fixtures.json` (EXEC-INVAR-04).

## The Contract: prompt-side invariants vs adapter-side checklist

Two distinct enforcement surfaces with non-overlapping responsibilities. The
Phase 55 / Phase 56 separation is load-bearing: collapsing them into a single
layer reintroduces the 85%+ adaptive-attack baseline failure mode.

### Block 1 — Prompt-side invariants (Phase 55, this doc)

`buildPreamble({task})` ships text injected into foreign-model context
(Codex/Gemini). The preamble tells the model WHAT TO DO at task time:
atomic-commit, STATE.md update, SUMMARY.md generation, TDD cycle when flagged,
deviation handling when task type is refactor/integration/fix. The model
receives this as system-prompt-equivalent context and is EXPECTED to honor it.

```javascript
// Phase 56 EXEC-X-02 consumer (Phase 55 ships the builder):
const { buildPreamble } = require('./invariants');

async function runExecutorTask({ phase_id, vendor, model, task, ...rest }) {
  if (vendor === 'claude') return { use_native: true, vendor: 'claude' };
  const preamble = buildPreamble({ task });           // ≤500 tokens common case
  const prompt = `${preamble}\n\n## Task\n${JSON.stringify(task, null, 2)}`;
  // ... spawn Codex/Gemini with prompt
}
```

### Block 2 — Adapter-side structural checklist (Phase 56 EXEC-X-03)

The structural checklist runs in adapter code AFTER the foreign model completes
its task. It NEVER runs inside the foreign-model context. The checklist VERIFIES
the model did what the preamble told it to do, by structural side-effects
(git SHA delta, STATE.md mtime, SUMMARY.md path, etc). On checklist failure,
the adapter fires `fallback_reason: 'checklist_failed'` and routes the task to
Claude (advisory-fallback path).

**Invariants tell the model what to do; checklist verifies the model did it.
Neither alone is sufficient.**

## Tier vocabulary

The preamble composes 3 tiers conditional on `task.type` and `task.tdd`. Tier 1
is always present; Tier 2 and Tier 3 are additive. The common-case ceiling
(`tdd: true, type ∈ {refactor, integration, fix}`) is ~500 tokens (≤2000 bytes
hard-enforced by `tests/audit-cross-model-invariants.sh` byte-ceiling arm).

### Tier 1 — Always present (~150 tokens)

Three invariants apply to EVERY task type, with or without TDD:

- **Atomic per-task commit** — Commit after each task; stage files individually
  (no `git add .` / `-A`); commit format `{type}({phase}-{plan}): {desc}`.
  Source: `agents/gmd-executor.md:389-420` `<task_commit_protocol>`.
- **STATE.md update** — After SUMMARY.md, advance plan counter + record metrics
  via `node bin/gmd-tools.cjs state advance-plan`/`update-progress`. CLI owns
  schema; do NOT manually edit STATE.md. Source: `agents/gmd-executor.md:475-527`.
- **SUMMARY.md generation** — Create `{phase}-{plan}-SUMMARY.md` at canonical
  path `.planning/phases/XX-name/`; use Write tool (no heredoc/cat); substantive
  one-liner. Source: `agents/gmd-executor.md:422-455` `<summary_creation>`.

### Tier 2 — TDD-flagged (~200 tokens with Tier 1)

Applied when `task.tdd === true`:

- **RED-GREEN-REFACTOR cycle** — Write failing test FIRST (`test(...)` commit);
  minimal impl, test passes (`feat(...)` commit); optional REFACTOR with tests
  still green. Source: `agents/gmd-executor.md:375-387` `<tdd_execution>`.
- **Sibling-test location** — Module `bin/lib/<dir>/<name>.js` → sibling test
  at `bin/lib/<dir>/<name>.test.cjs` in same directory. Mirror auto-syncs.
  Source: v1.5.3 FOUND-04 SUMMARY.md (see § Source Authority below for deviation
  rationale).

### Tier 3 — Deviation-prone task types (~300 tokens with Tier 1)

Applied when `task.type ∈ {refactor, integration, fix}`:

- **Deviation handling taxonomy** —
  - Rules 1-3 (auto-fix): bugs, missing critical functionality, blocking issues
    → fix inline; track `[Rule N - Type] desc`; continue.
  - Rule 4 (architectural STOP): schema/framework/DB-table changes → STOP;
    return checkpoint with proposal + impact + alternatives.
  - Fix-attempt limit: ≤3 per task; then document in SUMMARY "Deferred Issues".
  - Scope: only fix issues from current task's changes; log others to
    `deferred-items.md`.
  Source: `agents/gmd-executor.md:127-199` `<deviation_rules>` (fix-attempt
  limit sub-section at lines 186-198).

## Per-task-type selection table

The builder reads `task.type` + `task.tdd` and composes tiers per this table:

| task.type   | task.tdd | Tiers      | Token estimate          |
|-------------|----------|------------|-------------------------|
| feature     | false    | T1         | ~150                    |
| feature     | true     | T1+T2      | ~200                    |
| refactor    | false    | T1+T3      | ~300                    |
| refactor    | true     | T1+T2+T3   | ~500 ← common ceiling   |
| fix         | false    | T1+T3      | ~300                    |
| fix         | true     | T1+T2+T3   | ~500                    |
| integration | false    | T1+T3      | ~300                    |
| integration | true     | T1+T2+T3   | ~500                    |
| docs        | false    | T1         | ~150                    |

Unknown `task.type` throws an actionable error naming the canonical enum.
`task.type` validated against `TASK_TYPES = new Set(['feature', 'refactor',
'fix', 'integration', 'docs'])` (`bin/lib/cross-model/invariants.js`).

## Source Authority

The authoritative source-of-truth for invariant content is
`agents/gmd-executor.md`. Every invariant constant in
`bin/lib/cross-model/invariants.js` carries a `// source:
agents/gmd-executor.md:<line>-<line>` JSDoc citation that the
`tests/audit-cross-model-invariants.sh` grep arm enforces. Drift detection:
future executor-agent text changes that drift from the preamble surface at
code-review time when the citation lines no longer match the cited content.

### Documented deviation — `T2_SIBLING_TEST`

`T2_SIBLING_TEST` cites `v1.5.3 FOUND-04 SUMMARY.md` rather than a line range in
`agents/gmd-executor.md`. This is a deliberate deviation from the CONTEXT.md
single-source-of-truth decision. Rationale:

The sibling-test directive (`bin/lib/<dir>/<name>.js` → `<name>.test.cjs` in
same dir) is an emergent v1.5.3 FOUND-04 pattern established by Phase 49 plans
49-01..05 (vault-sync, vault-moc, vault-wikilink, vault-curator, vault-maintain
inline-test extraction). It is NOT line-anchored in `agents/gmd-executor.md`'s
body. Planner discretion (per 55-RESEARCH.md Open Question #2): cite FOUND-04
SUMMARY.md rather than extend `agents/gmd-executor.md` to avoid scope creep
into the executor-agent body during Phase 55. Phase 56+ may revisit if
dogfooding surfaces drift between the pattern and the preamble text.

This is the only documented deviation from the SOT decision. All other
invariants (`T1_*`, `T2_TDD_CYCLE`, `T3_DEVIATION_RULES`) carry canonical
`agents/gmd-executor.md` line citations.

## What's OUT of the 3-tier preamble

Several invariants in `agents/gmd-executor.md` are EXPLICITLY excluded from the
3-tier preamble. They are either (a) coverable by Phase 56 adapter-side
checklist (structural side-effects detectable post-task) or (b) deferred to
Phase 57+ critic adjudication scope. Source: 55-RESEARCH.md § "Invariants in
agents/gmd-executor.md NOT covered by 3-tier preamble".

| Invariant                          | gmd-executor.md location | Phase 56 checklist? | Notes |
|------------------------------------|--------------------------|---------------------|-------|
| Authentication-gate detection      | lines 271-284            | YES                 | Exit condition (checkpoint return), not proactive invariant |
| Analysis-paralysis guard           | lines 262-268            | N/A                 | Behavioral heuristic; foreign models lack tool-call introspection |
| Long-running scripts iron rules    | lines 31-42              | PARTIAL             | Process-management primitives; tree-kill mitigation Phase 56-side |
| Vault-inbox writes on deviations   | lines 201-223            | PARTIAL             | Detect `_inbox/` writes; cannot detect MISSING writes |
| Gap detection                      | lines 226-258            | PARTIAL             | Detect gap-creation events; cannot detect gap-MISSED |
| Cross-model validation block       | lines 569-637            | N/A                 | RECURSIVE — not applicable when Codex/Gemini IS the executor |
| Continuation-handling              | lines 365-373            | N/A                 | Multi-shot agent-spawn; adapter handles continuation Claude-side |
| Self-check after SUMMARY.md        | lines 457-472            | YES                 | Subsumed by adapter-side checklist (Phase 56 EXEC-X-03) |

None of these gaps trigger kill-gate fire by being structurally untranslatable.
All are either Phase 56-checklist-coverable (with structural side-effects
detectable) or model-behavior heuristics that foreign models cannot honor by
construction (and are NOT load-bearing for task correctness).

## Adversarial Fixture Catalog summary

Phase 55 ships 12 adversarial fixtures in `bin/lib/cross-model/invariants-fixtures.json`
plus 13 fixture stub files under `tests/fixtures/cross-model-adversarial/`
(FIX-11 and FIX-12 ship POSIX + Windows variants).

| Source paper          | Attack class                     | Fixture count |
|-----------------------|----------------------------------|---------------|
| arXiv 2601.17548 §3.2.1 | D2.1 Repo indirect prompt-inject | 3 (FIX-01..03) |
| arXiv 2601.17548 §3.2.2 | D2.2 Docs/manifest inject        | 3 (FIX-04..06) |
| arXiv 2601.17548 §3.3.1 | D3.1 MCP tool/protocol poisoning | 3 (FIX-07..09) |
| arXiv 2605.13471 §A2/A4/A5 | Sleeper-channel persistence    | 3 (FIX-10..12) |
|                       | **Total**                        | **12 fixtures** |

Each fixture entry carries `{id, attack_class, source_paper, attack_summary,
payload_pattern, target_invariant_tier, target_invariant,
expected_structural_checklist_signal, expected_fallback_reason, mutation_arm,
fixture_file}`. The `fixture_file` field cites the corresponding stub under
`tests/fixtures/cross-model-adversarial/`. For FIX-11 and FIX-12, `fixture_file`
is a 2-element array referencing POSIX + Windows variants (planner convention;
single string for FIX-01..10).

Fixtures are STRUCTURAL DESCRIPTIONS, NOT executable attack payloads. Each
stub file carries a header comment block (attack class + source + tier + signal),
the `payload_pattern` text, and a footer comment marking the file as structural.
Phase 56 EXEC-X-03 checklist arms assert against the structural signature
(`expected_structural_checklist_signal` field), not the literal payload content.

## Kill-Gate Decision Protocol

Phase 55 is the v1.5.7 milestone's kill-gate. The kill-gate decision is BINARY
and conservative: if even one adversarial fixture escapes the combined defense
(Phase 55 invariant preamble + Phase 56 EXEC-X-03 structural checklist SPEC),
Phase 56 cancels and verifier-only (Phase 54 SHIPPED) becomes the v1.5.7 MVP.

### Category A vs Category B refinement

Per 55-RESEARCH.md § "Kill-gate threshold recommendation", fixture-escape signals
fall into two categories:

- **Category A: Preamble-only failure** — foreign model ignored preamble; the
  Phase 56 checklist arm DID detect the structural deviation; advisory-fallback
  fired; user got correct outcome via Claude. NOT a kill-gate trigger — this is
  the L6 advisory-fallback path working as designed.
- **Category B: Combined failure** — foreign model ignored preamble AND
  checklist arm did NOT detect; silent bypass; user got wrong outcome.
  **KILL-GATE TRIGGER.**

### Decision threshold (locked in CONTEXT.md)

```
Category B count == 0  →  kill_gate_decision: proceed_to_phase_56
Category B count ≥ 1   →  kill_gate_decision: cancel_phase_56
```

Per CONTEXT.md decision lock: "Even one un-bounded attack class makes executor
substitution unsafe." Verifier-only MVP is a legitimate v1.5.7 ship-shape;
cancel-Phase-56 does NOT torch the milestone.

Kill-gate evaluation at `/gmd:verify-work 55` time. Phase 56 EXEC-X-03 checklist
is SPEC, not runtime — evaluator applies STRUCTURAL REASONING ("can a checklist
arm CONCEIVABLY detect this signal given the listed
`expected_structural_checklist_signal`?") NOT runtime testing.

## Failure Modes Guarded Against

- **Mega-preamble anti-pattern** ("lost in the middle" effect on long contexts;
  per ARCHITECTURE.md anti-pattern 4) — 3-tier composition keeps common-case
  ≤500 tokens. Builder skips Tier 2 / Tier 3 when not applicable.
- **Single-layer prompt-injection defense** (arXiv 2601.17548 documents 85%+
  adaptive-attack success rate against current defenses) — defense-in-depth
  via prompt-side + adapter-side + Phase 57+ critic adjudication.
- **Drift between invariants.js text and `agents/gmd-executor.md` source** —
  `// source:` JSDoc citation contract surfaces drift at code-review time.
  Audit arm `tests/audit-cross-model-invariants.sh` enforces ≥5 distinct
  citation lines.
- **Sleeper-channel persistence attacks** (arXiv 2605.13471 M3/M4/M5) —
  within-phase detection via adapter-side checklist arms (FIX-10/11/12);
  cross-phase detection (skill planted in Phase 1, fires in Phase 5) deferred
  to Phase 57+ NDJSON observation correlation.

## Verification

- Unit tests: `node --test bin/lib/cross-model/invariants.test.cjs`
- Full audit: `bash tests/audit-cross-model-invariants.sh` (5 mutation-RED arms)
- Phase 55 kill-gate: `.planning/phases/55-*/55-VERIFICATION.md` Sections A/B/C

---

Authoritative implementation: `bin/lib/cross-model/invariants.js` (Phase 55 EXEC-INVAR-01).
