Cory Policht
← All references
REF · IP-SCP-44-10-SANDBOX-TILE-READ-ONLY · IP

Ip Scp 44 10 Sandbox Tile Read Only

source: Spatial-Cognition-Platform/Epic-S31-Sandbox-Wall-And-Delivery-Lane/IP-SCP-44.10-Sandbox-Tile-Projection-Read-Only.mdscrubbed extract

Scrubbed extract of Spatial-Cognition-Platform/Epic-S31-Sandbox-Wall-And-Delivery-Lane/IP-SCP-44.10-Sandbox-Tile-Projection-Read-Only.md — cited by the Chronicle (07-the-long-march). Redactions: phone / service detail / secrets / clearance / personal / internal hosts.


type: implementation-plan category: Work project: Spatial-Cognition-Platform priority: High status: review pr: 520 created: ‘2026-09-21’ epic: Epic-S31-Sandbox-Wall-And-Delivery-Lane user-story: IP-SCP-44.10 effort: null points: 2 sizing: repos: 1 files: 4 reads: 3 read_files: 3 deliverables: 6 seams: 1 target_repo: spatial-cognition-xr precedence:

  • ‘[[../Epic-S27-Control-Room-Scene-And-Substrate/IP-SCP-40.11-Universal-State-Grammar-Contract|IP-SCP-40.11]]’ seams: [sandbox-tile-projection] parallel_group: lane-core review-profile: standard model: null tokens: 0 verification_class: B verification_rung: R1 needs_unity: false execution_lane: L1-headless stage: ‘3’ tags:
  • implementation-plan
  • control-room
  • sandbox-wall
  • asset-pipeline

IP-SCP-44.10: Sandbox Tile Projection Read-Only

Epic: [[README|Epic S31]] · Stage: 3 · Gate: B/R1 · Points: 2

Mission

The engine-free, read-only projection record for the Sandbox Wall: per-server plate state (health, 1 Hz heartbeat breath phase, panic, backup-freshness value or explicit unknown), per-tile state (capability class ∈ {Rounded reversible, Sharp irreversible, Ringed side-effecting, Hatched env-scoped} resolved from live ToolCapability declarations, never a config fallback, declaration-stale flag, enforcement-level frame value), the spark ledger (tool-call start/finish paired by _meta.correlationId → outbound / at-tile / returning / parked), and container inventory — plus the pinned-or-held filter: sparks only for sessions the operator pinned or is holding, everything else aggregated into a per-plate glow scalar. Caps are model invariants: ≤ 120 tiles (overflow counted, not rendered), ≤ 32 concurrent sparks (past-cap calls fold into the glow), ≤ 12 containers (beyond-count handed to the impostor renderer). Inputs are the S4 projections of FR-AIS-023 (registry + tool-call lifecycle with correlation) and FR-MCP-003 (container inventory + capability class); until an FR lands, its field renders the unknown state — never a guess.

Why

Card 12’s honesty rules — never auto-complete a lost call, never show a stale declaration as current, never spark for an unwatched session — are deterministic presentation logic, and the epic’s trust gates (“class is never a config guess”, “no client-side invented state”) must be R1-proven here, before any renderer exists.

Sizing

Declared table estimate: 2 points; 4 source files (tests/generated artifacts excluded), three concrete bootstrap reads, 6 acceptance boxes, one coherent sandbox-tile-projection boundary. Re-score after concrete file census; do not inflate scope to consume points.

Context Bootstrap

  1. C:\Users\coryp\Developer\spatial-cognition-xr\unity\SpatialCognition\Assets\Scp\Core\Scp.App\CommandArmingMachine.cs — engine-free state-machine precedent (fixed step, pure state transitions, tests) for the spark ledger.
  2. C:\Users\coryp\Developer\spatial-cognition-xr\unity\SpatialCognition\Assets\Scp\Core\Scp.App\Demo\DemoStageLayout.cs — shape of placement tables the projection reads (ControlRoom rows arrive in IP-SCP-40.15).
  3. C:\Users\coryp\Developer\obsidian-sync\Projects\Spatial-Cognition-Platform\Guide-Control-Room-Layout-Constants.md — cadence classes (heartbeats 1 Hz, deltas ≤ 5 Hz) this model budgets against.

Gate Policy

Trigger Gate type Condition
Core asmdef gains an engine reference Trust Stop work — no UnityEngine under Assets/Scp/Core/** (ADR-SCP-010 §1–2).
Any edit to AgentOps scene, kits, prefabs or existing Blender families Trust Refuse — this epic is additive on the ControlRoom scene only.
A figure not in the spec (glow aggregation rate, spark speed default) Confidence Mark tunable; the model exposes the parameter, the gym decides the value.
Budget row SandboxWall ≤ 3k / caps ≤120/32/12 Perf Caps are invariants of this model (renderer can only show what this emits); assert each with an inverted proof.

Deliverables

  • Projection records (plate / tile / spark / container state) created under Scp.Projection — plain deterministic values, no Unity types, no asset references; the class enum carries the four capability classes and nothing resolves a class from config fallback: an unreachable server yields last-known class + stale-declaration flag (inverted proof).
  • Spark ledger pairs lifecycle events by _meta.correlationId; an unmatched start parks forever (hourglass value) — no timer, no auto-complete path anywhere in the code (inverted proof); ≤ 32 concurrent, past-cap calls fold into the plate glow scalar.
  • Pinned-or-held filter reads the operator’s workspace pin state (ADR-SCP-005) and emits per-session spark streams vs aggregate glow accordingly; determinism proven by golden trace at a fixed 1/72 s step.
  • Plate health: breath value derived from the 1 Hz heartbeat cadence; panic flag; backup clock value is a provider-supplied field or an explicit unknown (no synthetic freshness); enforcement-level frame value projected per MCP_ENVIRONMENT state.
  • Container inventory capped at 12 with a beyond-count for the impostor renderer; tile overflow past 120 per class reported as a count, never silently dropped.
  • cd core && dotnet test Scp.Core.sln green with the new suite (class integrity, park-forever, filter correctness, all three caps, unknown-never-guessed); CI R1 green on the PR (ADR-SCP-015 amended: run id is the proof).

Execution Phases

Isolated worktree feat/IP-SCP-44.10-sandbox-tile-projection from BASE (post-Wave-1 main); red test commit, then the feat commit; PR; review. Lane: cloud (L1-headless); depends on IP-SCP-40.11’s grammar/cue contract landing first (wave C2); parallel with 44.11 — the two share only 40.x substrate.

Verification and evidence

  • R1 (CI): ci.yml run 15889, SHA b2273b4 — green.
  • R2-batchmode: n/a — needs_unity: false / R1-only (L1-headless); no Editor launched, no scene/kit/prefab/.inputactions in the diff.
  • Inverted proofs: 10, all in SandboxWallProjectionTests (core/tests/Scp.Projection.Tests/SandboxWallProjectionTests.cs at b2273b4; names extracted by grepping that file for proof, every one a [Fact]). This unit’s convention is the InvertedProof_ prefix, snake-case body.
    • SandboxWallProjectionTests.InvertedProof_no_live_declaration_yields_last_known_class_and_stale_never_a_guess — guard: with liveDeclarations: Array.Empty and a previous tile, the class carries over as SharpIrreversible, DeclarationStale is true and DisplayState is Stale — absence never becomes a guess.
    • SandboxWallProjectionTests.InvertedProof_the_resolver_exposes_no_config_default_parameter — guard: the “class is never a config guess” rule is structural — reflection over every public static Resolve overload asserts no parameter is typed SandboxCapabilityClass or SandboxCapabilityClass?, so no caller can feed one in.
    • SandboxWallProjectionTests.InvertedProof_unknown_backup_freshness_projects_as_unknown_never_a_default — guard: PlateBackupFreshness.Unknown projects IsKnown false, Age null and DisplayState == UnknownValue.
    • SandboxWallProjectionTests.InvertedProof_backup_freshness_rejects_a_computed_or_negative_age — guard: PlateBackupFreshness.Known(TimeSpan.FromSeconds(-1)) throws ArgumentOutOfRangeException — a client-side computed age is not constructible.
    • SandboxWallProjectionTests.InvertedProof_unmatched_parked_start_lives_forever_with_no_timer_or_autocomplete — guard: a parked spark survives five consecutive reads (Assert.Single(ledger.Sparks) each time), and reflection finds no DateTimeOffset/TimeSpan parameter anywhere on SandboxSparkLedger and no method named for Timeout/Expire/Autocompl/Sweep/Prune/Evict/Reap.
    • SandboxWallProjectionTests.InvertedProof_unmatched_finish_invents_no_spark — guard: a Finish for a correlation the ledger never started returns false and leaves Sparks empty and TrackedCount 0 — no phantom completion.
    • SandboxWallProjectionTests.InvertedProof_container_overflow_is_counted_not_silently_dropped — guard: 30 containers render 12 and report BeyondCount 18, with rendered + beyond asserted equal to the 30 inputs.
    • SandboxWallProjectionTests.InvertedProof_watching_the_stray_session_moves_it_out_of_the_glow — guard: watched set and glow are exact complements, never both — pinning all three sessions gives 3 watched and Glow == 0; pinning none gives 0 watched and Glow == 3.
    • SandboxWallProjectionTests.InvertedProof_tile_overflow_past_one_twenty_is_counted_per_class_never_dropped — guard: 125 tiles render 120 with OverflowTileTotal 5 equal to the sum of OverflowTilesByClass, plus a membership check proving every input tile is accounted for exactly once.
    • SandboxWallProjectionTests.InvertedProof_edited_golden_breaks_the_determinism_trace — guard: the golden is load-bearing, not decoration — a one-character tamper ("glow":1 → "glow":2) no longer matches the committed sandbox-wall-determinism.json.
  • Local R2-MCP / R3 / R4: n/a — no Unity, no headset, no device (ADR-SCP-015 §3 unchanged).
  • [[README#Binding contracts|Epic S31 binding contracts]] — mandatory on every dispatch.
  • [[../Feature-Requests/SCFR-025-sandbox-wall-mcp-registry-and-executions|SCFR-025]] — §3B/§3C: read-only, no reasoning objects, the filter is workspace state.
  • Provider surfaces read (projected by S4): FR-AIS-023, FR-MCP-003 (numbering sheet cross-project table).
  • [[../Prompt-Control-Room-Asset-Build|Prompt-Control-Room-Asset-Build]] · [[../Guide-Asset-Pipeline-Process]] · [[IP-Sizing-Rubric]]
  • Predecessor: [[../Epic-S27-Control-Room-Scene-And-Substrate/IP-SCP-40.11-Universal-State-Grammar-Contract|IP-SCP-40.11]] — the state grammar this projection speaks.