DRIFTLANDS co-op survival on drifting tides — studio progress

Architecture Review Report — 2026-09-01

Mode: /architecture-review (full) · Verdict: CONCERNS → all findings applied same session; ADR-0013 + ADR-0014 now ACCEPTED
Engine: Unity 6.3 LTS (6000.3) · URP 2D (Render Graph) · Shader Graph 17.x · NGO 2.x · Input System 1.20 · Addressables 2.x · UI Toolkit
Loaded: 10 ADRs (8 Accepted at review time; ADR-0013 + ADR-0014 reviewed as Proposed and Accepted at the end of this session) · tr-registry.yaml (111 IDs) · traceability-index.md · architecture.md · engine reference (VERSION, breaking-changes, deprecated-apis, current-best-practices, modules) · art bible §4.3/§7/§8 · 7 APPROVED UX specs · docs/registry/architecture.yaml
Scope: incremental over architecture-review-2026-08-28.md. Two new ADRs reviewed in full; the prior review's blocking items re-verified against source, not against the session log.
Method: structured extraction of both new ADRs; an independent cross-ADR conflict pass; two independent engine audits whose verdicts on ADR-0013's load-bearing claim conflicted and were adjudicated by the reviewer against Unity's primary documentation (see Engine Compatibility).


Prior Review's Blocking Items — Verified Closed

Each item was checked against the file, not the changelog.

Item Status Evidence
C1 — single Advance caller✅ADR-0001 §4: "Advance has exactly one caller … HostTickOrchestrator step 1"
C2 — dedup keyed by PlayerKey✅ADR-0002 §8: "the host keeps the last clientSeq per PlayerKey"
C3 — dependency cycle relabel✅ADR-0002 §Amendments: "ADR-0006 moved from Depends On to Related"
E2 — PanelEventHandler sentence✅ADR-0007 §Post-Cutoff (a) carries the corrected mechanism
E4 — wantsToQuit latch✅ADR-0004 amended
E10 — engine-reference banners⚠️ partialnetworking.md + input.md have DEPRECATED — DO NOT COPY banners; rendering.md and ui.md do not
3 GDD doc-sync items✅tide advisory wording removed; save quick-spec has socketCount; land-dock Rule 9 → ADR-0005
ADR-0001–0007 + 0015 → Accepted✅all eight confirmed Accepted

Pre-gate checklist is now fully green (was 4/4 ❌ at the last review): tests/unit/, tests/integration/, .github/workflows/tests.yml, design/accessibility-requirements.md, design/ux/interaction-patterns.md all present.


Traceability Summary

Coverage as assessed at review time, when both new ADRs were still Proposed — credit is granted on Acceptance, not authorship. Both were Accepted later in this session; the final numbers are 73 / 26 / 12 (see Resolution below and traceability-index.md).

Status Count %
✅ Covered6458 %
⚠️ Partial2825 %
❌ Gap1917 %
Total111100 %

Foundation layer: 0 gaps (unchanged).

No new TR-IDs were surfaced by this review. Both ADRs address requirements already registered on 2026-08-28; tr-registry.yaml is therefore unmodified — all 111 IDs keep their numbers, text and revised dates.

Projected coverage on Acceptance of both ADRs (actual outcome: 73 / 26 / 12 — TR-tide-008 was already ✅ and merely shed its remainder, and TR-ui-002 landed ⚠️ not ✅)

Now On Acceptance
✅ Covered6474
⚠️ Partial2825
❌ Gap1912

Both ADRs match their architecture.md Required-ADR charters (rows 13 and 14) exactly — no scope drift in either direction.


Cross-ADR Conflicts

F1 — ADR-0014 vs ADR-0005 · World-space UI atlas · CONCERN — ✅ RESOLVED THIS SESSION

F2 — ADR-0013 vs ADR-0005/ADR-0003 · LandTornDown signature · NOTE

Checked — no conflict

Checked Finding
Settings writer reuse (0014 §2 vs 0004)Legitimate. ADR-0004's atomicity (File.Replace) is separable from F_FULLFSYNC (a macOS-only P/Invoke step) and from backup rotation; ADR-0004 itself says the torn-write guarantee "is independent of this." ADR-0007 §4 set the precedent for bindings.json.
Per-frame TintFloats vs per-tick AdvanceNo conflict — flagged as the sharpest candidate and cleared. ADR-0001 Performance Implications explicitly budgets "per-frame only for tint floats." Advance writes t; TintFloats reads it. caching_evaluate_across_ticks bans caching, so re-reading each frame is the compliant direction.
MapState table (0014 §5 vs 0007 §3)No contradiction. Rows 1–3 match ADR-0007 §3 rules (a)(b)(c) including the Menu+Inventory capture exemption and the next-frame SetUiCaptured timing. The Title row is a genuine addition, correctly filed as an amendment — title-screen.md line 310 already requested it.
Assembly placementBoth legal. TideTintDriver is named verbatim in ADR-0003's Presentation table. ReasonSelector in Rules satisfies formula_in_runtime_assembly; its ReasonState/Step signature is primitives-only, so it compiles in a noEngineReferences assembly.
Per-system ms budgets vs 16.6 ms1.5 ms of 16.6. Registered: tide 0.1 + host-commit-drain 1.0 + save 0.2 + land-lifecycle 0.0 = 1.3; new: ui-panels 0.0 + ui-worldspace 0.2. ui-panels 0.0 follows the land-lifecycle zero-with-rationale precedent.
Draw calls vs < 100≈ +3 total. ADR-0013 build overlay +1 (build mode only); ADR-0014 world-space UI +2 (after F1's correction); tint adds 0 by design.
Dependency graphNo cycles, no unresolved deps. ADR-0013 → 0001/0003/0005; ADR-0014 → 0003/0004/0006/0007 — all Accepted. Mutual independence declared consistently from both sides.
Registry stance collisionsNone. All 11 proposed stance names are new against docs/registry/architecture.yaml.
0013 vs 0014 on world-space spritesConsistent and mutually cross-referenced. Shared sort order with different materials is not a conflict; ADR-0013's V2 already scopes its batch check to DriftLayer for exactly this reason.
State authorityNo overlap. settings.json is per-machine and explicitly excluded from ADR-0004's save (with a new schema-negative test ADR-0004 lacked); ui.json joins ADR-0006's manifest; settings.json correctly stays out of it.

ADR Dependency Order

Foundation (Accepted):
  1. ADR-0001 Tide Scheduler pure assembly
  2. ADR-0002 Host commit model
  3. ADR-0003 Assembly + purity boundaries
  4. ADR-0006 Game data loading + validation
  5. ADR-0004 Save format + reconcile pipeline
  6. ADR-0005 Land lifecycle on Addressables
  7. ADR-0007 Input action map
Core (Accepted):
  8. ADR-0015 Combat model
Presentation (Proposed — this review):
  9. ADR-0013 Tide-tint render pipeline   (← 0001, 0003, 0005)
 10. ADR-0014 UI stack                    (← 0003, 0004, 0006, 0007)
Not yet written:
  ADR-0008 Netcode wire (playtest-gated) → 0009, 0010, 0011, 0012

No cycles. No Proposed ADR depends on another Proposed ADR — both sit cleanly on the Accepted foundation, so either can be Accepted independently of the other.


GDD Revision Flags (Architecture → Design Feedback)

All three are art-bible.md edits already scheduled inside ADR-0013's "Pending on Acceptance" block. They are listed here because the review independently confirmed each against the art bible's own text.

GDD Assumption Reality Action
art-bible.md §4.3 (line 113)"one shader global _TideTint (color) + _TideAmount (float) per land instance"False the moment a second socket exists — tide GDD Rule 4 makes socket phases a permutation of C/N slots, so no two sockets are ever in phase and one frame-global cannot represent both (ADR-0013 §4)Add the N > 1 caveat where an artist would look
art-bible.md §8.B.3 (line 266)"Tint is a global-property read in-shader"True only if the property is declared Global scope; the art bible never states that precondition, and the default (Per Material) makes Shader.SetGlobal* a silent no-op (ADR-0013 §1, as corrected below)Add the precondition
art-bible.md §8.A.4 (line 252)Telegraph tint exemption cited as "combat GDD Rule 11"The requirement is real and binding but lives in the combat GDD's telegraph contract; ADR-0013 inherited the mislabel and self-correctedFix the pointer

No systems-index status changes are required — rows 13 and 14 remain Designed, and neither ADR invalidates a GDD design decision.


Engine Compatibility

Audit Results

Engine: Unity 6.3 LTS (6000.3) · ADRs with an Engine Compatibility section: 10 / 10 · Version agreement: 10 / 10 Deprecated API references: none in binding context. Every hit in the two new ADRs (SetupRenderPasses, AddRenderPasses, AccessibilityNode.selected) is in explicit "deprecated — do not use" or "renamed to" framing. ADR-0014 correctly targets AccessibilityNode.invoked, the 6.3 replacement, not the deprecated .selected. Stale version references: none.

⚠️ E14 — ADR-0013 §1 names the wrong Shader Graph control · BLOCKING (pre-Acceptance)

This finding required adjudication. Two independent engine audits returned opposite verdicts on the load-bearing mechanism of ADR-0013. The reviewer resolved it against Unity's primary documentation rather than accepting either.

What ADR-0013 §1 says: the two globals must be blackboard properties with "Show in Inspector" unchecked, on the reasoning that such a property "is hidden from the Material Inspector (Shader Graph emits [HideInInspector]) and, never being set per-material, falls through the precedence chain to the global."

What the primary sources establish:

Therefore: visibility and declaration scope are two different controls. [HideInInspector] keeps the property in the shader's Properties block, so the material does have it defined, so — by the precedence rule ADR-0013 itself quotes correctly — the material value wins and Shader.SetGlobal* is a silent no-op. This is precisely the failure mode the ADR was written to prevent; §1 currently prescribes it.

The correct mechanism is Scope = Global on the blackboard property. (One audit named this control "Override Property Declaration → Shader Declaration", which is older/source-level terminology; in the pinned Shader Graph 17.x manual the control is "Scope". The substance is identical.)

Knock-on corrections required with the fix:

Why this is not a FAIL-level finding: everything around §1 is right — the precedence rule is quoted correctly, the failure mode is real and correctly rated, the rule-plus-build-failing-test response is the right shape, and V1 is scoped to catch exactly this during an hour-long spike scheduled as migration step 1. The error is narrow: it names the wrong checkbox. It is BLOCKING only because §1 is a binding authoring rule scheduled to be copied into art-bible.md §8.B.3 on Acceptance and cited as the basis of a forbidden registry stance — so an uncorrected §1 propagates a wrong instruction to artists and to the next global anyone adds.

Engine Specialist Findings

# ADR Claim Verdict Severity
E140013 §1"Show in Inspector" unchecked → property receives SetGlobal*INCORRECT — visibility ≠ scope; use Scope = GlobalBLOCKING
E150013 §1Editor test asserts HideInInspector flagWRONG ASSERTION — must assert FindPropertyIndex == -1BLOCKING (with E14)
E160013 §4SpriteRenderer UV2/vertex streams "UNVERIFIED"UNDERSTATES RISK — SpriteRenderer generates its own mesh and exposes no vertex-stream API; the honest framing is "very likely unavailable without a custom mesh"CONCERN
E170014 §10"the map's own name is deliberately not asserted"PARTIALLY CORRECT — true for UI Toolkit's dispatch (it reads the Project Settings UI Actions assignment), but the Input System package docs make the map's name part of the compatibility contract for the default setup. Scope the claim to "because this project explicitly reassigns the UI Actions pointer"CONCERN
—0013Precedence per-instance → material → globalCORRECT (verbatim doc match)—
—0013No Shader.SetGlobalColorArray on 6.3CORRECT — SetGlobalColorArray 404s; only Float/Vector/Matrix array setters exist—
—0013Shader Graph 17.x has no array property typeCORRECT — the only "Array" entry is Texture2DArray—
—0013Globals do not break SRP batching; MPBs doCORRECT — MPBs are named in the incompatibility list; globals sit outside UnityPerMaterial/UnityPerDraw—
—0013 §8Ghost 0 + grid 1 draw callsCORRECT, correctly conditioned on the home atlas and on pre-authored dash frames—
—0014PanelSettings exposes scale-mode fields and a separate uniform scale; runtime settability UNVERIFIEDCORRECT — hedge justified; docs attest the property, not live re-layout—
—0014Built-in scene-independent runtime event system; PanelEventHandler/PanelRaycaster only with an EventSystemCORRECT (verbatim doc match)—
—0014TextMeshPro (mesh) not TextMeshProUGUI for world-space textCORRECT—
—0014RebindingOperation must be Dispose()d on all paths incl. successCORRECT — Unity's own sample disposes only on OnDisable, which is the exact gap the ADR flags—
—0014Strict USS makes an import-every-.uss test a valid CI gateCORRECT—
—0014[SerializeField] is fields-only on 6.3CORRECT—
—0014AccessibilityNode.invoked + non-flags AccessibilityRoleCORROBORATED by the repo's own verified reference docs; V3 compiles itNOTE

ADR-0014 is clean on all of its engine claims (two wording tightenings, no errors). ADR-0013 is clean on seven of eight, with the one exception being its load-bearing mechanism.

Process item (carried, E10): modules/rendering.md still contains a pre-Render-Graph AddRenderPasses(ScriptableRenderer, ref RenderingData) signature and 6000.0 URLs; modules/ui.md is likewise undated for 6.3. Both need the DEPRECATED — DO NOT COPY banner already applied to networking.md and input.md. ADR-0013 §6 flagged rendering.md independently.


Architecture Document Coverage


Verdict: CONCERNS

Both ADRs are well-constructed: dependencies rest entirely on Accepted decisions, the HIGH-risk rendering surface is avoided rather than navigated, and the "Pending on Acceptance" discipline is correct — every amendment target named by both ADRs was verified to exist and to read as claimed, and ADR-0014's "Blocked On" table is honest (confirmed: no ReadHeader/createdUtc/lastPlayedUtc/CreateWorld in ADR-0004, no Ping action in ADR-0007, no ADR-0010).

Off PASS for one engine error in a load-bearing rule (E14/E15) and two wording tightenings (E16, E17), plus carried housekeeping. Not FAIL: no Foundation gap, no cross-ADR contradiction survives (F1 was fixed this session), and E14's fix is a checkbox and an inverted test assertion — it disturbs nothing else in the decision.

✅ Resolution — all findings applied 2026-09-01 (same session)

Every blocking and concern-level finding below was applied and both ADRs were flipped to Accepted. Coverage moved 64 / 28 / 19 → 73 / 26 / 12.

Finding Applied
E14 ADR-0013 §1 wrong controlScope = Global throughout — title, §1 heading/table/rule, Post-Cutoff cell, Verification Required, requirements table, migration steps, registry api_decision, art-bible note. Explicit warning added that Show In Inspector is not the control.
E15 editor test asserted the wrong conditionInverted to FindPropertyIndex(...) == -1 (absence from the property block); noted both APIs are instance methods.
E14b V2 scopeV2 now runs against the Global-scope shader specifically (scope changes UnityPerMaterial layout).
E14c terminology conflationRisk mitigation reworded — names the behaviour, and records that the un-exposed / not-material-serialized conflation was the bug.
E16 SpriteRenderer UV2Sharpened from "unverified" to "likely unavailable — route 3 probably needs a custom mesh renderer"; the spike's question restated.
E17 ADR-0014 map-name claimScoped to "because this project explicitly assigns the UI Actions reference" in both §7 and §10.
F1 UI atlas / draw callsCorrected to the separate non-addressable UI atlas at ≈ +2 draw calls; new V7 marginal-delta test.
F2 SocketId unwrapStated in ADR-0013 §5.

Amendment sets applied on Acceptance (previously held in both ADRs' "Pending on Acceptance" blocks): art-bible.md ×3 (§4.3 N > 1 caveat, §8.B.3 Global-scope precondition, §8.A.4 telegraph citation) · adr-0006 (ui.json in §1 file list, Configs.Ui, stage 9 with ranges as warnings) · adr-0007 §3 (title-screen map state) · accessibility-requirements.md (OQ 1 resolved, new Known Intentional Limitation for world-space text, UI-scaling row annotated) · architecture.md (header, rows 13/14 → Accepted, QQ-06 closed) · UX spec OQ closures (settings 4/5/6/10, hud 7, title-screen 10) · technical-preferences.md ADR log + 4 new forbidden patterns.

Registry stances registered (docs/registry/architecture.yaml, YAML validated): 4 interfaces (tide_tint_driver, settings_service, panel_manager, reason_selector), 2 api decisions (tide_tint_globals, ui_framework_topology), 5 forbidden patterns (mpb_on_driftlayer_sprite, exposed_shader_global_read, non_render_graph_pass, baked_tide_state_variant, ui_event_system_present), 2 budgets (ui-panels 0.0, ui-worldspace 0.2 — total 1.5 ms of 16.6).

Blocking issues — required before ADR-0013 flips to Accepted (all applied — see above)

Required before ADR-0014 flips to Accepted

Required ADRs (unchanged priority)

Pre-gate checklist (checked 2026-09-01)

All pre-gate artifacts are present. /gate-check pre-production is reachable once the two ADRs are Accepted.


History

Date Verdict Covered / Partial / Gap Notes
2026-08-28CONCERNS64 / 28 / 19 of 111First review; 8 ADRs; TR registry v2 created
2026-08-28 (same session)amendments applied—Amendments 1–5 applied; all eight ADRs → Accepted
2026-09-01CONCERNS64 / 28 / 19 of 111ADR-0013 + ADR-0014 reviewed. F1 fixed in-session. E14 BLOCKING — ADR-0013 §1 named the wrong Shader Graph control (Scope = Global, not "Show in Inspector"); adjudicated against primary docs after two engine audits returned opposite verdicts. TR registry unchanged (no new TRs). Pre-gate 4/4 ✅
2026-09-01 (same session)findings applied73 / 26 / 12 of 111E14/E15/E16/E17 + F1/F2 applied; ADR-0013 and ADR-0014 flipped to Accepted; both amendment sets applied across 9 documents; 13 registry stances registered. Remaining 12 gaps are all Core/Feature (ADR-0008–0012) or deliberately deferred.