# Phase 7: Workflow Smoke + Convention Locks - Discussion Log

> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.

**Date:** 2026-05-19
**Phase:** 7-Workflow Smoke + Convention Locks
**Areas discussed:** gridSize + tilesetSourceHash ADRs, BNCentral chunk grid + smoke-test
**Areas auto-decided by Claude (operator delegated):** pnpm preflight + LDtk version pin, HYG-01 broadening + HYG-02 sweep scope

---

## gridSize + tilesetSourceHash ADRs

### Q1 — gridSize convention

| Option | Description | Selected |
|--------|-------------|----------|
| 4 (IntGrid/Tile) + 1 (Entity) | Research-default. IntGrid/Tile at 4 px (GCD-of-44-40), Entity at 1 px for pixel-accurate placement. | |
| 44 (Tile) + 1 (Entity) — Y-scale workaround | Tile layer at 44, rely on LDtk per-layer Y-scale to fake 40 px Y. Closer to GM mental model; risks QuickType round-trip. | |
| Entity-only (no IntGrid/Tile) | Floor tiles as entities at exact pxX/pxY. Loses auto-tile rules + walkable derivation. | |
| **(Other free text)** | **gridSize=4 unified across all layer types (IntGrid, Tile, Entity)** | ✓ |

**User's choice:** "4 for tile and entity"
**Notes:** Operator chose single unified gridSize over the research-recommended 4/1 split. Rationale: single mental grid model; 4 px entity snap acceptable since legacy BNO entities are tile-aligned anyway.

### Q2 — tilesetSourceHash algorithm

| Option | Description | Selected |
|--------|-------------|----------|
| sha256 | Standard Node crypto built-in; 32-byte digest; zero new deps. | ✓ |
| blake3 | ~5× faster; needs @noble/hashes or native binding. Speed irrelevant for loader-init check. | |
| xxhash64 | Fastest, non-cryptographic. Acceptable for drift detection but adds dep; overkill. | |

**User's choice:** sha256
**Notes:** None.

### Q3 — tilesetSourceHash placement

| Option | Description | Selected |
|--------|-------------|----------|
| Per-tileset custom field on each TilesetDefinition | F_String on each tileset def. Per-tileset granularity. | ✓ |
| Project-level custom field (single combined hash) | One enum field hashing all tilesets together. Simpler; loses granularity. | |
| Sidecar file outside `.ldtk` | Hash in `maps/.tileset-hashes.json`. Out of LDtk editor surface; can drift. | |

**User's choice:** Per-tileset custom field
**Notes:** None.

### Q4 — Drift behavior

| Option | Description | Selected |
|--------|-------------|----------|
| HARD throw + refuse to load | Loader throws; clear message naming drifted tileset + expected/got hashes. | ✓ |
| Warn + load anyway (degraded mode) | Console warning, attempt render. Risk: silent visual breakage. | |
| Auto-rehash on boot | Treat current PNG as source-of-truth, overwrite `.ldtk`. Defeats the gate. | |

**User's choice:** HARD throw + refuse to load
**Notes:** Matches Ed25519-sig-fail posture (fail-loud).

---

## BNCentral Chunk Grid + Smoke-Test

### Q1 — Chunk grid

| Option | Description | Selected |
|--------|-------------|----------|
| 4×4 of 2000×1600 px (Recommended) | 16 chunks; all under 5000 px lag threshold; aligns with grid=4. | |
| 2×2 of 4000×3200 px | 4 chunks; at lag-threshold edge; risks editor slowness. | |
| Defer chunking to v1.2 | Phase 7 smoke-tests without committing. | |
| **(Other free text)** | **3×3 (odd, so center isn't split)** | ✓ |

**User's choice:** "choose an odd number so the center won't be split into quads. 3x3?"
**Notes:** Sharp insight — BNCentral's hub is central; even-grid chunking puts seams through the most important area. 3×3 = 9 chunks at ~2668×2136 each (rounded to 4-multiples per gridSize=4), all well under 5000 px lag threshold. Center chunk owns the hub.

### Q2 — Smoke-test shape

| Option | Description | Selected |
|--------|-------------|----------|
| 3×3 synthetic GridVania matching the chunking exactly | Mirrors chosen chunking; real-world timing data. | ✓ |
| Single flat 8000×6400 IntGrid (no chunking) | Stress-test failure threshold to confirm chunking required. | |
| Scripted synthetic JSON (no editor session) | Programmatic generation; skips human-editor-tolerance check. | |

**User's choice:** 3×3 synthetic matching chunking
**Notes:** None.

### Q3 — Smoke-test pass gate

| Option | Description | Selected |
|--------|-------------|----------|
| Subjective operator green-light + screenshot | Boolean PASS/FAIL; matches qualitative "tolerable" criterion. | ✓ |
| Hard latency budget (e.g. <500ms tile-paint, <2s save) | Stopwatch budget. Objective but LDtk has no perf telemetry. | |
| Lag-event log + fail on any UI freeze >1s | ProcessExplorer instrumentation. Overkill for v1.1. | |

**User's choice:** Subjective + screenshot
**Notes:** None.

### Q4 — Artifact location

| Option | Description | Selected |
|--------|-------------|----------|
| `maps/_smoke/synth-8000x6400.ldtk` + `.planning/phases/07-*/SMOKE-NOTES.md` | Establishes `maps/` top-level now; `_smoke/` prefix marks throwaway; notes survive milestone archive. | ✓ |
| Inline in phase dir (no `maps/` yet) | Avoids creating `maps/` before Phase 8. Confusing precedent. | |
| Throwaway `tmp/` dir, not committed | Loses re-runnability + audit trail. | |

**User's choice:** `maps/_smoke/` + phase-dir SMOKE-NOTES.md
**Notes:** None.

---

## Claude's Discretion

User invoked "agent decides skipped areas using best judgment". Decisions captured in CONTEXT.md D-14 through D-21:

**Preflight composition + LDtk version pin** (CONTEXT.md D-12 through D-17):
- Serial, fail-fast (`&&` chain).
- 7-step order: LDtk version pin assert → lint:room-layout → no-inline-origin → trace:check → lint:no-req-placeholders → atlas-hash assertion (no-op when maps/ empty) → conversion-regression stub.
- LDtk version pin = file-only (`tools/ldtk-version.txt`); operator-trust posture (matches 06.7 client-trust).
- Documented MANDATORY in `docs/deploy/LOCAL-DEPLOY.md` as `## Preflight gate (MANDATORY)`.

**HYG-01 broadening** (CONTEXT.md D-18):
- Leave existing `tooling/no-inline-origin.ts` scope unchanged in Phase 7.
- Phase 7 work = wire into preflight only.
- `__pivot` discard rule belongs in Phase 8 alongside `LdtkLoader.ts` (where the temptation exists).

**HYG-02 sweep scope** (CONTEXT.md D-19 through D-21):
- Sweep already clean (grep returned zero hits across `.planning/` + `docs/`).
- Convert one-shot sweep → drift-lock: new `lint:no-req-placeholders` script in preflight.
- Pattern: `REQ-SRV-XX|REQ-X[^-A-Z0-9]|REQ-MAP-XX|REQ-HYG-XX|REQ-[A-Z]+-XX`, exit non-zero on hit.

## Deferred Ideas

- Concrete BNCentral conversion (v1.2+; Phase 7 only locks the chunk-grid convention).
- `__pivot` discard rule (Phase 8 alongside `LdtkLoader.ts`).
- CI restoration / Playwright smoke split (v1.2+; operator-local `flyctl deploy` doctrine stays).
- Hard latency budget / lag-event log for LDtk smoke (v1.2+; subjective gate stays for v1.1).
- Programmatic LDtk-install version introspection (operator-trust posture preferred).
