# M12 W3 — doyle design ruling

> doyle 2026-06-14, gating todlando's W3 plan (M12-W3-PLAN.md). Claims independently verified
> against source before ruling:
> - `strings: toml::Table` opaque, REQ-MANIFEST-3, dot-path via `profile::string_at` — manifest.rs:66-75 ✓
> - `get_string` resolves through `resolve_option` (already `<adapter>:<profile>`-aware) — registry.rs:377-384 ✓
> - `fallback_adapter` exists ONLY in docs (CONTEXT.md:137, MANIFEST.md) — NO `.rs` consumer ✓

## T3.1 — file-backed `[strings]` — APPROVED with guards

**Q1 syntax — APPROVE inline table, disambiguated.** `key = { file = "rel/path" }`. Pointer
recognized ONLY when the value is a table with EXACTLY one key `file` → typed `StringFile { file: String }`.
Any other table shape stays an opaque nested strings tree (back-compat: existing nested string
trees untouched). Leaf-replace overlay already replaces the whole node, so a profile cleanly swaps
pointer→literal string OR literal→pointer. No new overlay logic.

**Q2 location — APPROVE `adapters/<adapter>/strings/`** (sibling of `profiles/`). File paths resolve
relative to that dir. **REQUIRED guard (HAZARD-class): reject `..` and absolute paths** (path
traversal) — ship a dedicated unit test for the escape attempt, tag it as the hazard evidence.
Shipped adapter ships its strings file in-package; a local profile may override the pointer
(different file) OR inline a literal (leaf-replace). Confirmed.

**Q3 timing — APPROVE.** Validate-exists at register; read LAZILY at `get-string` (edits reflect
without re-register); skip-diagnostics (emit a diagnostic, NO silent drop, do NOT error) on
missing/unreadable — matches the `[digest]` stance. `get_string` returns `Value::String(file_contents)`
on a healthy read.

**REQ:** mint REQ-MANIFEST-5 in traceable-reqs.toml FIRST. ADR-0001 wire territory — additive,
N-1-safe, regen `manifest.schema.json` (CI drift-gated). Stages activate as evidence lands.

## T3.2 — cross-adapter fallback target `<adapter>:<profile>` — RULE (a), minimal contract-only

NOT (b). No consumer exists; standing up the node-wide setting + invocation-switch now is
speculative wiring → violates "activate, don't pre-fail." The consuming milestone (echo/psyche
rate-limit fallback) owns the switch.

For W3: `resolve_option` ALREADY resolves `<adapter>:<profile>`. So T3.2 is a **contract lock**:
- Unit test proving a fallback-target address resolves adapter:profile-aware via `resolve_option`
  (incl. bare `ccs` and `ccs:<profile>`).
- Reconcile CONTEXT.md:137 wording — ccs is its own adapter, addressable bare `ccs` or `ccs:<profile>`.
- **Do NOT add a config field with no reader.** No new impl REQ unless genuinely new surface appears
  — tag unit + doc stages only (fold under an existing MANIFEST/registry REQ if one fits).

**Escalation clause:** if during build you find spt-claude-code is genuinely BLOCKED without an
authored fallback seam, STOP and escalate to me — don't build speculative config.

## Gate posture
Build uncommitted. I gate (reproduce traceable + read the load-bearing tests) before any commit.
Full suite + clippy -D + traceable EXIT=0 required. Operator calls the commit.
