diff --git a/.claude/skills/worldos-dev/SKILL.md b/.claude/skills/worldos-dev/SKILL.md index b0432bff..b55e56d7 100644 --- a/.claude/skills/worldos-dev/SKILL.md +++ b/.claude/skills/worldos-dev/SKILL.md @@ -1,6 +1,6 @@ --- name: worldos-dev -description: Develop, test, and QA the WorldOS plugin (the living-world D&D 5e engine). Use when implementing an engine/content/QA change, running or scoring a playtest, delegating a build to a subagent, or resuming the project after a compaction. Encodes the exact dev loop (worktree → additive change → single-process LEXAR pytest → PR → squash-admin merge → prune), the QA loop (duo / combat-sprint runners + the 3 lenses + behavioral gate), and the load-bearing engine invariants. Read WorldOS-RUNBOOK.md first for full context. +description: Develop, test, and QA the WorldOS plugin (the living-world D&D 5e engine). Use when implementing an engine/content/QA change, running or scoring a playtest, delegating a build to a subagent, or resuming the project after a compaction. Encodes the exact dev loop (worktree → additive change → focused single-process pytest → PR → merge → prune), the QA loop (duo / combat-sprint runners + the 3 lenses + behavioral gate), and the load-bearing engine invariants. Read WorldOS-RUNBOOK.md first for full context. --- # WorldOS Dev @@ -29,8 +29,8 @@ universe-system that generates worlds. Source-available commercial product, BG-f touch agents `main`/`operations`. ## THE DEV LOOP (exact) -Test policy: **GitHub-CI-first; local only on LEXAR; SINGLE-PROCESS** (main disk ~4.3 GB -free — parallel workers OOM the host). This repo is on LEXAR, so local single-process is OK. +Test policy: **GitHub-CI-first for broad validation; focused local tests for fast feedback.** +Keep Python tests single-process unless the lane explicitly supports parallel execution. 1. **Worktree off main** (keeps the engine/content/QA lane disjoint from the sibling macOS/OpenWorlds lane). Implement **additively** (honor every invariant). @@ -38,8 +38,8 @@ free — parallel workers OOM the host). This repo is on LEXAR, so local single- ```bash uv run --directory servers/engine python -m pytest -q -p no:xdist ``` - **NEVER pass `-n` / use xdist** — parallel workers OOM the host. Run focused files for - the change; run the full `servers/engine/tests` before merge. + Run focused files for the change; rely on GitHub CI for broad validation unless the + full local suite is explicitly needed. 3. **Push + open the PR** with a HEREDOC body: ```bash gh pr create --title "…" --body "$(cat <<'EOF' @@ -49,8 +49,8 @@ free — parallel workers OOM the host). This repo is on LEXAR, so local single- ``` **DO NOT pipe `gh pr create` through `tail` inside an `&&` chain** — it masks a transient failure and silently skips the merge (bit us on #185). Verify the returned PR URL. -4. **Merge (local-gate):** `gh pr merge --squash --admin` — GitHub Actions is degraded - repo-wide; we gate on local single-process pytest + `license_check`, CI reconciles post-merge. +4. **Merge only after checks pass.** Use the repository's normal PR merge path and treat + local `license_check` + focused pytest as pre-push confidence, not a replacement for CI. 5. **Sync + clean:** ```bash git pull --ff-only origin main diff --git a/.gitignore b/.gitignore index 16fd1e98..c6c56647 100644 --- a/.gitignore +++ b/.gitignore @@ -6,13 +6,15 @@ /play-state/ # QA harness runtime artifacts — sandboxed playtest state, transcripts, logs. -# The harness scripts/rubric/config (qa/*.sh, qa/*.py, qa/*.json, qa/*.txt, qa/*.md) -# ARE committed; only these run outputs are ignored. +# The harness scripts/rubric/config are committed. Local machine overrides and run +# outputs are ignored; the portable MCP template is qa/qa.mcp.example.json. /qa/state/ /qa/state-oc/ /qa/transcripts/ +/qa/runs/ /qa/*.err /qa/smoke*.json +/qa/qa.mcp.json # The on-demand findings ledger (qa/collect_findings.py output) — a regenerable digest of # local QA run scores, derived from the ignored qa/transcripts/. The collector script IS # committed; its output is not. @@ -57,6 +59,18 @@ venv/ .cache/ /state/audio/ +# Local operator / agent artifacts. Public repo guidance should live in tracked docs; +# machine-specific Claude/Codex notes stay local. +/CLAUDE.md +.claude/local.json +.claude/settings.local.json +.claude/*.local.json +.claude.local/ +.aider* +*.har +*.trace +*.tmp + # OS / editor .DS_Store .idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index e1be8353..b4c5695e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ CI-green PRs so nothing broke mid-flight: - **Identity (#296):** plugin id `clawdnd` → **`worldos`**; the macOS app is now `WorldOSApp` / `WorldOS.app`; author `electricsheephq`; repo moved to `github.com/electricsheephq/WorldOS` (prior owner paths may redirect). - README, CLAUDE.md, docs, and the dev skill (`worldos-dev`) rebranded. + README, public docs, and the dev skill (`worldos-dev`) rebranded. - **Code + docs (#299):** ClawDnD → WorldOS across source and documentation; **zero ClawDnD references remain in `servers/engine`**. - **Env compatibility (#300, non-breaking):** a shared resolver now prefers @@ -134,9 +134,9 @@ signatures). longer calls `getcwd()` on an external/removable volume — a kernel-level enumeration that some security scanners hang on `open$NOCANCEL`. `server.py` resolves all of its assets from `__file__`, so the cwd change is transparent to it. -- **Repo can be checked out anywhere.** Verified by cloning into `~/ClawDnD-val` on - internal disk in parallel with the LEXAR worktree; the build + the test suites + the - whole OpenWorlds screen set all run identically from either location. +- **Repo can be checked out anywhere.** Verified by cloning into a clean checkout in + parallel with another worktree; the build + the test suites + the whole OpenWorlds + screen set all run identically from either location. ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 7e09425d..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,105 +0,0 @@ -# WorldOS — working guide (read before editing/running) - -WorldOS simulates living, AI-generated worlds and lets you play epic D&D 5e inside them — -shipped as a Claude Code plugin, with a native macOS app. The flagship world is a -post-Baldur's-Gate-3 5e living world. This file is auto-loaded when working in the repo — it -exists so we never again run the wrong UI or work from the wrong checkout (both have bitten us). - -> Naming note: the project was renamed `ClawDnD` → `WorldOS` (GitHub repo -> `electricsheephq/WorldOS`; prior owner paths may redirect). The product name is -> now **WorldOS**. Some lower-level identifiers are still being migrated in later PRs and remain -> `clawdnd`-named on purpose: the MCP servers (`clawdnd-engine`, `clawdnd-rules`, `clawdnd-voice`, -> `clawdnd-player`), the `CLAWDND_*` env vars, and the `dev.clawdnd.app` bundle id. Treat those as -> the live wire/contract names until their dedicated rename PRs land — don't "fix" them ad hoc. - -## Which checkout am I in? -- **Canonical working checkout: `/Users/lume/ClawDnD-val`** — it tracks GitHub - `electricsheephq/WorldOS` `main`. Edit, run, commit, and verify here unless a - task explicitly asks for a fresh Lexar worktree. -- **`/Volumes/LEXAR/repos/ClawDnD-val` and `/Volumes/LEXAR/repos/ClawDnD` are DEPRECATED** - (the pre-2026-05-28 location). Do not edit or run from them. They're kept only as - fast-forwarded read-only mirrors and are guarded so an accidental run still serves local code. -- If `git remote -v` shows `electricsheephq/WorldOS` but your `pwd` is under `/Volumes/LEXAR/…`, - you're on the deprecated mirror unless the task explicitly requested a fresh Lexar worktree. -- The Claude Code **preview tool roots at the LEXAR path**, so don't trust `preview_start` - blindly — run the viewer yourself (below) from the canonical checkout, or confirm the served - code is current (root must 302→`/openworlds/`; `chrome.jsx` must have zero `traffic-lights`). - -## The UI is OpenWorlds — NOT the root dashboard -- The real, current UI is the **OpenWorlds React SPA at `/openworlds/`** - (`viewer/openworlds/*.jsx`, in-browser Babel, no build step), served by `viewer/server.py`. - The native app `dist/WorldOS.app` loads `/openworlds/`. ("OpenWorlds" is the SPA codename — - a sub-component of WorldOS — and stays.) -- **`http://127.0.0.1:/` (the root) is the LEGACY pre-OpenWorlds dashboard** — it now - redirects to `/openworlds/`. Never treat the root, `viewer/index.html`, or `dashboard.html` - as the current UI. Always verify at `/openworlds/`. - -## Run / verify the viewer -```sh -CLAWDND_STATE_DIR= CLAWDND_REPO_ROOT="$PWD" \ - python3 "$PWD/viewer/server.py" "" # argv: server.py [campaign] [port] -# Pass "" for the campaign to just set a port (a bare numeric arg is read as a CAMPAIGN id, -# not the port, and silently binds the default 8765). Then open /openworlds/ — not the root. -``` -(The `CLAWDND_*` env vars are the current names; a later PR adds `WORLDOS_*` aliases.) -Headless capture: `qa/owshot.sh ` (fresh Chrome profile → no -stale cache). The viewer sends `Cache-Control: no-store` and version-stamps the index scripts, -but a long-lived browser profile can still hold an old copy — use a fresh profile when in doubt. - -## Architecture invariants -- **The engine (`servers/engine/`) is the SOLE writer** of `snapshot.json`. The viewer, the - native app, and the player facade only READ engine read-models and submit intent via - `POST /move`. Never write play-state directly. -- Three MCP servers (`.mcp.json`): engine, rules, voice. - -## Don't repeat these (hard-won) -- **Version-skew:** never `git commit` engine `.py` while a play/QA session is running. A - long-lived DM engine subprocess running older `models.py` than the snapshot writer fails - every tool call with pydantic `extra_forbidden`. Commit engine changes BEFORE starting a - session. Viewer/.jsx, qa/, content/, docs/, skills/, macos/ are session-safe. -- **One checkout:** a stale second checkout silently serving old code is the #1 cause of - "the fixes aren't there / wrong UI." Keep the LEXAR mirror fast-forwarded after every push. - -## Testing characters -Use CANON BG NPCs from the 2076-record pool (e.g. `dal-lightspark`) — they have real ingested -portraits. NOT the 7 BG3 origin heroes (overpowered), and NOT custom PCs. A character with no -ingested face shows a neutral silhouette — never a class/race heraldic crest. - -## Tests -Engine: `uv run --directory servers/engine python -m pytest tests/ -q` (single-process, ~15s, -1400+ tests). Viewer: `uv run --directory viewer pytest -q tests/`. Prefer GitHub CI for the -full sweep when the main disk is tight; engine single-process locally is light. - ---- - -## Project map — read order + where everything lives -An agent resuming this project should read in this order, then consult the rest by need: - -1. **`CLAUDE.md`** (this file) — checkout/UI/run guardrails + this map. Auto-loaded. -2. **`WorldOS-RUNBOOK.md`** — the compaction-resilience doc: architecture, invariants, dev/QA loops, lessons, work queue. READ FIRST after this. -3. **`WorldOS-NORTH-STAR.md`** — *what "great" means* (the optimization target). The score is a **proxy** for the felt prestige-CRPG session; come here when score and gut disagree. -4. **`qa/SCORECARD.md`** — the running results ledger (every scored run). -5. Latest **session runbook**: `/Volumes/LEXAR/Codex/session-notes//clawdnd-*/` (`implementation-notes.html` or `runbook.html`) — the in-flight day-log. Current sprint: `2026-05-27/clawdnd-1.0-autonomous-sprint/runbook.html`. - -### How we measure (the fitness function — still the gate) -Defined in **`qa/SCORING.md`**. A run = **1 hard behavioral gate** + **3 LLM lenses (1–5)**: -- **Behavioral gate** (`qa/assert_behavioral.py`) — deterministic PASS/FAIL: dice rolled, clock advanced, party visited ≥2 locations, new NPC met, player moves resolved, no dangling combat/conditions. RED caps the lenses. -- **Mechanical** lens → `qa/rubric.md` (5e/rules/tool-fidelity). **Target ≥ 4.5.** -- **Story-craft / "Tolkien"** lens → `qa/rubric_tolkien.md` (grandeur/character/prose/momentum/theme; act-aware). **Target ≥ 4.3** (enduring North Star 4.5). -- **Combat / "Angry-DM"** lens → `qa/rubric_angry_dm.md` (5e combat fidelity). Drive upward; the engine core is clean, residual is DM adherence + sampling. -- **OpenWorlds app sweep** adds: image-render ≥95% + button-coverage ≥95% + zero console errors (`qa/screen_coverage.py`, `qa/owshot.sh`). -- Scorer: `qa/score.sh` (+ `qa/SCORING.md` for the schemas). **Log every scored run to `qa/SCORECARD.md`.** - -### Run a session / sweep -- 2-agent duo (DM + constrained AI player): `qa/run_duo.sh baldurs-gate qa/play_player_openworlds.txt ` (player persona = a CANON BG NPC, e.g. Dal Lightspark). -- Companions ensemble: `qa/run_party.sh`. Combat fidelity lane: `qa/run_combat_sprint.sh`. Parallel: `qa/run_parallel.sh`. -- All scored runs land in `qa/SCORECARD.md`. - -### The skills the DM/companions run -`skills/dungeon-master/` (`SKILL.md` = beat cycle + non-negotiables; `AGENT.md` = DM identity + 3-act process; `reference/*.md` = combat, storycraft, living-world, living-arcs, quest-generation, death-and-reroll). Plus `skills/companion/`, `skills/campaign-author/`, `skills/world-author/`. - -### Design / architecture docs (`docs/`) -`ARCHITECTURE.md` · `OPENWORLDS_NATIVE_APP_ROADMAP.md` · `OPENWORLDS_UI_AUDIT.md` (the page-by-page audit; GitHub epic #242) · `OPENWORLDS_FIDELITY_PLAN.md` · `OPENWORLDS_DESIGN_ASSET_POLICY.md` (official images → gitignored `_private/`, never committed) · `RELEASE_1.0_CHECKLIST.md` · `MONSTER_AUTHORING.md` · `SPARKLE_SETUP.md`. - -### Content + ingest -World seeds + lore: `content/worlds/baldurs-gate/` (canon characters, areas, endings, `lore/` + `lore/wiki/`). Wiki-first ingest pipeline: `tools/ingest/` (`wiki_fetch.py` → `wiki_to_{lore,characters,areas}.py`, `wiki_images.py` → gitignored `content/worlds/_private/`). Direction: pull from the BG3/FR wikis, generate little (see memory `clawdnd-wiki-first`). diff --git a/WorldOS-RUNBOOK.md b/WorldOS-RUNBOOK.md index 793f6678..d56f23ca 100644 --- a/WorldOS-RUNBOOK.md +++ b/WorldOS-RUNBOOK.md @@ -1,24 +1,18 @@ # WorldOS — RUNBOOK (READ FIRST on resume) -> **Before anything: confirm you are in the canonical checkout and know the right UI.** -> `CLAUDE.md` (repo root, auto-loaded) is the hub — canonical checkout is -> `/Users/lume/ClawDnD-val` (== GitHub `electricsheephq/WorldOS` main; the `/Volumes/LEXAR/…` -> copies are DEPRECATED), the UI is OpenWorlds at `/openworlds/` (the root is the legacy -> dashboard), and it carries the full project map + read order. See also the auto-memory -> `clawdnd-canonical-setup`. (Product renamed ClawDnD → WorldOS; the checkout dir keeps its -> `ClawDnD-val` name for now per issue #295/W0-F.) +> **Before anything: confirm you are in the repo root and know the right UI.** +> The current product UI is OpenWorlds at `/openworlds/`; the root dashboard is +> legacy. This runbook is the public project map + read order. Machine-specific +> agent notes such as `CLAUDE.md` are intentionally local-only and gitignored. > **This is the compaction-resilience doc.** If you are an agent resuming this project > after a context reset, read this top-to-bottom before doing anything. It captures the > project, architecture, the load-bearing invariants you must not violate, the exact dev > + QA loops, how to delegate, the hard-won lessons, and the current state + work queue. > -> Companion artifacts (the live day-log + decision records) live at -> `/Volumes/LEXAR/Codex/session-notes/2026-05-26/clawdnd-overnight-qa/`: -> `implementation-notes.html` (running log — read it for the latest in-flight state), -> `decision-quest-arc-engine.md`, `decision-143-variant-matrices.md`, -> `decision-campaign-director.md`, `decision-event-parley.md`, `insights-encounter-balance.md`. > The QA results ledger is `qa/SCORECARD.md`; the scoring spec is `qa/SCORING.md`. +> If an operator hands you local session notes or decision records, treat them as +> private working artifacts unless they are intentionally promoted into tracked docs. > > Last updated: 2026-05-28 (post-v1.0.1). > @@ -135,14 +129,13 @@ change must respect them. ## THE DEV LOOP (exact) -> **Test-execution policy (from `~/.claude/CLAUDE.md`): GitHub-CI-first; local only on -> LEXAR; SINGLE-PROCESS.** Main disk (`/`) has ~4.3 GB free and parallel test workers have -> OOM-ed the host. This repo (`/Volumes/LEXAR/repos/ClawDnD*`) is on LEXAR, so local -> single-process pytest is OK. **NEVER run with `-n` / xdist — parallel workers OOM the host.** +> **Test-execution policy:** prefer GitHub CI for broad validation. For local +> development, run focused tests first and keep Python test runs single-process unless +> you have explicitly verified your machine can handle parallel workers. 1. **Branch off main in a fresh worktree** (keeps lanes disjoint from the sibling desktop work). Implement **additively** (honor every invariant above). -2. **Single-process test on LEXAR:** +2. **Run focused local tests single-process:** ```bash uv run --directory servers/engine python -m pytest -q -p no:xdist ``` @@ -161,13 +154,8 @@ change must respect them. **DO NOT pipe `gh pr create` through `tail` inside an `&&` chain** — a transient GraphQL blip gets masked, the branch ends up pushed-but-unmerged, and the merge is silently skipped. (This bit us on #185.) Check the exit / the returned PR URL. -4. **Merge (local-gate):** - ```bash - gh pr merge --squash --admin - ``` - GitHub Actions went **degraded repo-wide** (~2026-05-26), so we gate on **local - single-process pytest + `license_check`** per the test policy; CI reconciles - post-merge. `--admin` is intentional here for that reason. +4. **Merge only after checks pass.** Use the standard PR merge flow once GitHub CI + and required review gates are green. 5. **Sync + clean up:** ```bash git pull --ff-only origin main @@ -176,8 +164,8 @@ change must respect them. git worktree prune ``` -**The whole shape:** worktree off main → implement additive → single-process LEXAR test → -push → `gh pr create` (no `tail` in an `&&` chain) → `gh pr merge --squash --admin` → +**The whole shape:** worktree off main → implement additive → focused single-process test → +push → `gh pr create` (no `tail` in an `&&` chain) → merge after checks pass → `git pull --ff-only origin main` → remove worktree + delete branch + prune. --- @@ -238,7 +226,7 @@ Orchestrate via subagents; verify from the top. 1. **Create a fresh worktree** off main for the agent's lane. 2. **Spawn an agent** (Agent tool / `claude -p`) with: a **precise spec**, the - **single-process-test guardrail** (`-p no:xdist`, never `-n`, LEXAR only), and the + **single-process-test guardrail** (`-p no:xdist`, never `-n` unless the lane explicitly supports it), and the directive **"flag-don't-force-fix if many tests break"** (don't let an agent weaken assertions or paper over a real regression to make a suite go green). 3. **Review the diff** — *trust BUT verify*. Read what the agent actually changed; confirm @@ -268,7 +256,7 @@ out freely. Only **`claude -p` QA is host-heavy** (the duo/sprint spin up engine after #181). Use it deliberately. - **First-principles for load-bearing decisions.** A public contract / schema / tool API / concurrency change with real trade-offs = run the research/decision loop and write a - decision doc (see the four in the session-notes dir), not speculative code. + decision record, not speculative code. - **Don't reflexively import a gate from one code path into another** — ask "in which domain is this concern real?" first (e.g. apply_damage is *legit* for traps/poison, so a blanket "reject apply_damage" rule was wrong). @@ -393,8 +381,8 @@ lands on app relaunch with NO Swift rebuild** (the swift build is a ~0.1s no-op) --- ## RESUME CHECKLIST -1. Read this file, then `implementation-notes.html` (latest in-flight state) + `qa/SCORECARD.md`. -2. `cd /Volumes/LEXAR/repos/ClawDnD` (or the relevant worktree); `git log --oneline -10`. +1. Read this file, then any handed-off local session notes + `qa/SCORECARD.md`. +2. `cd` to the repo checkout or relevant worktree; `git log --oneline -10`. 3. `gh pr list` + `gh issue list` to see open lanes (avoid the desktop-lane PRs above). -4. Pick up the Quest-Arc L3 build / the queued waves; build → single-process LEXAR test → - PR (no `tail` in `&&`) → `--squash --admin` → sync + prune. Log every QA run to SCORECARD. +4. Pick up the Quest-Arc L3 build / the queued waves; build → focused single-process test → + PR (no `tail` in `&&`) → merge after checks pass → sync + prune. Log every QA run to SCORECARD. diff --git a/docs/PRIVATE_COMPENDIUM_SIDECAR.md b/docs/PRIVATE_COMPENDIUM_SIDECAR.md index 3acc5f3d..d8eb420a 100644 --- a/docs/PRIVATE_COMPENDIUM_SIDECAR.md +++ b/docs/PRIVATE_COMPENDIUM_SIDECAR.md @@ -13,8 +13,8 @@ generated corpora to the repository. ## Boundaries - Keep the sidecar outside the repo. The default is - `/Volumes/LEXAR/Codex/clawdnd-private-compendium`. -- Override the sidecar root with `CLAWDND_PRIVATE_COMPENDIUM_ROOT` when needed. + `~/.worldos/private-compendium`. +- Override the sidecar root with `WORLDOS_PRIVATE_COMPENDIUM_ROOT` when needed. - Treat `content/worlds/_private//...` as the only future WorldOS output namespace for private world material. - Do not mutate campaign state, engine state, or public `content/worlds/*` content from @@ -33,7 +33,7 @@ python3 tools/ingest/private_compendium_sidecar.py --init By default this writes: ```text -/Volumes/LEXAR/Codex/clawdnd-private-compendium/private-compendium-manifest.json +~/.worldos/private-compendium/private-compendium-manifest.json ``` Example manifest: diff --git a/docs/ui-audit/MASTER_TRACKER.md b/docs/ui-audit/MASTER_TRACKER.md index 4a041382..d29a9b08 100644 --- a/docs/ui-audit/MASTER_TRACKER.md +++ b/docs/ui-audit/MASTER_TRACKER.md @@ -53,7 +53,7 @@ Attacked Loop-2 closeout gaps. Confidence rises **to ~90%.** Pushed against the asymptote. Confidence rises **to ~95%.** See [`docs/ui-audit/MAINTAIN.md`](MAINTAIN.md) for the honest "100% on a moving target" framing. -- **Live-session UX (evidence-at-rest).** Inspected `/Volumes/LEXAR/Codex/clawdnd-artproof-state/campaigns/camp_54fd704d985b/snapshot.json` — the saved engine state for the canonical BG campaign. **`combat.active = true`** at rest with full schema: `round=1`, `turn_index=2`, `order=[char_149e22788290 init 13 reaction_used=true, char_3d2a73f8f833 init 7, char_d710006ae7e0 init 6]`, `action_used=true`, `action_attacks_made=1`, `bonus_action_used=false`, `surge_actions=0`, `zones=[]`. **The live-combat state shape is documented.** When a DM provider attaches and the action bar / initiative tracker / battle log render against this state, the UI's `screen-combat.jsx:75-93` consumers (`tokens / initiative / actionBar / zones / battleLog / encounter / commandCenter / economy`) all have observable shapes to validate against. Live-session UX is no longer structurally invisible — only the operator-driven walk-through remains. +- **Live-session UX (evidence-at-rest).** Inspected a local saved engine snapshot from the canonical BG campaign. **`combat.active = true`** at rest with full schema: `round=1`, `turn_index=2`, `order=[char_149e22788290 init 13 reaction_used=true, char_3d2a73f8f833 init 7, char_d710006ae7e0 init 6]`, `action_used=true`, `action_attacks_made=1`, `bonus_action_used=false`, `surge_actions=0`, `zones=[]`. **The live-combat state shape is documented.** When a DM provider attaches and the action bar / initiative tracker / battle log render against this state, the UI's `screen-combat.jsx:75-93` consumers (`tokens / initiative / actionBar / zones / battleLog / encounter / commandCenter / economy`) all have observable shapes to validate against. Live-session UX is no longer structurally invisible — only the operator-driven walk-through remains. - **Native macOS app — title-bar interaction clarified.** Inspected `macos/WorldOSApp/Sources/WorldOSApp/Views/RootView.swift:348-398`. The Swift `OpenWorldsChromeHostView` enables `titlebarAppearsTransparent + fullSizeContentView` AND `unhides` all three standard window buttons (close/min/zoom). The `paddingLeft: 76` in `chrome.jsx:422` exists **specifically to clear the macOS native traffic lights**. #260's acceptance criteria need to be **platform-aware**: keep 76px in native, drop/shrink in browser. Filed as a clarification comment on #260. - **Interactive coverage code-verified.** Keyboard map (`app.jsx:197-220`): 17 shortcuts (t/x/p/m/c/i/f/r/j/b/a/$/w/n/s/,/?). Palette (`styles.css`): 2 alt-palette blocks (`cool`, `dark`) + default warm. A11y (`styles.css:849-892`): `[data-reduced-motion=on]` universal animation-duration zeroing + `[data-contrast=high]` panel/btn/window/parchment chrome overrides + `--ui-scale` via `zoom` on `.window`. Create wizard (`screen-create.jsx:19-27, 133-139, 572-576`): 7 steps + 5e-canonical point-buy `abilityCost = {8:0, 9:1, 10:2, 11:3, 12:4, 13:5, 14:7, 15:9}`. All wiring matches the audit's framework reading. - **`dist/WorldOS.app` exists locally.** The native app has been built. Operator can run it for the visual walk that Loop 4 can't do artifact-only. @@ -339,8 +339,8 @@ docs/ui-audit/ ## Session reference -This audit was produced 2026-05-29 in one session as part of issue [#242](https://github.com/electricsheephq/WorldOS/issues/242) Phase 5. Session notes live at `/Volumes/LEXAR/Codex/session-notes/2026-05-29/openworlds-ui-ux-review/implementation-notes.html` (per the user-level guidance to keep a per-session scratchpad for substantial / state-changing sessions). +This audit was produced 2026-05-29 in one session as part of issue [#242](https://github.com/electricsheephq/WorldOS/issues/242) Phase 5. Local session notes were kept outside the repository; the tracked issue and docs are the public record. -The audit was authored against the canonical checkout `/Users/lume/ClawDnD-val` (per `clawdnd-canonical-setup` memory), git rev `227ff3227010820b2df2e5692dc77505ef7f980e`. The viewer was served via both `/Volumes/LEXAR/repos/ClawDnD-val` (port 8799, content-identical mirror) and `/Users/lume/ClawDnD-val` (port 8795); both were verified at the same revision. +The audit was authored against git rev `227ff3227010820b2df2e5692dc77505ef7f980e`. The viewer was served from local checkouts on ports 8799 and 8795; both were verified at the same revision. The audit does NOT touch product code — see the goal directive. The implementation agent is the appropriate owner for any code changes; this audit hands them everything they need to start. diff --git a/docs/ui-audit/screens/character.md b/docs/ui-audit/screens/character.md index 099a4f87..49a7c92b 100644 --- a/docs/ui-audit/screens/character.md +++ b/docs/ui-audit/screens/character.md @@ -51,7 +51,7 @@ - **Item icons** for canonical 5e gear in `_private/baldurs-gate/items/.png`: longsword, mace, dagger, shortbow, longbow, hand crossbow, light/medium/heavy armor (leather/studded/scale/chain/plate), shield, helmet, cloak, ring, amulet, potion-of-healing, … reuse via inventory's `itemScope`. - **Ability/Feat glyphs** in the icon registry (#174 already tracks): Lay on Hands, Channel Divinity, Sneak Attack, Bardic Inspiration, Sorcerer Metamagic, Pact Boon, etc. -- **Class portraits** are NOT actually needed here — header uses portrait-, not class crest (per `clawdnd-canonical-setup` invariant). +- **Class portraits** are NOT actually needed here — header uses portrait-, not class crest. ## Recommended next pass diff --git a/docs/ui-audit/screens/launcher.md b/docs/ui-audit/screens/launcher.md index daa7ae37..4c1e3c4f 100644 --- a/docs/ui-audit/screens/launcher.md +++ b/docs/ui-audit/screens/launcher.md @@ -48,7 +48,7 @@ ## Asset gaps (wiki-first inventory) - **Chronicle cover scenes** — need `_private//scenes/cover-.jpg` OR reuse `location:` as the chronicle thumbnail. -- **Hero (Caelar) portrait** — Caelar is a custom test PC; per `clawdnd-canonical-setup` memory, a faceless custom PC must show the silhouette, never a class crest. Behavior is correct ✓. Provide a portrait pipeline for created characters (gateway-gen vs default; see EPIC A discussion in `#242`). +- **Hero (Caelar) portrait** — Caelar is a custom test PC; a faceless custom PC must show the silhouette, never a class crest. Behavior is correct ✓. Provide a portrait pipeline for created characters (gateway-gen vs default; see EPIC A discussion in `#242`). ## Recommended next pass diff --git a/docs/ui-audit/screens/table.md b/docs/ui-audit/screens/table.md index c4431df5..2e6ad28f 100644 --- a/docs/ui-audit/screens/table.md +++ b/docs/ui-audit/screens/table.md @@ -28,7 +28,7 @@ | # | Severity | Title | File:line | Epic | Acceptance criteria | |---|---|---|---|---|---| | T-01 | **Critical** | Title-bar text overlaps nav-rail at top-left | `chrome.jsx:415-432` | epic:per-page-polish | (Cross-cutting; see L-01 in `screens/launcher.md`.) | -| T-02 | **Major** | Caelar portrait is a silhouette across PartyRow + hero card | `screen-table.jsx:439`, `chrome.jsx:225-235` | epic:portraits | When the PC is custom-created (`bindHero`), the portrait pipeline writes `portrait-.png` to the served image store. Until then, silhouette is the correct fallback (per `clawdnd-canonical-setup`). This is a wait-on-EPIC-A item. | +| T-02 | **Major** | Caelar portrait is a silhouette across PartyRow + hero card | `screen-table.jsx:439`, `chrome.jsx:225-235` | epic:portraits | When the PC is custom-created (`bindHero`), the portrait pipeline writes `portrait-.png` to the served image store. Until then, silhouette is the correct fallback for a faceless custom PC. This is a wait-on-EPIC-A item. | | T-03 | **Major** | Recent-Events stream mixes engine-recent + chat-tail + local log in display order | `screen-table.jsx:44` | epic:per-page-polish | `visibleLog = [...recentEvents, ...chatBeats, ...log]` — concatenation, not time-merged. Should sort by an authoritative timestamp so a player who acts mid-poll doesn't see their own action above older DM prose. Add `entry.at` (engine seq#) and stable-sort by it before rendering. | | T-04 | **Major** | Encounter panel actions show full label even when disabled — disabled_reason in `hand muted` is small | `screen-table.jsx:381-389` | epic:per-page-polish | When `!a.available`, the row prefixes the label with the disabled glyph (`inventory.locked`) ✓ but the disabled_reason copy is `hand muted` (line 384) — bump to a `body-xs` warning tone for legibility. Verify against actual disabled actions during a paused combat. | | T-05 | **Minor** | GM Advisory shows ONLY the first debt (line 313-327) — total debt count is visible but extras hidden | `screen-table.jsx:311-332` | epic:per-page-polish | Either render up to 3 debts (mirroring `screen-journal.jsx`) or add a "+N more debts" chip that opens the Journal Advisory section. | diff --git a/docs/ui-audit/screenshots/README.md b/docs/ui-audit/screenshots/README.md index fc948657..0b76a713 100644 --- a/docs/ui-audit/screenshots/README.md +++ b/docs/ui-audit/screenshots/README.md @@ -12,9 +12,9 @@ commit; the same logic applies to derivative screenshots that embed them. ## How to regenerate locally ```sh -# From the canonical repo root (/Users/lume/ClawDnD-val). +# From the repo root. # Viewer must be running (default port 8799): -CLAWDND_STATE_DIR=/tmp/clawdnd-audit-state CLAWDND_REPO_ROOT="$PWD" \ +CLAWDND_STATE_DIR="$(mktemp -d)" CLAWDND_REPO_ROOT="$PWD" \ python3 viewer/server.py "" 8799 & # Then capture all 16 screens: diff --git a/qa/SCORECARD.md b/qa/SCORECARD.md index fb650f85..6e2f7bde 100644 --- a/qa/SCORECARD.md +++ b/qa/SCORECARD.md @@ -27,7 +27,7 @@ | duo-wired1 | 2026-05-27 | baldurs-gate | wayfarer | sonnet | 10 | GREEN | **4.2** | 3.7 | 2.9 | **POST-wiring — story-craft 4.0→4.2** (the living-story wiring #203 lifted it: rule-of-three + events + the DM consulting present_events/check_companion_arc). **0.1 off the 4.3 target.** round1_turn_skipped PASS (#183 holding), world_peopled PASS (new NPC met). mech 3.7 / angry 2.9 = combat-light wayfarer (sampling). NEW soft WARN `action_economy_engaged`: start_combat fired but no action consumed (a minimal wander-combat resolved narratively — coverage/adherence signal, not fatal). **The wiring moves the story north-star.** | | sprint-enriched2 | 2026-05-27 | baldurs-gate | (combat-sprint, enriched) | sonnet | 1 fight | GREEN | — | — | 3.4 | **#209 repeat-save VALIDATED** — verdict: "the save-ends paralysis wiring is a genuine bright spot" (Hold Person lockdown worst-seam GONE; core loop init/to-hit/adv/crit/conditions runs clean). angry-dm HELD at 3.4 (not climbed) — the SAME recurring HIGH defects cap it: **#211 Ghoul Multiattack composition** (DM ran Bite+Claw not 2 Bites — partly the prompt conflating the Claw's paralysis with the Multiattack), **#210 Ghoul necrotic dropped** (attack() single-damage), + Battle Master superiority die never rolled/added (→ filed). Verdict: fix Multiattack composition + the maneuver die first — repeatable every session, corrupt the benchmark until closed. Next: monster-attack fidelity wave (#210+#211) + the maneuver-die fix. | | sprint-postmaneuver | 2026-05-27 | baldurs-gate | (combat-sprint, enriched) | sonnet | 1 fight | GREEN | — | — | **3.7** | **NEW SPRINT HIGH (3.4→3.7) — #215 maneuver-die VALIDATED in live play.** Verdict: "the paralysis and Battle Master maneuver paths are clean — condition-triggered auto-crits fired, save-ends wired correctly, **every narrated damage number traces to a tool call**." combat_resolution 3→4. The 3 recurring HIGH engine defects (Multiattack composition #211, necrotic #210, maneuver die #213) are **GONE from the verdict** — all closed. tally: attack×14 (multiattack), use_resource×2 (maneuvers fired), saving_throw×4, cast_spell×3, add_condition. NEW defects are lower-severity: [HIGH] Bandit Captain **Parry reaction** never called (DM adherence, not engine) → skill nudge + assert; [MED] repeat-saves auto-fire (#209) but **aren't surfaced in next_turn JSON** for audit (real small engine-surfacing nit → follow-up); [MED] point-blank ranged disadvantage (adherence); [LOW] crit narrated "nat 20" when it was paralyzed-auto-crit (attribution → crit_source field). The engine combat core is now clean; residual is DM adherence + auditability. | -| ow-fix-011115 | 2026-05-29 | baldurs-gate | Dal Lightspark (canon Harper wizard) | sonnet | 8 | **GREEN** | **4.1** | **3.8** | **3.4** | **★ VERSION-SKEW FIX VALIDATED.** First fully-clean OpenWorlds-persona run after re-rooting the DM engine to the live checkout (qa.mcp.json had pointed at a stale LEXAR tree → every tool call failed extra_forbidden → 2 prior RED-capped runs). All 17 behavioral gates PASS incl. the previously-failing party_traveled + world_peopled, plus dice_used + world_advanced_time (→ day 3). Below the 4.3/4.5 bars — social-only 8-beat sample. Triage found 2 real mechanical defects → FIXED: (1) caster PC shipped with an EMPTY spellbook (no spells to cast → mech/angry capped) → `_seed_starting_spells` (commit d9f1d44, engine 1404→1405); (2) `xp:0` in xp-mode (no milestone XP) → DM-skill nudge (commit 8555ca7). Also MED: recruited companion `companion_dossier` arrays empty (recruit_companion should copy canon dossier — queued). | +| ow-fix-011115 | 2026-05-29 | baldurs-gate | Dal Lightspark (canon Harper wizard) | sonnet | 8 | **GREEN** | **4.1** | **3.8** | **3.4** | **★ VERSION-SKEW FIX VALIDATED.** First fully-clean OpenWorlds-persona run after re-rooting the DM engine to the live checkout (the old local MCP template had pointed at a stale sibling tree → every tool call failed extra_forbidden → 2 prior RED-capped runs). All 17 behavioral gates PASS incl. the previously-failing party_traveled + world_peopled, plus dice_used + world_advanced_time (→ day 3). Below the 4.3/4.5 bars — social-only 8-beat sample. Triage found 2 real mechanical defects → FIXED: (1) caster PC shipped with an EMPTY spellbook (no spells to cast → mech/angry capped) → `_seed_starting_spells` (commit d9f1d44, engine 1404→1405); (2) `xp:0` in xp-mode (no milestone XP) → DM-skill nudge (commit 8555ca7). Also MED: recruited companion `companion_dossier` arrays empty (recruit_companion should copy canon dossier — queued). | | ow-spell-022355 | 2026-05-29 | baldurs-gate | Dal Lightspark (canon) | sonnet | 8 | **GREEN** | **3.9** | **3.9** | **3.9** | **Spellbook fix VALIDATED on combat-fidelity: angry-dm 3.4→3.9 (+0.5)** — tool_fidelity 5, "the spell slot math holds, every check traces to a tool" (Dal cast from the seeded loadout). Mech 3.8→3.9. Story 4.1→3.9 (sampling variance, N=1). Still social-only (zero combat again) → angry-dm capped: combat_resolution/conditions/coverage all =2 ("this run didn't touch an attack roll, a saving throw"). NEW defects: [HIGH] companion `location_id` not updated on travel (Wyll stuck at checkpoint while party moved) — engine/DM state-integrity; [MED] quest `completed_objectives=[]` despite achieving the objective (DM adherence/tracking); [LOW] **milestone XP STILL 0 — the DM-skill nudge (8555ca7) did NOT fire → XP needs an ENGINE auto-award on objective completion, not a prompt.** Next levers: (a) engine milestone-XP + companion-location-on-travel (mech→4.5); (b) get COMBAT into sessions (combat-sprint lane or stronger trigger) — angry-dm can't exceed ~3.9 without a fight. | | ow-combat-031717 | 2026-05-29 | baldurs-gate | Dal (canon) | sonnet | 8 | GREEN | 4.1 | 3.9 | 3.3 | Forced-combat persona EXPERIMENT — FAILED: the AI player ignored the "must fight by midpoint" directive (5 social checks, 0 attack/cast; DM staged no fight) → angry-dm fell. Confirms emergent OpenWorlds play is intrinsically social; combat fidelity belongs to the sprint lane, not the duo. Persona reverted to natural (35662ec). | | ow-cs2-040914 | 2026-05-29 | baldurs-gate | (combat-sprint, pre-seeded) | sonnet | 1 fight | GREEN | — | — | 3.0 | Honest angry-dm via the proper vehicle. combat_resolution 4 ("every number traces to a tool"), but capped by DM combat-ADHERENCE: ranged-in-melee without disadvantage, Parry reaction unused, Second Wind unused, early end_combat. Engine core clean; residual is the DM not USING the engine's combat options. | diff --git a/qa/play_human.sh b/qa/play_human.sh index ae9f17d7..5b89691a 100755 --- a/qa/play_human.sh +++ b/qa/play_human.sh @@ -22,7 +22,7 @@ mkdir -p "$T" "$STATE_DIR"; rm -rf "$STATE_DIR/campaigns" 2>/dev/null DM_CFG="$STATE_DIR/dm.mcp.json"; MOVES="$STATE_DIR/player_moves.jsonl"; : > "$MOVES" CHAT="$T/$RUN.chat.jsonl"; : > "$CHAT"; COMBINED="$T/$RUN.jsonl"; : > "$COMBINED" -python3 - "$ROOT/qa/qa.mcp.json" "$STATE_DIR" "$DM_CFG" <<'PY' +python3 - "$ROOT/qa/qa.mcp.example.json" "$STATE_DIR" "$DM_CFG" <<'PY' import json, sys cfg = json.load(open(sys.argv[1])); cfg["mcpServers"]["clawdnd-engine"]["env"]["CLAWDND_STATE_DIR"] = sys.argv[2] json.dump(cfg, open(sys.argv[3], "w")) diff --git a/qa/qa.mcp.json b/qa/qa.mcp.example.json similarity index 75% rename from qa/qa.mcp.json rename to qa/qa.mcp.example.json index 6665fd60..026a72c1 100644 --- a/qa/qa.mcp.json +++ b/qa/qa.mcp.example.json @@ -6,11 +6,11 @@ "args": [ "run", "--directory", - "/Users/lume/WorldOS-val/servers/engine", + "servers/engine", "server.py" ], "env": { - "CLAWDND_STATE_DIR": "/Users/lume/WorldOS-val/qa/state" + "CLAWDND_STATE_DIR": "qa/state" } }, "clawdnd-rules": { @@ -19,7 +19,7 @@ "args": [ "run", "--directory", - "/Users/lume/WorldOS-val/servers/rules", + "servers/rules", "server.py" ], "env": { @@ -32,7 +32,7 @@ "args": [ "run", "--directory", - "/Users/lume/WorldOS-val/servers/voice", + "servers/voice", "server.py" ], "env": { @@ -40,4 +40,4 @@ } } } -} \ No newline at end of file +} diff --git a/qa/run_combat_sprint.sh b/qa/run_combat_sprint.sh index 14ad5070..fbdd54eb 100755 --- a/qa/run_combat_sprint.sh +++ b/qa/run_combat_sprint.sh @@ -38,16 +38,16 @@ fi echo "[cs] seed JSON: $SEED_JSON" # ── 2. Per-run MCP config (engine points at THIS worktree + THIS state dir) ── -# Mirror run_duo.sh lines ~39-46: patch qa.mcp.json with the run-local state dir +# Mirror run_duo.sh lines ~39-46: patch qa.mcp.example.json with the run-local state dir # AND override the engine --directory to this worktree so we use local code. DM_CFG="$STATE_DIR/dm.mcp.json" -python3 - "$ROOT/qa/qa.mcp.json" "$STATE_DIR" "$ROOT" "$DM_CFG" <<'PY' +python3 - "$ROOT/qa/qa.mcp.example.json" "$STATE_DIR" "$ROOT" "$DM_CFG" <<'PY' import json, sys src, state_dir, root, out = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] cfg = json.load(open(src)) eng = cfg["mcpServers"]["clawdnd-engine"] eng["env"]["CLAWDND_STATE_DIR"] = state_dir -# Override --directory arg to use THIS worktree's engine (not the qa.mcp.json default) +# Override --directory arg to use THIS worktree's engine (not the template default) args = eng.get("args", []) try: idx = args.index("--directory") diff --git a/qa/run_duo.sh b/qa/run_duo.sh index 28c5e18d..1803c2d7 100755 --- a/qa/run_duo.sh +++ b/qa/run_duo.sh @@ -38,13 +38,13 @@ mkdir -p "$T" "$STATE_DIR"; rm -rf "$STATE_DIR/campaigns" 2>/dev/null # DM gets the engine (state dir patched in); the player gets an EMPTY strict config. DM_CFG="$STATE_DIR/dm.mcp.json"; PLAYER_CFG="$STATE_DIR/player.mcp.json" MOVES="$STATE_DIR/player_moves.jsonl"; : > "$MOVES" # the player's structured moves (It.1) -python3 - "$ROOT/qa/qa.mcp.json" "$STATE_DIR" "$DM_CFG" "$ROOT" <<'PY' +python3 - "$ROOT/qa/qa.mcp.example.json" "$STATE_DIR" "$DM_CFG" "$ROOT" <<'PY' import json, sys cfg_path, state, out, root = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] cfg = json.load(open(cfg_path)) # RE-ROOT every MCP server's `--directory` at THIS repo ($ROOT) so the DM engine, # rules, and voice run the SAME code as the rest of the harness + the snapshot writer -# (the player facade is already launched from $ROOT). The committed qa.mcp.json template +# (the player facade is already launched from $ROOT). The committed qa.mcp.example.json template # may hardcode a DIFFERENT absolute checkout (e.g. a stale sibling clone); if the DM # engine runs older models.py than the writer, EVERY DM tool call fails with # "extra_forbidden" on the newer snapshot fields and the DM silently falls back to @@ -53,7 +53,15 @@ for name, srv in cfg.get("mcpServers", {}).items(): args = srv.get("args", []) if "--directory" in args: i = args.index("--directory") - pkg = args[i + 1].rstrip("/").split("/servers/")[-1] # "engine" | "rules" | "voice" + raw = args[i + 1].rstrip("/") + if raw.startswith("./"): + raw = raw[2:] + if "/servers/" in raw: + pkg = raw.rsplit("/servers/", 1)[1] + elif raw.startswith("servers/"): + pkg = raw[len("servers/"):] + else: + pkg = raw args[i + 1] = f"{root}/servers/{pkg}" if name == "clawdnd-engine": srv.setdefault("env", {})["CLAWDND_STATE_DIR"] = state diff --git a/qa/run_party.sh b/qa/run_party.sh index 891cb011..609cfd44 100755 --- a/qa/run_party.sh +++ b/qa/run_party.sh @@ -62,7 +62,7 @@ mkdir -p "$T" "$STATE_DIR"; rm -rf "$STATE_DIR/campaigns" 2>/dev/null # --- DM config: the engine, with the state dir patched in (same as run_duo) --------- DM_CFG="$STATE_DIR/dm.mcp.json" -python3 - "$ROOT/qa/qa.mcp.json" "$STATE_DIR" "$DM_CFG" <<'PY' +python3 - "$ROOT/qa/qa.mcp.example.json" "$STATE_DIR" "$DM_CFG" <<'PY' import json, sys cfg = json.load(open(sys.argv[1])); cfg["mcpServers"]["clawdnd-engine"]["env"]["CLAWDND_STATE_DIR"] = sys.argv[2] json.dump(cfg, open(sys.argv[3], "w")) diff --git a/qa/run_qa.sh b/qa/run_qa.sh index 8b8ffbc7..a3e400b0 100755 --- a/qa/run_qa.sh +++ b/qa/run_qa.sh @@ -36,9 +36,9 @@ mkdir -p "$T" "$STATE_DIR" echo "[qa] run=$RUN budget=\$$BUDGET state=$STATE_DIR" rm -rf "$STATE_DIR/campaigns" 2>/dev/null -# Per-run MCP config: qa/qa.mcp.json with this run's state dir patched in, so each +# Per-run MCP config: qa/qa.mcp.example.json with this run's state dir patched in, so each # parallel run's engine writes to its own campaigns/ tree. -python3 - "$ROOT/qa/qa.mcp.json" "$STATE_DIR" "$MCP_CONFIG" <<'PY' +python3 - "$ROOT/qa/qa.mcp.example.json" "$STATE_DIR" "$MCP_CONFIG" <<'PY' import json, sys src, state_dir, out = sys.argv[1], sys.argv[2], sys.argv[3] cfg = json.load(open(src)) diff --git a/qa/runs/sweep-20260528-225614/screen_coverage.json b/qa/runs/sweep-20260528-225614/screen_coverage.json deleted file mode 100644 index f31f1e35..00000000 --- a/qa/runs/sweep-20260528-225614/screen_coverage.json +++ /dev/null @@ -1,395 +0,0 @@ -{ - "duration_s": 405.5957109928131, - "image_audit": { - "hit": 44, - "probes": { - "class-barbarian": { - "bytes": 73801, - "ok": true, - "status": 200 - }, - "class-bard": { - "bytes": 25101, - "ok": true, - "status": 200 - }, - "class-cleric": { - "bytes": 23837, - "ok": true, - "status": 200 - }, - "class-druid": { - "bytes": 27265, - "ok": true, - "status": 200 - }, - "class-fighter": { - "bytes": 21204, - "ok": true, - "status": 200 - }, - "class-monk": { - "bytes": 22986, - "ok": true, - "status": 200 - }, - "class-paladin": { - "bytes": 112437, - "ok": true, - "status": 200 - }, - "class-ranger": { - "bytes": 28011, - "ok": true, - "status": 200 - }, - "class-rogue": { - "bytes": 96366, - "ok": true, - "status": 200 - }, - "class-sorcerer": { - "bytes": 20386, - "ok": true, - "status": 200 - }, - "class-warlock": { - "bytes": 23130, - "ok": true, - "status": 200 - }, - "class-wizard": { - "bytes": 23677, - "ok": true, - "status": 200 - }, - "faction-flaming-fist": { - "bytes": 0, - "ok": false, - "status": 404 - }, - "faction-harpers": { - "bytes": 0, - "ok": false, - "status": 404 - }, - "faction-zhentarim": { - "bytes": 0, - "ok": false, - "status": 404 - }, - "item-chain-mail": { - "bytes": 35275, - "ok": true, - "status": 200 - }, - "item-greataxe": { - "bytes": 44536, - "ok": true, - "status": 200 - }, - "item-lantern": { - "bytes": 21580, - "ok": true, - "status": 200 - }, - "item-longsword": { - "bytes": 42096, - "ok": true, - "status": 200 - }, - "item-potion-of-healing": { - "bytes": 34102, - "ok": true, - "status": 200 - }, - "item-rations": { - "bytes": 2296, - "ok": true, - "status": 200 - }, - "portrait-astarion": { - "bytes": 1348277, - "ok": true, - "status": 200 - }, - "portrait-emperor": { - "bytes": 0, - "ok": false, - "status": 404 - }, - "portrait-gale": { - "bytes": 883713, - "ok": true, - "status": 200 - }, - "portrait-halsin": { - "bytes": 165032, - "ok": true, - "status": 200 - }, - "portrait-jaheira": { - "bytes": 526120, - "ok": true, - "status": 200 - }, - "portrait-karlach": { - "bytes": 1350729, - "ok": true, - "status": 200 - }, - "portrait-lae-zel": { - "bytes": 1042761, - "ok": true, - "status": 200 - }, - "portrait-minsc": { - "bytes": 789557, - "ok": true, - "status": 200 - }, - "portrait-shadowheart": { - "bytes": 1373966, - "ok": true, - "status": 200 - }, - "portrait-volo": { - "bytes": 1549397, - "ok": true, - "status": 200 - }, - "portrait-withers": { - "bytes": 819766, - "ok": true, - "status": 200 - }, - "portrait-wyll": { - "bytes": 1285255, - "ok": true, - "status": 200 - }, - "race-dragonborn": { - "bytes": 52172, - "ok": true, - "status": 200 - }, - "race-drow": { - "bytes": 104152, - "ok": true, - "status": 200 - }, - "race-dwarf": { - "bytes": 100142, - "ok": true, - "status": 200 - }, - "race-elf": { - "bytes": 5598, - "ok": true, - "status": 200 - }, - "race-githyanki": { - "bytes": 523442, - "ok": true, - "status": 200 - }, - "race-gnome": { - "bytes": 530501, - "ok": true, - "status": 200 - }, - "race-half-elf": { - "bytes": 490634, - "ok": true, - "status": 200 - }, - "race-half-orc": { - "bytes": 42196, - "ok": true, - "status": 200 - }, - "race-halfling": { - "bytes": 104656, - "ok": true, - "status": 200 - }, - "race-human": { - "bytes": 525147, - "ok": true, - "status": 200 - }, - "race-tiefling": { - "bytes": 441744, - "ok": true, - "status": 200 - }, - "scene-baldurs-mouth": { - "bytes": 0, - "ok": false, - "status": 404 - }, - "scene-elfsong-tavern": { - "bytes": 845154, - "ok": true, - "status": 200 - }, - "scene-lower-city": { - "bytes": 904100, - "ok": true, - "status": 200 - }, - "scene-sorcerous-sundries": { - "bytes": 3549562, - "ok": true, - "status": 200 - }, - "scene-upper-city": { - "bytes": 533120, - "ok": true, - "status": 200 - } - }, - "rate": 0.8979591836734694, - "total": 49 - }, - "pass": { - "image_render": false, - "screen_render": true - }, - "screen_render_rate": 1.0, - "screens": [ - { - "label": "Launcher / Chronicles", - "png_bytes": 1562582, - "png_path": "qa/runs/sweep-20260528-225614/shots/launcher.png", - "screen": "launcher", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/launcher.png ( 1562582 bytes)" - }, - { - "label": "Session / Table", - "png_bytes": 1354948, - "png_path": "qa/runs/sweep-20260528-225614/shots/table.png", - "screen": "table", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/table.png ( 1354948 bytes)" - }, - { - "label": "Combat / Battle", - "png_bytes": 1066744, - "png_path": "qa/runs/sweep-20260528-225614/shots/combat.png", - "screen": "combat", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/combat.png ( 1066744 bytes)" - }, - { - "label": "Parley / Dialogue", - "png_bytes": 1278959, - "png_path": "qa/runs/sweep-20260528-225614/shots/dialogue.png", - "screen": "dialogue", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/dialogue.png ( 1278959 bytes)" - }, - { - "label": "World Atlas / Map", - "png_bytes": 1405193, - "png_path": "qa/runs/sweep-20260528-225614/shots/map.png", - "screen": "map", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/map.png ( 1405193 bytes)" - }, - { - "label": "Character Sheet", - "png_bytes": 925723, - "png_path": "qa/runs/sweep-20260528-225614/shots/character.png", - "screen": "character", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/character.png ( 925723 bytes)" - }, - { - "label": "Inventory / Stash", - "png_bytes": 1043547, - "png_path": "qa/runs/sweep-20260528-225614/shots/inventory.png", - "screen": "inventory", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/inventory.png ( 1043547 bytes)" - }, - { - "label": "Item Forge", - "png_bytes": 837221, - "png_path": "qa/runs/sweep-20260528-225614/shots/forge.png", - "screen": "forge", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/forge.png ( 837221 bytes)" - }, - { - "label": "Relations", - "png_bytes": 1039950, - "png_path": "qa/runs/sweep-20260528-225614/shots/relations.png", - "screen": "relations", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/relations.png ( 1039950 bytes)" - }, - { - "label": "Quest Journal", - "png_bytes": 596824, - "png_path": "qa/runs/sweep-20260528-225614/shots/journal.png", - "screen": "journal", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/journal.png ( 596824 bytes)" - }, - { - "label": "Codex / Bestiary", - "png_bytes": 766419, - "png_path": "qa/runs/sweep-20260528-225614/shots/bestiary.png", - "screen": "bestiary", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/bestiary.png ( 766419 bytes)" - }, - { - "label": "Acts", - "png_bytes": 862801, - "png_path": "qa/runs/sweep-20260528-225614/shots/acts.png", - "screen": "acts", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/acts.png ( 862801 bytes)" - }, - { - "label": "Market / Merchant", - "png_bytes": 906525, - "png_path": "qa/runs/sweep-20260528-225614/shots/merchant.png", - "screen": "merchant", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/merchant.png ( 906525 bytes)" - }, - { - "label": "Creation Plane", - "png_bytes": 901415, - "png_path": "qa/runs/sweep-20260528-225614/shots/create.png", - "screen": "create", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/create.png ( 901415 bytes)" - }, - { - "label": "World Seed", - "png_bytes": 893153, - "png_path": "qa/runs/sweep-20260528-225614/shots/seed.png", - "screen": "seed", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/seed.png ( 893153 bytes)" - }, - { - "label": "Settings", - "png_bytes": 822487, - "png_path": "qa/runs/sweep-20260528-225614/shots/settings.png", - "screen": "settings", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-225614/shots/settings.png ( 822487 bytes)" - } - ], - "started_at": 1779983774.766356, - "thresholds": { - "image": 0.95 - }, - "viewer_port": 8766 -} \ No newline at end of file diff --git a/qa/runs/sweep-20260528-225614/screen_coverage.md b/qa/runs/sweep-20260528-225614/screen_coverage.md deleted file mode 100644 index 9c7cf401..00000000 --- a/qa/runs/sweep-20260528-225614/screen_coverage.md +++ /dev/null @@ -1,35 +0,0 @@ -# Screen-coverage report - -- Viewer: `http://127.0.0.1:8766/` -- Started: Thu May 28 22:56:14 2026 -- Duration: 405.6s - -## Image-render rate -**89.8%** (44/49 probed scopes returned real bytes ≥1 KB). -Threshold for release: 95%. → **FAIL** - -### Failed scopes -- `portrait-emperor` → HTTP 404, 0 bytes -- `scene-baldurs-mouth` → HTTP 404, 0 bytes -- `faction-flaming-fist` → HTTP 404, 0 bytes -- `faction-zhentarim` → HTTP 404, 0 bytes -- `faction-harpers` → HTTP 404, 0 bytes - -## Screens captured - -- ✓ **launcher** (Launcher / Chronicles) — 1,562,582 bytes → `qa/runs/sweep-20260528-225614/shots/launcher.png` -- ✓ **table** (Session / Table) — 1,354,948 bytes → `qa/runs/sweep-20260528-225614/shots/table.png` -- ✓ **combat** (Combat / Battle) — 1,066,744 bytes → `qa/runs/sweep-20260528-225614/shots/combat.png` -- ✓ **dialogue** (Parley / Dialogue) — 1,278,959 bytes → `qa/runs/sweep-20260528-225614/shots/dialogue.png` -- ✓ **map** (World Atlas / Map) — 1,405,193 bytes → `qa/runs/sweep-20260528-225614/shots/map.png` -- ✓ **character** (Character Sheet) — 925,723 bytes → `qa/runs/sweep-20260528-225614/shots/character.png` -- ✓ **inventory** (Inventory / Stash) — 1,043,547 bytes → `qa/runs/sweep-20260528-225614/shots/inventory.png` -- ✓ **forge** (Item Forge) — 837,221 bytes → `qa/runs/sweep-20260528-225614/shots/forge.png` -- ✓ **relations** (Relations) — 1,039,950 bytes → `qa/runs/sweep-20260528-225614/shots/relations.png` -- ✓ **journal** (Quest Journal) — 596,824 bytes → `qa/runs/sweep-20260528-225614/shots/journal.png` -- ✓ **bestiary** (Codex / Bestiary) — 766,419 bytes → `qa/runs/sweep-20260528-225614/shots/bestiary.png` -- ✓ **acts** (Acts) — 862,801 bytes → `qa/runs/sweep-20260528-225614/shots/acts.png` -- ✓ **merchant** (Market / Merchant) — 906,525 bytes → `qa/runs/sweep-20260528-225614/shots/merchant.png` -- ✓ **create** (Creation Plane) — 901,415 bytes → `qa/runs/sweep-20260528-225614/shots/create.png` -- ✓ **seed** (World Seed) — 893,153 bytes → `qa/runs/sweep-20260528-225614/shots/seed.png` -- ✓ **settings** (Settings) — 822,487 bytes → `qa/runs/sweep-20260528-225614/shots/settings.png` \ No newline at end of file diff --git a/qa/runs/sweep-20260528-225614/shots/acts.png b/qa/runs/sweep-20260528-225614/shots/acts.png deleted file mode 100644 index c905d483..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/acts.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/bestiary.png b/qa/runs/sweep-20260528-225614/shots/bestiary.png deleted file mode 100644 index bf355aa7..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/bestiary.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/character.png b/qa/runs/sweep-20260528-225614/shots/character.png deleted file mode 100644 index a4f9273f..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/character.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/combat.png b/qa/runs/sweep-20260528-225614/shots/combat.png deleted file mode 100644 index b615c818..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/combat.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/create.png b/qa/runs/sweep-20260528-225614/shots/create.png deleted file mode 100644 index 47f63c0e..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/create.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/dialogue.png b/qa/runs/sweep-20260528-225614/shots/dialogue.png deleted file mode 100644 index 4a278587..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/dialogue.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/forge.png b/qa/runs/sweep-20260528-225614/shots/forge.png deleted file mode 100644 index 947035b9..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/forge.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/inventory.png b/qa/runs/sweep-20260528-225614/shots/inventory.png deleted file mode 100644 index a781ce92..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/inventory.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/journal.png b/qa/runs/sweep-20260528-225614/shots/journal.png deleted file mode 100644 index 7d4c5d88..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/journal.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/launcher.png b/qa/runs/sweep-20260528-225614/shots/launcher.png deleted file mode 100644 index 198cc92b..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/launcher.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/map.png b/qa/runs/sweep-20260528-225614/shots/map.png deleted file mode 100644 index 5ff3a4dc..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/map.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/merchant.png b/qa/runs/sweep-20260528-225614/shots/merchant.png deleted file mode 100644 index 3fc70813..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/merchant.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/relations.png b/qa/runs/sweep-20260528-225614/shots/relations.png deleted file mode 100644 index 51e97b23..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/relations.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/seed.png b/qa/runs/sweep-20260528-225614/shots/seed.png deleted file mode 100644 index b6e53b4f..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/seed.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/settings.png b/qa/runs/sweep-20260528-225614/shots/settings.png deleted file mode 100644 index 45787e84..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/settings.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-225614/shots/table.png b/qa/runs/sweep-20260528-225614/shots/table.png deleted file mode 100644 index 050199c4..00000000 Binary files a/qa/runs/sweep-20260528-225614/shots/table.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/screen_coverage.json b/qa/runs/sweep-20260528-233434/screen_coverage.json deleted file mode 100644 index 3f2b2a17..00000000 --- a/qa/runs/sweep-20260528-233434/screen_coverage.json +++ /dev/null @@ -1,395 +0,0 @@ -{ - "duration_s": 407.11159491539, - "image_audit": { - "hit": 49, - "probes": { - "class-barbarian": { - "bytes": 73801, - "ok": true, - "status": 200 - }, - "class-bard": { - "bytes": 25101, - "ok": true, - "status": 200 - }, - "class-cleric": { - "bytes": 23837, - "ok": true, - "status": 200 - }, - "class-druid": { - "bytes": 27265, - "ok": true, - "status": 200 - }, - "class-fighter": { - "bytes": 21204, - "ok": true, - "status": 200 - }, - "class-monk": { - "bytes": 22986, - "ok": true, - "status": 200 - }, - "class-paladin": { - "bytes": 112437, - "ok": true, - "status": 200 - }, - "class-ranger": { - "bytes": 28011, - "ok": true, - "status": 200 - }, - "class-rogue": { - "bytes": 96366, - "ok": true, - "status": 200 - }, - "class-sorcerer": { - "bytes": 20386, - "ok": true, - "status": 200 - }, - "class-warlock": { - "bytes": 23130, - "ok": true, - "status": 200 - }, - "class-wizard": { - "bytes": 23677, - "ok": true, - "status": 200 - }, - "faction-flaming-fist": { - "bytes": 58922, - "ok": true, - "status": 200 - }, - "faction-harpers": { - "bytes": 131648, - "ok": true, - "status": 200 - }, - "faction-zhentarim": { - "bytes": 359646, - "ok": true, - "status": 200 - }, - "item-chain-mail": { - "bytes": 35275, - "ok": true, - "status": 200 - }, - "item-greataxe": { - "bytes": 44536, - "ok": true, - "status": 200 - }, - "item-lantern": { - "bytes": 21580, - "ok": true, - "status": 200 - }, - "item-longsword": { - "bytes": 42096, - "ok": true, - "status": 200 - }, - "item-potion-of-healing": { - "bytes": 34102, - "ok": true, - "status": 200 - }, - "item-rations": { - "bytes": 2296, - "ok": true, - "status": 200 - }, - "portrait-astarion": { - "bytes": 1348277, - "ok": true, - "status": 200 - }, - "portrait-gale": { - "bytes": 883713, - "ok": true, - "status": 200 - }, - "portrait-halsin": { - "bytes": 165032, - "ok": true, - "status": 200 - }, - "portrait-jaheira": { - "bytes": 526120, - "ok": true, - "status": 200 - }, - "portrait-karlach": { - "bytes": 1350729, - "ok": true, - "status": 200 - }, - "portrait-lae-zel": { - "bytes": 1042761, - "ok": true, - "status": 200 - }, - "portrait-minsc": { - "bytes": 789557, - "ok": true, - "status": 200 - }, - "portrait-shadowheart": { - "bytes": 1373966, - "ok": true, - "status": 200 - }, - "portrait-the-emperor": { - "bytes": 541012, - "ok": true, - "status": 200 - }, - "portrait-volo": { - "bytes": 1549397, - "ok": true, - "status": 200 - }, - "portrait-withers": { - "bytes": 819766, - "ok": true, - "status": 200 - }, - "portrait-wyll": { - "bytes": 1285255, - "ok": true, - "status": 200 - }, - "race-dragonborn": { - "bytes": 52172, - "ok": true, - "status": 200 - }, - "race-drow": { - "bytes": 104152, - "ok": true, - "status": 200 - }, - "race-dwarf": { - "bytes": 100142, - "ok": true, - "status": 200 - }, - "race-elf": { - "bytes": 5598, - "ok": true, - "status": 200 - }, - "race-githyanki": { - "bytes": 523442, - "ok": true, - "status": 200 - }, - "race-gnome": { - "bytes": 530501, - "ok": true, - "status": 200 - }, - "race-half-elf": { - "bytes": 490634, - "ok": true, - "status": 200 - }, - "race-half-orc": { - "bytes": 42196, - "ok": true, - "status": 200 - }, - "race-halfling": { - "bytes": 104656, - "ok": true, - "status": 200 - }, - "race-human": { - "bytes": 525147, - "ok": true, - "status": 200 - }, - "race-tiefling": { - "bytes": 441744, - "ok": true, - "status": 200 - }, - "scene-baldurs-mouth": { - "bytes": 1081834, - "ok": true, - "status": 200 - }, - "scene-elfsong-tavern": { - "bytes": 845154, - "ok": true, - "status": 200 - }, - "scene-lower-city": { - "bytes": 904100, - "ok": true, - "status": 200 - }, - "scene-sorcerous-sundries": { - "bytes": 3549562, - "ok": true, - "status": 200 - }, - "scene-upper-city": { - "bytes": 533120, - "ok": true, - "status": 200 - } - }, - "rate": 1.0, - "total": 49 - }, - "pass": { - "image_render": true, - "screen_render": true - }, - "screen_render_rate": 1.0, - "screens": [ - { - "label": "Launcher / Chronicles", - "png_bytes": 1662866, - "png_path": "qa/runs/sweep-20260528-233434/shots/launcher.png", - "screen": "launcher", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/launcher.png ( 1662866 bytes)" - }, - { - "label": "Session / Table", - "png_bytes": 1436791, - "png_path": "qa/runs/sweep-20260528-233434/shots/table.png", - "screen": "table", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/table.png ( 1436791 bytes)" - }, - { - "label": "Combat / Battle", - "png_bytes": 1065581, - "png_path": "qa/runs/sweep-20260528-233434/shots/combat.png", - "screen": "combat", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/combat.png ( 1065581 bytes)" - }, - { - "label": "Parley / Dialogue", - "png_bytes": 1603695, - "png_path": "qa/runs/sweep-20260528-233434/shots/dialogue.png", - "screen": "dialogue", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/dialogue.png ( 1603695 bytes)" - }, - { - "label": "World Atlas / Map", - "png_bytes": 1407294, - "png_path": "qa/runs/sweep-20260528-233434/shots/map.png", - "screen": "map", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/map.png ( 1407294 bytes)" - }, - { - "label": "Character Sheet", - "png_bytes": 924323, - "png_path": "qa/runs/sweep-20260528-233434/shots/character.png", - "screen": "character", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/character.png ( 924323 bytes)" - }, - { - "label": "Inventory / Stash", - "png_bytes": 1042120, - "png_path": "qa/runs/sweep-20260528-233434/shots/inventory.png", - "screen": "inventory", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/inventory.png ( 1042120 bytes)" - }, - { - "label": "Item Forge", - "png_bytes": 835885, - "png_path": "qa/runs/sweep-20260528-233434/shots/forge.png", - "screen": "forge", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/forge.png ( 835885 bytes)" - }, - { - "label": "Relations", - "png_bytes": 1038813, - "png_path": "qa/runs/sweep-20260528-233434/shots/relations.png", - "screen": "relations", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/relations.png ( 1038813 bytes)" - }, - { - "label": "Quest Journal", - "png_bytes": 612406, - "png_path": "qa/runs/sweep-20260528-233434/shots/journal.png", - "screen": "journal", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/journal.png ( 612406 bytes)" - }, - { - "label": "Codex / Bestiary", - "png_bytes": 765083, - "png_path": "qa/runs/sweep-20260528-233434/shots/bestiary.png", - "screen": "bestiary", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/bestiary.png ( 765083 bytes)" - }, - { - "label": "Acts", - "png_bytes": 861508, - "png_path": "qa/runs/sweep-20260528-233434/shots/acts.png", - "screen": "acts", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/acts.png ( 861508 bytes)" - }, - { - "label": "Market / Merchant", - "png_bytes": 882934, - "png_path": "qa/runs/sweep-20260528-233434/shots/merchant.png", - "screen": "merchant", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/merchant.png ( 882934 bytes)" - }, - { - "label": "Creation Plane", - "png_bytes": 900092, - "png_path": "qa/runs/sweep-20260528-233434/shots/create.png", - "screen": "create", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/create.png ( 900092 bytes)" - }, - { - "label": "World Seed", - "png_bytes": 891387, - "png_path": "qa/runs/sweep-20260528-233434/shots/seed.png", - "screen": "seed", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/seed.png ( 891387 bytes)" - }, - { - "label": "Settings", - "png_bytes": 821228, - "png_path": "qa/runs/sweep-20260528-233434/shots/settings.png", - "screen": "settings", - "shot_ok": true, - "shot_stdout": "OK qa/runs/sweep-20260528-233434/shots/settings.png ( 821228 bytes)" - } - ], - "started_at": 1779986074.400529, - "thresholds": { - "image": 0.95 - }, - "viewer_port": 8766 -} \ No newline at end of file diff --git a/qa/runs/sweep-20260528-233434/screen_coverage.md b/qa/runs/sweep-20260528-233434/screen_coverage.md deleted file mode 100644 index b39c27fb..00000000 --- a/qa/runs/sweep-20260528-233434/screen_coverage.md +++ /dev/null @@ -1,31 +0,0 @@ -# Screen-coverage report - -- Viewer: `http://127.0.0.1:8766/` -- Started: Thu May 28 23:34:34 2026 -- Duration: 407.1s - -## Image-render rate -**100.0%** (49/49 probed scopes returned real bytes ≥1 KB). -Threshold for release: 95%. → **PASS** - -### Failed scopes -(all probed scopes returned real bytes — image-render coverage is complete) - -## Screens captured - -- ✓ **launcher** (Launcher / Chronicles) — 1,662,866 bytes → `qa/runs/sweep-20260528-233434/shots/launcher.png` -- ✓ **table** (Session / Table) — 1,436,791 bytes → `qa/runs/sweep-20260528-233434/shots/table.png` -- ✓ **combat** (Combat / Battle) — 1,065,581 bytes → `qa/runs/sweep-20260528-233434/shots/combat.png` -- ✓ **dialogue** (Parley / Dialogue) — 1,603,695 bytes → `qa/runs/sweep-20260528-233434/shots/dialogue.png` -- ✓ **map** (World Atlas / Map) — 1,407,294 bytes → `qa/runs/sweep-20260528-233434/shots/map.png` -- ✓ **character** (Character Sheet) — 924,323 bytes → `qa/runs/sweep-20260528-233434/shots/character.png` -- ✓ **inventory** (Inventory / Stash) — 1,042,120 bytes → `qa/runs/sweep-20260528-233434/shots/inventory.png` -- ✓ **forge** (Item Forge) — 835,885 bytes → `qa/runs/sweep-20260528-233434/shots/forge.png` -- ✓ **relations** (Relations) — 1,038,813 bytes → `qa/runs/sweep-20260528-233434/shots/relations.png` -- ✓ **journal** (Quest Journal) — 612,406 bytes → `qa/runs/sweep-20260528-233434/shots/journal.png` -- ✓ **bestiary** (Codex / Bestiary) — 765,083 bytes → `qa/runs/sweep-20260528-233434/shots/bestiary.png` -- ✓ **acts** (Acts) — 861,508 bytes → `qa/runs/sweep-20260528-233434/shots/acts.png` -- ✓ **merchant** (Market / Merchant) — 882,934 bytes → `qa/runs/sweep-20260528-233434/shots/merchant.png` -- ✓ **create** (Creation Plane) — 900,092 bytes → `qa/runs/sweep-20260528-233434/shots/create.png` -- ✓ **seed** (World Seed) — 891,387 bytes → `qa/runs/sweep-20260528-233434/shots/seed.png` -- ✓ **settings** (Settings) — 821,228 bytes → `qa/runs/sweep-20260528-233434/shots/settings.png` \ No newline at end of file diff --git a/qa/runs/sweep-20260528-233434/shots/acts.png b/qa/runs/sweep-20260528-233434/shots/acts.png deleted file mode 100644 index e3b631f1..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/acts.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/bestiary.png b/qa/runs/sweep-20260528-233434/shots/bestiary.png deleted file mode 100644 index 2652ce85..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/bestiary.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/character.png b/qa/runs/sweep-20260528-233434/shots/character.png deleted file mode 100644 index 6c118f27..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/character.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/combat.png b/qa/runs/sweep-20260528-233434/shots/combat.png deleted file mode 100644 index 9415621f..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/combat.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/create.png b/qa/runs/sweep-20260528-233434/shots/create.png deleted file mode 100644 index d5da55e6..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/create.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/dialogue.png b/qa/runs/sweep-20260528-233434/shots/dialogue.png deleted file mode 100644 index a107a57a..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/dialogue.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/forge.png b/qa/runs/sweep-20260528-233434/shots/forge.png deleted file mode 100644 index cef1c94c..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/forge.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/inventory.png b/qa/runs/sweep-20260528-233434/shots/inventory.png deleted file mode 100644 index 8c391fcd..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/inventory.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/journal.png b/qa/runs/sweep-20260528-233434/shots/journal.png deleted file mode 100644 index ca0a6407..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/journal.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/launcher.png b/qa/runs/sweep-20260528-233434/shots/launcher.png deleted file mode 100644 index 51f71c9a..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/launcher.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/map.png b/qa/runs/sweep-20260528-233434/shots/map.png deleted file mode 100644 index f28e7c96..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/map.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/merchant.png b/qa/runs/sweep-20260528-233434/shots/merchant.png deleted file mode 100644 index 2c6bbbbc..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/merchant.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/relations.png b/qa/runs/sweep-20260528-233434/shots/relations.png deleted file mode 100644 index 52efc8c0..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/relations.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/seed.png b/qa/runs/sweep-20260528-233434/shots/seed.png deleted file mode 100644 index 3019b8f6..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/seed.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/settings.png b/qa/runs/sweep-20260528-233434/shots/settings.png deleted file mode 100644 index b08ceb47..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/settings.png and /dev/null differ diff --git a/qa/runs/sweep-20260528-233434/shots/table.png b/qa/runs/sweep-20260528-233434/shots/table.png deleted file mode 100644 index a9f8c141..00000000 Binary files a/qa/runs/sweep-20260528-233434/shots/table.png and /dev/null differ diff --git a/script/unblock_native_app.sh b/script/unblock_native_app.sh index 9ed39599..cdadaee9 100755 --- a/script/unblock_native_app.sh +++ b/script/unblock_native_app.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# One-shot unblock for the ClawDnD desktop app. +# One-shot unblock for the WorldOS desktop app. # # Background: NordVPN Threat Protection's Shield (an Endpoint Security extension) # re-scans freshly-rebuilt ad-hoc-signed apps on every build, and that scan can @@ -13,7 +13,7 @@ # app (prefers Developer ID signing if your keychain ACL permits it, falls back to # ad-hoc), opens it, and polls for the viewer to bind. Usage: # -# bash ~/ClawDnD-val/script/unblock_native_app.sh +# bash ~/WorldOS/script/unblock_native_app.sh # # If the Keychain dialog appears during codesign and you click "Always Allow", the # Developer ID identity becomes silent for every future rebuild (no more popups — @@ -23,7 +23,7 @@ set -uo pipefail -REPO="${CLAWDND_REPO:-$HOME/ClawDnD-val}" +REPO="${WORLDOS_REPO:-${CLAWDND_REPO:-$HOME/WorldOS}}" PORTS_TO_CHECK="${CLAWDND_PORTS:-8765 8766 8767 8768 8769}" step() { printf "\n→ %s\n" "$1"; } @@ -32,10 +32,11 @@ warn() { printf " ! %s\n" "$1"; } err() { printf " ✗ %s\n" "$1" 1>&2; } if [ ! -d "$REPO" ]; then - err "repo not found at $REPO (set CLAWDND_REPO to override)"; exit 2 + err "repo not found at $REPO (set WORLDOS_REPO to override; CLAWDND_REPO is also supported)"; exit 2 fi -step "Reap any stale ClawDnD / viewer processes (clean host)" +step "Reap any stale WorldOS / viewer processes (clean host)" +pkill -f "dist/WorldOS.app" 2>/dev/null || true pkill -f "dist/ClawDnD.app" 2>/dev/null || true pkill -f "viewer/server.py" 2>/dev/null || true sleep 1 @@ -52,7 +53,7 @@ else warn "no NordVPN Shield / helper running — already clean" fi -step "Build + launch the ClawDnD app (prefers Developer ID, falls back to ad-hoc)" +step "Build + launch the WorldOS app (prefers Developer ID, falls back to ad-hoc)" cd "$REPO" script/build_and_run.sh run ok "build + open issued" @@ -72,7 +73,7 @@ done if [ -n "$bound" ]; then ok "viewer is serving on port $bound — the desktop app window should be playable now" - printf "\n Open the ClawDnD window and click Resume / Forge a hero / Begin.\n Engine 1385/1385 ✓ · viewer 90/90 ✓ · all 14 screens render polished.\n\n" + printf "\n Open the WorldOS window and click Resume / Forge a hero / Begin.\n Engine 1385/1385 ✓ · viewer 90/90 ✓ · all 14 screens render polished.\n\n" exit 0 fi diff --git a/scripts/license_check.py b/scripts/license_check.py index 6e0afdce..a4ffbe8e 100644 --- a/scripts/license_check.py +++ b/scripts/license_check.py @@ -35,6 +35,7 @@ "content/campaigns/_imported/", "content/campaigns/_private/", "content/worlds/_private/", + "worldos-private-compendium/", "clawdnd-private-compendium/", "private-compendium/", "tools/ingest/private-compendium/", diff --git a/servers/engine/server.py b/servers/engine/server.py index 5d85bae8..bb76d5eb 100644 --- a/servers/engine/server.py +++ b/servers/engine/server.py @@ -2063,8 +2063,8 @@ def load_canon_character(campaign_id: str, name: str, kind: str = "npc", add_to_ ch = Character( name=canonical, # A canon figure can be pulled in as the PROTAGONIST (the player), not just an - # npc/companion — using one as the PC is the documented testing path (CLAUDE.md: - # "Use CANON BG NPCs ... as the player persona"). Coercing kind down to "npc"/ + # npc/companion — using one as the PC is a documented QA path for player + # personas. Coercing kind down to "npc"/ # "companion" meant a canon-loaded PC left `party` with ZERO kind=="player" members # and tripped the player_in_party behavioral gate (QA: ow-duoF went RED). Allow # "player" through; anything unexpected still defaults to "npc". diff --git a/servers/voice/playtest_voice.py b/servers/voice/playtest_voice.py index aec5a07b..419abbde 100644 --- a/servers/voice/playtest_voice.py +++ b/servers/voice/playtest_voice.py @@ -2,8 +2,8 @@ adventure, each in its assigned character voice, proving the voice layer plays actual campaign content with distinct voices. -Run (HF cache on LEXAR to spare the main disk): - HF_HOME=/Volumes/LEXAR/.cache/huggingface \ +Run (optionally point HF_HOME at a large local cache directory): + HF_HOME="$HOME/.cache/huggingface" \ uv run --directory servers/voice --group kokoro python playtest_voice.py """ diff --git a/servers/voice/smoke_test.py b/servers/voice/smoke_test.py index 06fce6a1..85453406 100644 --- a/servers/voice/smoke_test.py +++ b/servers/voice/smoke_test.py @@ -4,8 +4,8 @@ voices and writes WAV files, proving the local Kokoro backend produces real, nonzero, multi-voice audio. The first run downloads the Kokoro model. -Run (point the HF cache at LEXAR to spare the small main disk): - HF_HOME=/Volumes/LEXAR/.cache/huggingface \ +Run (optionally point HF_HOME at a large local cache directory): + HF_HOME="$HOME/.cache/huggingface" \ uv run --directory servers/voice --group kokoro python smoke_test.py """ diff --git a/tools/ingest/README.md b/tools/ingest/README.md index 451bfec1..897920ae 100644 --- a/tools/ingest/README.md +++ b/tools/ingest/README.md @@ -126,8 +126,8 @@ python3 tools/ingest/private_compendium_sidecar.py --init python3 tools/ingest/private_compendium_sidecar.py ``` -The default sidecar root is `/Volumes/LEXAR/Codex/clawdnd-private-compendium`, overridable -with `CLAWDND_PRIVATE_COMPENDIUM_ROOT`. See `docs/PRIVATE_COMPENDIUM_SIDECAR.md`. +The default sidecar root is `~/.worldos/private-compendium`, overridable with +`WORLDOS_PRIVATE_COMPENDIUM_ROOT`. See `docs/PRIVATE_COMPENDIUM_SIDECAR.md`. ## Licensing (TEXT ONLY) diff --git a/tools/ingest/private_compendium_sidecar.py b/tools/ingest/private_compendium_sidecar.py index ca5a75b7..1cdde0de 100644 --- a/tools/ingest/private_compendium_sidecar.py +++ b/tools/ingest/private_compendium_sidecar.py @@ -17,10 +17,10 @@ from dataclasses import dataclass from pathlib import Path -# WorldOS rename (issue #295, W0-E/4.2): prefer the new ~worldos~ scratch root + +# WorldOS rename (issue #295, W0-E/4.2): prefer the new worldos scratch root + # WORLDOS_* env var; keep the legacy clawdnd ones as warn-only fallbacks for v1.x. -WORLDOS_DEFAULT_SIDECAR_ROOT = Path("/Volumes/LEXAR/Codex/worldos-private-compendium") -DEFAULT_SIDECAR_ROOT = Path("/Volumes/LEXAR/Codex/clawdnd-private-compendium") +WORLDOS_DEFAULT_SIDECAR_ROOT = Path("~/.worldos/private-compendium").expanduser() +DEFAULT_SIDECAR_ROOT = Path("~/.clawdnd/private-compendium").expanduser() ENV_SIDECAR_ROOT_WORLDOS = "WORLDOS_PRIVATE_COMPENDIUM_ROOT" ENV_SIDECAR_ROOT = "CLAWDND_PRIVATE_COMPENDIUM_ROOT" MANIFEST_NAME = "private-compendium-manifest.json" diff --git a/viewer/server.py b/viewer/server.py index db066b66..3354f0d4 100644 --- a/viewer/server.py +++ b/viewer/server.py @@ -294,8 +294,8 @@ def _singular_to_plural(v: str) -> str: def _load_ingested_descriptor(desc_path: Path) -> Optional[dict]: """Load a wiki_ingest.json + re-anchor its `path` field to live next to the - descriptor. The ingest pipeline writes an absolute `path` at ingest time (e.g. - /Volumes/LEXAR/.../class_fighter/image.png); when the repo is cloned / moved / + descriptor. The ingest pipeline writes an absolute `path` at ingest time (for + example, /path/to/sidecar/class_fighter/image.png); when the repo is cloned / moved / checked out on a different machine, that original absolute path no longer exists and `_serve_image`'s containment check correctly rejects it — even though the image bytes ARE present at the canonical location next to wiki_ingest.json