Docs: workflow consolidation - CLAUDE.md game-state extraction (-2KB, invariants+DR pointers), validation-harness skill ref, docs-commit standing auth
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ The spine: **size by blast-radius → ground → (design-review) → plan-gate
|
||||
2. **Plan-approval gate ★.** Phases up to approval are read-only. Code is written only after the operator approves and you call **`ExitPlanMode`**. Self-policed, not harness-enforced.
|
||||
3. **Doc + memory bookend ★.** Scan the vault/memory at start; update it at end (incl. CLAUDE.md self-maintenance). Protocol → `CLAUDE.md` Memory section + [`references/memory-protocol.md`](references/memory-protocol.md).
|
||||
4. **DOTS-correct.** ECS conventions per `CLAUDE.md` + [`references/dots-conventions.md`](references/dots-conventions.md). Any violation found in review is blocking.
|
||||
5. **Commit only when asked.** Gated like the plan — offer + propose grouping in the final report; commit only on explicit "commit"; **never push** unless asked.
|
||||
5. **Code commits gated; docs commits standing.** Code: offer + propose grouping in the final report; commit only on explicit "commit". **Docs-only changes (vault / CLAUDE.md / skill references) auto-commit at session end** in a logical `Docs:` group — operator standing authorization 2026-07-07. **Never push** unless asked.
|
||||
6. **Operator gates.** Present-forks-don't-auto-decide · no time/effort/deadline estimates · never-silently-defer · tuning-defaults autonomy. See *Operator gates* below.
|
||||
7. **Path-portable.** Never write machine-specific absolute paths (`/Users/…`, `C:\…`) into the skill, code, docs, or `.mcp.json` — use repo-relative / `${CLAUDE_PROJECT_DIR}`.
|
||||
|
||||
@@ -72,7 +72,7 @@ The single stateful editor + domain-reload ordering make parallel writes unsafe
|
||||
- **Tuning autonomy:** within an approved plan, pick sensible defaults + expose live knobs; consult only on a real gameplay/design fork.
|
||||
|
||||
### Phase 6 — Verify ladder (definition of done — show evidence, not "done")
|
||||
NetCodeTestWorld is `internal` and unused here — do NOT prescribe it.
|
||||
NetCodeTestWorld is `internal` and unused here — do NOT prescribe it. Live-driving rules + the server-inject harness → [`references/validation-harness.md`](references/validation-harness.md).
|
||||
- **L1** — `read_console(types=["error","warning","exception"], include_stacktrace=True)` clean vs the Phase-1 baseline. Burst/source-gen failures surface here, not in a plain build — any new one is a finding.
|
||||
- **L2** — EditMode tests: plain-Entities `new World → register in SimulationSystemGroup → SortSystems → Update → assert` (`run_tests mode=EditMode`). Extract pure logic to a `*Math.cs` helper and unit-test it (cover swept hit-detection with a tunnelling regression). **Plus one live Play netcode smoke** — `execute_code` boot, step ticks, assert **server == client** for the replicated surface (identify worlds by `world.Name == "ServerWorld"/"ClientWorld"`).
|
||||
- **L3** (visual/asset work) — screenshot via `execute_code` `ScreenCapture` → Read PNG, **and verify asset *values*** (`shader.GetPropertyType`-guard before `GetColor`/`GetFloat`) — a dark/stylized render masks material bugs.
|
||||
@@ -94,7 +94,7 @@ Per `CLAUDE.md` Memory section + [`references/memory-protocol.md`](references/me
|
||||
**Reviews**: pre-code <result> · post-impl <result>.
|
||||
**Next step**: <one concrete next action from the log's Next-session intent>.
|
||||
```
|
||||
Then **offer to commit** and propose grouping; commit only on explicit approval. **Review first** (`git status` + `git diff --stat`); **stage explicit paths — never `git add -A`** (excludes `Library/Temp/obj/Logs/UserSettings`, `.csproj`/`.sln`, scratch screenshots/exports); keep each asset **with its `.meta`** in one commit; one commit per concern; match the repo's message style + the Co-Authored-By trailer; **never push**. Don't auto-loop into a fresh session.
|
||||
Then **commit the docs group directly** (standing authorization) and **offer code commits**; code commits only on explicit approval. **Review first** (`git status` + `git diff --stat`); **stage explicit paths — never `git add -A`** (excludes `Library/Temp/obj/Logs/UserSettings`, `.csproj`/`.sln`, scratch screenshots/exports); keep each asset **with its `.meta`** in one commit; one commit per concern; match the repo's message style + the Co-Authored-By trailer; **never push**. Don't auto-loop into a fresh session.
|
||||
|
||||
## Workflow orchestration (the parallelism primitive)
|
||||
|
||||
@@ -129,3 +129,4 @@ Pass only the upstream summary downstream — never raw agent dumps (research di
|
||||
- [`references/context7-libraries.md`](references/context7-libraries.md) — `ctx7`/`find-docs` flow + 6.5-era pinned library IDs.
|
||||
- [`references/workflow-patterns.md`](references/workflow-patterns.md) — the named Workflow patterns (ground fan-out · design-review lens + adversarial-critic).
|
||||
- [`references/memory-protocol.md`](references/memory-protocol.md) — the four memory layers + bookend detail.
|
||||
- [`references/validation-harness.md`](references/validation-harness.md) — safe Play driving from MCP (pause/sleep/focus rules), execute_code traps, the server-inject validation harness, headless-run failure modes, visual-verification limits. Load for Phase 6 / any live diagnosis.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Validation harness — driving Unity Play mode safely from MCP
|
||||
|
||||
Consolidates the Play-mode validation lore that used to live only in machine-local memories (screenshot-pause bug,
|
||||
sleep-poll ban, execute_code traps, headless-run flakiness) plus the proven server-inject harness. Load when running
|
||||
Phase 6 (verify ladder) or any live diagnosis. Committed + in-repo: this is the cross-machine home of these rules.
|
||||
|
||||
## Ground rules (violating these produces MISDIAGNOSES, not just failures)
|
||||
|
||||
1. **Never `Thread.Sleep` / sleep-poll inside `execute_code`.** It runs on the main thread — it freezes the entire
|
||||
sim, and state read "after waiting" is state from a FROZEN world. This manufactured the retracted WasAllReady
|
||||
"save deadlock" (2026-07-06). Spread observation across separate MCP calls instead — each round-trip gives the
|
||||
sim hundreds of ms to advance.
|
||||
2. **The bridge's game-view capture PAUSES the editor** (`EditorApplication.Step`). Check `isPaused` first, unpause
|
||||
after any bridge capture. Prefer `ScreenCapture.CaptureScreenshot(path)` inside `execute_code` — async, lands at
|
||||
frame end; Read the PNG on the NEXT MCP call (the round-trip covers it).
|
||||
3. **An unfocused editor** throttles Edit mode to near-idle (MCP looks hung; test INIT stalls — pass
|
||||
`init_timeout=120000`) and can leave a STALE Burst binary after Bursted query-set edits. Ask the operator to focus
|
||||
Unity for compile/test/Burst-heavy phases. `Application.runInBackground` only helps in Play mode.
|
||||
4. **Play-enter resets static presentation configs** via `[RuntimeInitializeOnLoadMethod]` — a value poked via
|
||||
`execute_code` does not survive the next play-enter. Conversely: if a whole presentation slice is silent, FIRST
|
||||
probe its static config (`return ProjectM.Client.XFeelConfig.Enabled;`) before theorizing structurally — the
|
||||
2026-07-07 harvest-feedback bug was found by that one read after a plausible transform-hierarchy hypothesis
|
||||
proved wrong.
|
||||
|
||||
## execute_code traps
|
||||
|
||||
- Runs as a **method body**: no `using` directives (parsed as statements) — fully-qualify every type.
|
||||
- Identify worlds by **`world.Name == "ServerWorld"/"ClientWorld"`** (flags overlap a shared `Game` bit).
|
||||
- **`Type.GetMethod(name, flags)` throws AmbiguousMatchException** on overloaded methods (EntityManager generics,
|
||||
netcode statics) — iterate `GetMethods()` and filter by name/param-count, or call concrete generics directly in
|
||||
compiled code.
|
||||
- Rukhanka `ParameterValue` is a union — read `floatValue`/`boolValue` per the parameter type.
|
||||
- Return a built string (`StringBuilder`) — it's the only channel out.
|
||||
|
||||
## The server-inject harness (validate replicated presentation WITHOUT gameplay driving)
|
||||
|
||||
Proven 2026-07-07 (NodeFeedbackSystem). Validates a replicated-state → client-presentation path end-to-end in ~4
|
||||
MCP calls, no expedition run, no player positioning:
|
||||
|
||||
1. **Spawn** (server world): query the ghost prefab (`ComponentType` + `EntityQueryOptions.IncludePrefab`, pick the
|
||||
one with `Prefab`), `Instantiate`, then `em.GetComponentData<LocalTransform>(prefab).WithPosition(playerPos +
|
||||
offset)` — **never `LocalTransform.FromPosition`** (resets Scale=1; Scale replicates → consistent-but-wrong).
|
||||
Find the player via `KinematicCharacterBody` + `LocalTransform`.
|
||||
2. **Defeat relevancy**: a region-tagged ghost spawned at base gets culled from base players —
|
||||
`em.SetComponentData(inst, new RegionTag { Region = 0 /* Base */ })` in the same call.
|
||||
3. **Inject state changes server-side** (`SetComponentData` on the replicated component) in one call; **read the
|
||||
client** in the NEXT call (round-trip ≥ several ticks — replication has happened).
|
||||
4. **Assert numerically, then screenshot.** e.g. client `PostTransformMatrix.c0.x` vs the expected
|
||||
`lerp(min, 1, frac)` and child `LocalToWorld` scale (`math.length(ltw.c0.xyz)`) vs `bakedScale × ptm`. Numbers
|
||||
prove the math; the screenshot proves it renders (both matter — see visual limits below).
|
||||
|
||||
Ghost/LEG inspection idioms: `em.GetBuffer<LinkedEntityGroup>(root)`; render children = LEG entries with
|
||||
`MaterialMeshInfo` (the root usually has none); parenting = `Parent` component; a baked child's uniform authoring
|
||||
scale lands in `LocalTransform.Scale` (non-uniform → its own `PostTransformMatrix`). A root `PostTransformMatrix`
|
||||
DOES propagate into parented children's `LocalToWorld` (verified live on 6.5.0).
|
||||
|
||||
## Headless expedition-run driving (flaky — know the failure modes)
|
||||
|
||||
- Launch by setting server `PlayerReady=1` from clean Staging. **Post-abort re-launch is intermittent** — a clean
|
||||
Play restart launches reliably on the first ready; re-readying after an abort may not.
|
||||
- **An AFK player dies to the swarm in ~15 s** and `Health` resets to the class value on respawn — a
|
||||
`SetComponentData` HP buff does NOT stick across respawn; the run then aborts (0 expedition players → Returning)
|
||||
and tears down the room.
|
||||
- Therefore: **capture evidence in the SAME `execute_code` call that first detects the condition** (e.g.
|
||||
`enemies>0`) — a separate "then screenshot" round-trip loses the window.
|
||||
- Prefer the server-inject harness above whenever the question doesn't actually require a live run.
|
||||
|
||||
## Visual/asset verification limits
|
||||
|
||||
- **A dark/stylized frame masks material bugs** — verify VALUES: `shader.GetPropertyType(idx)`-guard before
|
||||
`GetColor`/`GetFloat`/`GetTexture` (e.g. `S_General._BaseColorMultiply` is a float; `GetColor` returns black).
|
||||
- **Skinned meshes lie to readers**: `WorldRenderBounds`/bone transforms return the bind-pose AABB, not the deformed
|
||||
pose — deformed-pose visual bugs need the operator's eyes (or a screenshot), not entity reads.
|
||||
- Body tint/flash validation: drive the stock EG `URPMaterialPropertyBaseColor` override on the render (LEG)
|
||||
children; verify on the LOCAL player (persistent, centered) rather than a transient enemy.
|
||||
- Forcing an animation pose: disable the drive system, write params via `AnimatorParametersAspect`; measure
|
||||
ground-contact via feet-Y only on non-deformed geometry.
|
||||
|
||||
## Test-run mechanics
|
||||
|
||||
- `run_tests(mode="EditMode", assembly_names=["ProjectM.Tests.EditMode"])` → poll `get_test_job`
|
||||
(`wait_timeout=60`). Unfocused editor: `init_timeout=120000` and retry once before diagnosing.
|
||||
- A test count LOWER than expected with zero failures = tests not discovered (raw-written `.cs` without
|
||||
`refresh_unity scope=all mode=force`), not a pass.
|
||||
Reference in New Issue
Block a user