Commit Graph

15 Commits

Author SHA1 Message Date
34bf064904 fix(creation-screen): the bonus-skill hole, the theme guard that could not fail, and the strings the player was reading
Final whole-branch review of M4-b. Six findings.

1. The player could press his way into an invalid draft. can_take_bonus()
   refused an already-picked skill; can_pick() did not consider the bonus, so
   the guard was one-directional. Take athletics as the human's bonus, pick it
   again from the Sellsword's pool, and the draft is one NewGame.validate
   rejects — the invalid draft "he did not cause and cannot see" that
   set_calling's own comment forbids. Fixed in the DRAFT (the screen owns no
   rules): can_pick() now mirrors can_take_bonus(), so the chip goes inert, the
   idiom the pool row already uses for a race-granted skill. Recoverable: hand
   the bonus back and the chip is live again. Both directions tested.

2. The theme drift guard STILL could not fail. ThemeKeys.ALL is stylebox
   variations only, by its own docstring — so the six FONT roles the builder
   sets fonts, sizes and colours for were entirely unguarded. Changing
   Palette.CREAM and skipping the rebuild shipped a stale game_theme.tres with
   the suite green (proven, on the committed test). Adds ThemeKeys.FONT_ROLES
   beside ALL (never inside it — ALL's contract is relied on), compares fonts,
   font sizes and font colours as well as styleboxes, and names the variation
   AND the property that drifted. Plus a meta-guard that walks the builder's own
   output and fails if a variation it styles is in neither collection — this is
   the second time this guard has been fixed by "enumerate the right set", and
   nothing was checking the set.

3. §13: the CTA label piped NewGame.validate's diagnostics straight to the
   player. On the game's SECOND SCREEN he read "hedge_mage picks 2 skills, got
   0". Now CreationCopy.error_line maps them to authored copy — "name yourself",
   "choose two more proficiencies" (the number DERIVED from Callings.skill_count,
   never written down), "one more proficiency, any of them — take it" — with an
   authored fallback for anything unmapped, because §13 means the unmapped case
   still speaks in voice. validate's strings are untouched: they are a contract,
   so they are translated in the presentation layer, not rewritten.

4. §2 had no test that fails if the boundary is breached. The emit test compared
   the signal to draft.to_creation() — both sides move together. Pins the seven
   contract keys exactly, and feeds construct a hostile creation dict carrying an
   18 in every stat, asserting the sheet is still roll_attributes(seed) + spend.
   Every other test in test_new_game passed with that breach in place.

5. Three holes: the orphan sweep skipped PoolRow (a stray Pool6 rendered unbound
   and visible, suite green — the exact bug the test exists to catch, in the one
   row it forgot); the §7 sweep forbade "luck" but not Luck.BANDS' descriptors,
   which are worse than the number (the player would re-roll until his Luck read
   well — calculable Luck); and the DM panel's composition hardcodes the article
   "a", so a vowel-initial calling would break it silently — guarded in the
   content parity test, which names the template as the reason.

6. The calling card printed "talent second_wind". Four of seven talents carry an
   underscore. Humanised, the way skill_label() already does. The test that
   pinned the RAW form is moved to the full derived phrase ("talent second wind")
   so it still fails if someone hardcodes a talent into the format string.

Every guard was proven by re-breaking the code and watching the named test go
red (docs/traps.md). Suite 302 -> 315, content build green, no new warnings.

Report: .superpowers/sdd/final-review-fix-report.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 16:25:12 -05:00
0bbf94289a feat(theme): the creation screen's five variations
SelectCard (a Button whose `pressed` IS the chosen ring), SkillChip (a toggle
with unpicked/picked/granted-so-inert), the CHOSEN and PRIMARY tags, and the
gold PrimaryCard ring. Plus INK_LABEL_MUTED for the sheet's section headers.

Built by build_game_theme.gd and regenerated; the .tres is never hand-edited.
2026-07-13 14:48:17 -05:00
f07139256b feat(title): warm shader background, pulsing glow, drifting embers
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 10:54:22 -05:00
c6d513c966 feat(theme): TitleLogo + TitleKicker display variations
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 10:41:42 -05:00
6ef71aaa31 fix(shell): readable ink prose + uniform consumable tiles
The narration RichTextLabel had no theme styling, so it rendered near-white
and was unreadable on parchment. Add a base RichTextLabel style to the theme
(dark INK_BODY serif at 20px, italic face wired) — systemic and previews in
the editor; every parchment prose surface now reads as dark ink. Also clip
the consumable slot labels so a long name ("Antidote") can't stretch its tile
wider than the empty ones — all 8 stay a uniform 62px.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 09:47:21 -05:00
bca04e3a2e feat(theme): parchment Button variation for clickable parchment rows
ThemeKeys.PARCHMENT_CARD is a PanelContainer-base variation (only a
`panel` stylebox) — a Button looks up normal/hover/pressed/disabled,
which don't exist there, so a Button styled with it renders unskinned.
Add PARCHMENT_BUTTON, a Button-base variation with those four
styleboxes plus an empty focus box, built from Palette colours only.
Same fix pattern as DOCK_BUTTON for the system dock. Regenerated
game_theme.tres from the builder.
2026-07-11 09:07:16 -05:00
35c4586792 fix(shell): dark DockButton theme variation for the system dock
DarkPanel is a PanelContainer-base variation (only a `panel`
stylebox); a Button looks up normal/hover/pressed, which don't exist
on it, so SystemDock's menu buttons rendered as default gray Godot
buttons instead of mock-faithful dark nav buttons.

Add a dedicated Button-base DockButton variation (Palette-only
colours, dock-dark normal/hover/pressed styleboxes), point
system_dock.gd's menu buttons at it, and regenerate the committed
game_theme.tres artifact from the builder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 09:00:54 -05:00
4a32e7df8a fix(theme): showcase renders when run — viewport-fit + display config
Two root causes behind the blank F6 run:
1. project.godot had no display/stretch config (README mandates canvas_items
   @ 1920x1080). Added [display] window size + stretch mode/aspect — the
   project's canvas foundation, needed by every screen.
2. A root Control run directly (F6) is not sized to the viewport by full-rect
   anchors — it collapses to (0,0), clipping the ScrollContainer and hiding all
   content. Size the root to the viewport explicitly and track size_changed
   (dropping the full-rect anchors, which conflict with setting size directly).

Faithful main-scene run now measures root = scroll = viewport = 1920x1080.
Suite 125/125.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:11:34 -05:00
0c12e13ac1 fix(theme): drop redundant const preloads shadowing global class_names
palette.gd / theme_keys.gd already declare `class_name`, so `const Palette =
preload(...)` in every consumer shadowed the global class and emitted 4
editor errors ("has the same name as a global class"). Reference the global
class_names directly. Editor reload now clean; suite 125/125; runtime probe
still builds all 75 showcase nodes with the theme applied.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 21:58:34 -05:00
6530625f4d fix(theme): drift-guard test, interactive stylebox states, ThemeKeys font consts, richer showcase (whole-branch review)
- build_game_theme.gd: extract build_theme() as a static func returning the
  fully-built Theme; _init() is now a thin save+quit wrapper. Enables a test
  to build a fresh Theme in memory without running the SceneTree main loop.
- test_theme_resource.gd: add test_committed_tres_matches_builder() — builds
  a fresh Theme via the preloaded builder and compares bg_color against the
  committed game_theme.tres for PrimaryCTA/normal, ParchmentCard/panel, and
  DarkPanel/panel. Catches "palette/builder changed, .tres not regenerated."
- build_game_theme.gd: give Tab, TabActive, and Chip explicit hover/pressed/
  focus styleboxes (focus is StyleBoxEmpty to suppress the default ring) so
  they no longer fall back to Godot's default gray button on interaction.
  Add a disabled stylebox + font_disabled_color to PrimaryCTA for the
  unaffordable-CTA state the 2a shell will need.
- theme_keys.gd: add HEADING/ACCENT/MONO font-role consts (kept out of ALL,
  which is stylebox-variation-only); build_game_theme.gd and
  theme_showcase.gd now reference them instead of raw StringName literals.
- theme_showcase.gd: add an italic DM-voice RichTextLabel (EB Garamond
  italic face, spec §6) and two semantic-coloured chips (BLOOD for
  aggressive actions, GOLD for pay/faith) alongside the existing neutral
  chip, demonstrating the script-applied colouring from spec §5.3.
- dark_bay.gdshader: comment noting the stripe uses FRAGCOORD (screen-space)
  while the vignette uses UV (node-local) — intended for full-bleed use.
- surfaces/*.gd: ## comments documenting the accepted deviation from spec
  §5.4 (uniforms set at runtime from Palette rather than baked into the
  .tscn) — no behavior change.
- Regenerated game_theme.tres via build_game_theme.gd.

Full suite: 125/125 (was 124, +1 drift-guard test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFDm1ku9WDUacK2S78m2be
2026-07-10 20:59:05 -05:00
0082765ee7 fix(theme): showcase renders every §6 element (3rd surface, 3 variations, all fonts, labelled swatches)
- _surfaces(): add VignetteOverlay.tscn alongside ParchmentPanel/DarkBay,
  backed by a Palette.STEEL ColorRect so the dim is visibly rendering.
- _variations(): add ItemTileEmpty (next to filled ItemTile), ParchmentInset,
  and DarkPanel stylebox nodes.
- _fonts(): new section rendering default serif body, Accent
  (Architects Daughter), and Mono (JetBrains Mono) labels at real sizes.
- _swatches(): wrap each ColorRect in a VBoxContainer with a Mono caption
  Label naming its Palette const, per spec's "labelled swatches."

No raw hex / Color(...) literals introduced; all colour via Palette.*, all
styleboxes via ThemeKeys.* variation names.
2026-07-10 20:46:25 -05:00
7954c579d3 feat(theme): showcase harness — the human-eyeball proof
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 20:39:09 -05:00
73fcf2d096 feat(theme): parchment/dark-bay/vignette shader surfaces as drop-in scenes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 20:34:52 -05:00
9091707535 feat(theme): game_theme.tres (fonts + stylebox variations) via builder 2026-07-10 20:29:22 -05:00
888b2b32f5 feat(theme): palette tokens as the single colour source 2026-07-10 20:21:42 -05:00