feat: agent guardrails, headless smoke test, and guardrails CI #58

Closed
Timmy wants to merge 1 commits from feat/agent-guardrails-and-smoke-test into main

1 Commits

Author SHA1 Message Date
Alexander Whitestone
0e5538319b feat: agent guardrails, headless smoke test, and CI workflow
Some checks failed
Accessibility Checks / a11y-audit (pull_request) Failing after 2s
Guardrails / guardrails (pull_request) Successful in 4s
Smoke Test / smoke (pull_request) Failing after 3s
Adds a three-layer defense against the class of silent correctness bugs
surfaced in #54. The layers are: documented rules (AGENTS.md), dynamic
assertions (scripts/smoke.mjs), and static checks (scripts/guardrails.sh).
A new Gitea Actions workflow runs the dynamic + static layers on every PR.

Also fixes two of the bugs the smoke test immediately caught on main:

1. G.flags is now initialized to {} in the globals block. Previously it
   was created lazily by the p_creativity project effect, which forced
   every reader to write `G.flags && G.flags.x` — and left a window where
   a new writer could drop the defensive guard and crash.

2. The community_drama debuff no longer mutates G.codeBoost. Its applyFn
   was invoked from updateRates() on every tick (10 Hz), so the original
   `G.codeBoost *= 0.7` compounded: after ~100 ticks of the drama debuff,
   codeBoost was ~3e-16 instead of the intended 0.7. The fix targets
   G.codeRate instead, which is reset at the top of updateRates() and is
   therefore safe to multiplicatively reduce inside applyFn. AGENTS.md
   rule 1 explains the distinction between persistent multipliers and
   per-tick rate fields so future debuffs don't reintroduce the bug.

The smoke test (`scripts/smoke.mjs`) runs game.js in a vm sandbox with a
minimal DOM stub, no npm deps. It boots the engine, runs ticks, clicks,
buys a building, fires every debuff, checks codeBoost stability, checks
updateRates idempotency, and does a save/load round-trip. 30 assertions,
~0.1s on a dev machine.

The static guardrails (`scripts/guardrails.sh`) grep for the patterns
AGENTS.md forbids. Two rules (click power single-source, no Object.assign
in loadGame) are marked PENDING because PR #55 is landing the fix for
them — the workflow reports them but doesn't fail until #55 merges.

Refs: #54
2026-04-10 20:50:50 -04:00