Static HTML/CSS/JS site (no build step, no dependencies, no Node). 44 hash-routed pages — 29 in the main application sequence plus 15 supporting/conditional/utility pages. State is held in a 345-line schema (js/store/schema.js) that is the SINGLE source of truth — pages are not allowed to write to sessionStorage directly; they go through a session helper that validates dot-notation paths against the schema. Includes a Login.gov simulation layer (6 pages), progressive form validation, conditional page logic (skipped if conditionFn returns false), and a ?sessionFixture=name URL parameter that loads a pre-recorded JSON snapshot from /tests/fixtures/ for isolated page testing. Designed-in deterministic mode (?mode=deterministic) zeroes out simulated randomness (e.g. 10% identity-verification failure).
Role in the system: Drives agent regression tests; lets the AI agent containers be developed and validated without hammering DisasterAssistance.gov; intentionally insecure-by-design but safe because no real PII or auth flows through it
Surfaces:
- 44 hash-routed pages (28 unconditional + 16 conditional/Login/tools)
- URL params: ?sessionFixture, ?mode=deterministic|randomized, ?debug, ?logEvents
- /#/session-editor — dev-only direct JSON state editor
- Fixtures in tests/fixtures/*.json
- Playwright e2e suite (in /tests/)
User workflows
Serve site
http://localhost:8000 ready
Run agent against mock
Agent regression-tested without hitting real FEMA
Inject session fixture
Single page testable in isolation with realistic state
Deterministic mode
Reproducible runs
API endpoints
- GET
/Home (landing) - GET
/#/recaptchaMock CAPTCHA - GET
/#/locationLocation of loss - GET
/#/disasterDisaster selection - GET
/#/countyCounty selection - GET
/#/assistance-infoAssistance info - GET
/#/needsDisaster-related needs - GET
/#/conditional-funeralCONDITIONAL: funeral expenses - GET
/#/conditional-medicalCONDITIONAL: medical - GET
/#/conditional-homeCONDITIONAL: primary home damage - GET
/#/conditional-childcareCONDITIONAL: child care - GET
/#/conditional-safetyCONDITIONAL: home safety - GET
/#/needs-reviewReview selected needs - GET
/#/date-of-lossDate of loss - GET
/#/damage-typesDamage types - GET
/#/privacyPrivacy Act statement - GET
/#/login-signin … /#/login-successLogin.gov simulation (6 pages) - GET
/#/app-center-introApplication Center entry - GET
/#/personal-infoPersonal info form - GET
/#/address + /#/address-verificationAddress + verify - GET
/#/extent-of-damage (cond) … /#/home-insurance (cond)Conditional damage detail pages - GET
/#/occupants /#/income-information /#/payment-information /#/notifications /#/disability-needs /#/other-needs /#/review-application /#/identity-error /#/successApplication Center remaining pages - GET
/#/session-editorDev/test direct JSON state editor
Third-party APIs
DOMPurify (CDN)
HTML sanitization for any user-supplied HTML
Google Fonts (CDN)
Source Sans Pro + Merriweather
Service dependencies
Python http.server (MANDATORY)
Static server with correct ES module MIME type
Analysis
af-mock-disaster-assistance-gov — Prop-Build Analysis
Document Type: Critical Review & Analysis (companion to prop-build-template.md)
Scope: Per-Repo / Per-Module
Subject: af-mock-disaster-assistance-gov (FEMA Disaster Assistance Mock Website)
Reviewer(s): Claude (automated code review)
Date: 2026-04-09
Version: 0.1
Confidence Level: Medium
What would raise confidence: running the mock end-to-end against the live AI agents, executing the Playwright/pytest integration suite, and interviewing the author (Gordon Zheng).
Inputs Reviewed:
- Prop-build doc:
data/af-mock-disaster-assistance-gov.yaml - Companion files:
data/af-mock-disaster-assistance-gov/{component-tree,data-flow,deployment,runbook,api-examples}.md - Source:
/Users/andres/src/af/af-mock-disaster-assistance-gov/(js/, tests/, index.html, README.md)
Part A — Per-Repo / Per-Module Analysis
Note: this repo is a mock / test harness used as a deterministic fixture for training and regression-testing AidFinder AI agents. Judged on test-harness quality (determinism, fixture ergonomics, schema stability, observability), not production hardening.
A.1 Executive Summary
- Overall health: Ambitious, well-structured vanilla-ES6 harness: 43 page modules, canonical session schema, fixture injection, sizeable Python/Playwright test suite — solid bones for an AI-agent training fixture.
- Top risk: Left-behind debug
console.logstatements dumpingpersonalInformationon every save/load (js/storage.js:21,29,69,79,88,90,97;js/state.js:68) — harmless in a mock but a footgun if repurposed for recording agent traces. See A.4.1. - Top win / thing worth preserving: Schema-first session model (
js/store/schema.js) +session-adaptervalidation +storage.injectSnapshot()fixture pipeline (js/storage.js:44-52) — exactly the right shape for a deterministic agent harness. - Single recommended next action: Delete debug
console.logblock injs/storage.jsandjs/state.js:68; route diagnostics throughlogger.js. - Blocking unknowns: Pixel-parity vs. real DisasterAssistance.gov (not verifiable from code); whether Playwright suite is wired into CI; flake rate of schema-migration tests.
A.2 Health Scorecard
| # | Dimension | Score (1–5) | Justification |
|---|---|---|---|
| 1 | Module overview / clarity of intent | 4 | README, prop-build YAML, and layout (js/pages, js/store, js/components) make intent obvious. |
| 2 | External dependencies | 5 | Zero runtime deps; only playwright devDep (package.json). |
| 3 | API endpoints | N/A | Static site, no server API; api-examples.md documents the client state contract. |
| 4 | Database schema | 4 | js/store/schema.js is versioned (_meta._version); migrations.js exists. |
| 5 | Backend services | N/A | No backend. |
| 6 | WebSocket / real-time | N/A | None. |
| 7 | Frontend components | 3 | Components reused (form-field, navigation-buttons, sidebar-*), but large modules: session-editor.js 1295 LOC, vehicle-damage.js 687, personal-info.js 572, review-application.js 507; inline innerHTML. |
| 8 | Data flow clarity | 4 | Hash router → page render → state.set → session-adapter.patch → schema validate → storage.save is legible end-to-end. |
| 9 | Error handling & resilience | 3 | error-handler.js (428 LOC) + forceErrors injection in config.js — good for a harness. |
| 10 | Configuration | 4 | js/config.js centralizes deterministic/randomized mode, query-string flags (?debug, ?logEvents, ?mode=), failure rates, force-error toggles. |
| 11 | Data refresh patterns | 3 | Auto-save every 30s + save-on-set; no multi-tab reconciliation. |
| 12 | Performance | 4 | Static assets; heavy modules irrelevant at mock scale. |
| 13 | Module interactions | 4 | Clean import graph from app.js; no obvious cycles. |
| 14 | Troubleshooting / runbooks | 4 | runbook.md + logger.downloadEventLog() + ?debug + in-browser session-editor.js. |
| 15 | Testing & QA | 4 | ~20 Python/Playwright integration scripts + named fixtures (tests/fixtures/*.json). No JS unit tests. |
| 16 | Deployment & DevOps | 3 | python3 -m http.server deliberate; deployment.md documents it. No visible CI. |
| 17 | Security & compliance | 2 | Not meaningfully applicable to a mock, but debug console.log of personalInformation is a real smell even here. |
| 18 | Documentation & maintenance | 4 | README + YAML + 5 companion docs + JSDoc headers. |
| 19 | Roadmap clarity | N/A | No roadmap artifact beyond status: Active. |
Overall score: 3.69 (weighted for the harness role, effective grade is higher: dimensions 1/2/4/8/10/14/15 all score 4+; 7 and 17 are either cosmetic or out-of-scope).
A.3 What's Working Well
-
Strength: Schema-first session model with explicit versioning and migrations.
- Location:
js/store/schema.js:1-50,js/store/migrations.js,js/store/session-adapter.js(validateSchema,validatePath,patch). - Why it works: Every write funnels through a validated
patch()path;_meta._versionlets the harness evolve without breaking saved fixtures. - Propagate to:
af-fema-form-automationand future mock repos.
- Location:
-
Strength: Fixture-injection seam for deterministic agent runs.
- Location:
js/storage.js:44-52(injectSnapshot),tests/fixtures/*.json,js/utils/fixtures.js. - Why it works: Jump straight to any page state without walking the 29-page flow.
- Propagate to: Any real-site-replica mock.
- Location:
-
Strength: Deterministic-vs-randomized mode via URL flags.
- Location:
js/config.js:11-45(mode,identityVerificationFailureRate,forceErrors,enableDebugPanel,enableEventLogging). - Why it works: A/B correctness vs. robustness tests without code changes.
- Location:
-
Strength: In-browser session editor for debugging agent runs.
- Location:
js/pages/session-editor.js(1295 LOC). - Why it works: Operator can inspect/patch live state mid-run.
- Location:
A.4 What to Improve
A.4.1 P1 — Debug console.log leaking personalInformation on every save/load
- Problem:
js/storage.jsandjs/state.jsunconditionallyconsole.logthe fullpersonalInformationsubtree on every save, load, andset()— bypassinglogger.js. - Evidence:
js/storage.js:21-22,28-30,69,79,88-90,97;js/state.js:68. - Suggested change: Delete; if still useful, route through
logger.debug(). - Estimated effort: S.
- Risk if ignored: Console noise; if harness is ever repurposed for real data, a PII-in-logs incident.
A.4.2 P2 — Oversized page modules mixing render, validation, handlers
- Problem: Multiple 500+ LOC pages inline large
innerHTMLstrings alongside validation and handler wiring. - Evidence:
js/pages/session-editor.js1295,vehicle-damage.js687,personal-info.js572 (SSN toggle HTML at lines 70, 367, 370),review-application.js507,needs.js413. - Suggested change: Extract per-page templates into sibling
*.template.js; lean more onjs/components/form-field.js. - Estimated effort: M.
- Risk if ignored: Shotgun-surgery pressure grows as the mock tracks upstream UI changes.
A.4.3 P2 — No JS-level unit tests
- Problem: No unit coverage for
js/store/schema.js,js/store/session-adapter.js,js/store/migrations.js,js/validation.js— the exact modules correctness depends on. - Evidence:
tests/is Python-only;package.json"test"is a stubecho. - Suggested change: Add Vitest or
node --testsuite for store modules. - Estimated effort: M.
- Risk if ignored: Schema regressions only caught by slow Playwright runs.
A.5 Things That Don't Make Sense
-
Observation:
package.jsonhard-blocksnpm start/npm run devwithecho && exit 1and declares"engines": { "node": "DO NOT USE - ..." }, yetplaywrightis a devDep that needs Node.- Location:
package.jsonscripts + engines. - Hypotheses considered: (a) past
http-serverES-module MIME bug; (b) enforcing Python parity with CI. - Question for author: What specifically broke under Node servers?
- Location:
-
Observation:
session-editor.js(1295 LOC) is larger thanapp.js(476) +router.js(338) combined.- Location:
js/pages/session-editor.js. - Hypotheses considered: Debug-tool accumulation point; runbook UI.
- Question for author: Split into
js/components/session-editor/?
- Location:
A.6 Anti-Patterns Detected
A.6.1 Code-level
- God object / god function —
js/pages/session-editor.js(1295 LOC). - Shotgun surgery
- Feature envy
- Primitive obsession
- Dead code
- Copy-paste / duplication — inline SVG toggles at
js/pages/personal-info.js:367,370. - Magic numbers
- Deep nesting (>3)
- Long parameter lists (>4)
- Boolean-flag parameters
A.6.2 Architectural
- Big ball of mud
- Distributed monolith
- Chatty services
- Leaky abstraction
- Golden hammer
- Vendor lock-in
- Stovepipe
- Missing seams for testing — explicitly not checked;
injectSnapshot+forceErrors+mode=deterministicare exactly the seams this asks for.
A.6.3 Data
N/A — no database; localStorage schema is versioned and validated.
A.6.4 Async / Ops
N/A — no queues, no distributed work.
A.6.5 Security
- PII/PHI in logs —
console.logofpersonalInformationinjs/storage.js:21,29,69,88andjs/state.js:68. Synthetic today, pattern is the smell. - Secrets in code / .env committed
- Missing authn/z on internal endpoints — N/A
- Overbroad IAM — N/A
- Unvalidated input crossing trust boundary — N/A
- Missing CSRF/XSS/SQLi/SSRF — not flagging (mock; store is schema-validated).
A.6.6 Detected Instances
| # | Anti-pattern | Location (file:line) | Severity | Recommendation |
|---|---|---|---|---|
| 1 | God function / oversized module | js/pages/session-editor.js (1295 LOC) | P2 | Split into js/components/session-editor/{view,actions,schema-tree}.js. |
| 2 | Copy-paste inline SVG toggle | js/pages/personal-info.js:367,370 | P2 | Extract components/icon-toggle.js or use <svg><use> sprite. |
| 3 | PII-shaped data in console.log | js/storage.js:21,29,69,79,88,90,97; js/state.js:68 | P1 | Delete or route through logger.debug(). |
A.7 Open Questions
-
Q: Is
tests/integration/wired into CI or run manually?- Blocks: A.2 row 15, A.13.
- Who can answer: Gordon Zheng / QA owner.
-
Q: How is schema-version drift between this mock and
af-disaster-assistance-gov-agentcoordinated when FEMA changes a field?- Blocks: A.10, Part B lockstep risk.
- Who can answer: Agent-container maintainers.
-
Q: Are
tests/fixtures/guaranteed-synthetic or seeded from real runs?- Blocks: A.11 Information Disclosure.
A.8 Difficulties Encountered
-
Difficulty: No local execution — Playwright not run, flow not walked.
- Impact: Cannot verify pixel parity, flake rate, or that
migrations.jsupgrades legacy fixtures cleanly. - Fix: One-command
make verifythat spins server, runs suite, emits JUnit.
- Impact: Cannot verify pixel parity, flake rate, or that
-
Difficulty: Page modules mix HTML + handlers + validation; "where is field X written?" needs whole-file reads.
- Impact: Could not cheaply sample all 43 pages; row 7 is based on 5 largest + core modules.
- Fix: A.4.2 template extraction + per-page header listing schema paths written.
-
Difficulty: No
git logreview of churn.- Impact: A.15 answered from YAML author metadata only.
A.9 Risks & Unknowns
A.9.1 Known risks
| # | Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|---|
| 1 | Schema drift between mock and live agent breaks training runs | M | M | Publish SESSION_SCHEMA_VERSION as shared constant; contract test in agent repo. |
| 2 | Debug console.log of personal fields leaks real data if repurposed | L | M | A.4.1. |
| 3 | Oversized page modules slow FEMA refresh cycles | M | L | A.4.2. |
| 4 | Python-only rule breaks Node-only CI containers | L | L | Document known-good image; or ship Node wrapper. |
A.9.2 Unknown unknowns
-
Area not reviewed: Actual correctness vs. real DisasterAssistance.gov.
- Reason: No access during review; "pixel-perfect" untestable from code.
- Best guess risk: M — silent divergence trains agents on stale UI.
-
Area not reviewed:
js/store/migrations.jsrun end-to-end on legacy fixtures.- Reason: Tests not executed.
- Best guess risk: L–M.
-
Area not reviewed: ~37 of 43 page modules (sampled ~6).
- Reason: Time.
- Best guess risk: L — sampled patterns are consistent.
-
Area not reviewed:
dynamic_flow_enhacement/,FEMA-Video-Screen-Cap/,update-mock-webpage/,specs/.- Reason: Out of primary code path.
- Best guess risk: L.
A.10 Technical Debt Register
| # | Debt | Quadrant | Interest | Remediation |
|---|---|---|---|---|
| 1 | Debug console.log of personalInformation (js/storage.js:21,29,69,79,88,90,97; js/state.js:68) | Reckless & Inadvertent | Chronic noise; latent PII risk | A.4.1. |
| 2 | Oversized page modules (session-editor.js 1295, vehicle-damage.js 687, personal-info.js 572, review-application.js 507) mixing HTML + handlers + validation | Prudent & Inadvertent | Slows edits, complicates review | A.4.2. |
| 3 | No JS unit tests for js/store/* and js/validation.js | Prudent & Deliberate | Schema bugs only caught by full Playwright | A.4.3. |
| 4 | Hard block on Node servers in package.json while keeping playwright devDep | Reckless & Deliberate | CI-container friction | Document reason or ship Node static-server wrapper. |
| 5 | session-editor as single 1295-LOC module | Prudent & Inadvertent | Hard to extend | Split directory. |
A.11 Security Posture (lightweight STRIDE)
N/A — single-origin, client-only static mock; no auth, no network, no backend. The one real finding (PII-shaped data in console.log) is captured in A.4.1, A.6.5, and A.10 #1.
A.12 Operational Readiness
N/A — ops concepts (SLOs, on-call, DR, backups) don't apply to a dev-run static fixture. Harness-level observability is captured under A.2 row 14 and A.3.
A.13 Test & Quality Signals
- Coverage: Unknown (no tooling).
- Trend: Unknown.
- Flake rate: Unknown.
- Slowest tests: Likely
e2e_full_flow_test.py,test_full_flow_to_success.py. - Untested critical paths:
js/store/schema.js,js/store/session-adapter.js,js/store/migrations.js,js/validation.js. - Missing test types: [x] unit [ ] integration [ ] e2e [x] contract (no cross-repo contract test) [x] load (N/A) [x] security/fuzz (N/A).
A.14 Performance & Cost Smells
N/A — static site, localStorage, no infra.
A.15 Bus-Factor & Knowledge Risk
- Only-person:
authors: ["Gordon Zheng"]in the YAML. Bus factor ≈ 1 for the 29-page → schema mapping. - What breaks if they vanish: Upstream FEMA UI tracking; schema-drift reconciliation with the agent repo.
- Tribal knowledge: Which pages hand-traced vs. auto-captured; conditional-skip logic; the Node-server taboo.
- Knowledge-transfer actions: One-page "how to add a FEMA page" doc; document
FEMA-Video-Screen-Cap/andupdate-mock-webpage/workflows.
A.16 Compliance Gaps
N/A — YAML claims no HIPAA/SOC 2/state-insurance compliance; explicitly a mock.
A.17 Recommendations Summary
| Priority | Action | Owner | Effort | Depends on |
|---|---|---|---|---|
| P1 | Remove debug console.log of personalInformation in js/storage.js:21,29,69,79,88,90,97 and js/state.js:68; route any still-useful diagnostics through logger.debug(). | Mock maintainer (Gordon Zheng) | S | — |
| P1 | Add JS unit-test suite (Vitest or node --test) for js/store/schema.js, js/store/session-adapter.js, js/store/migrations.js, js/validation.js. | Mock maintainer + QA | M | — |
| P1 | Publish SESSION_SCHEMA_VERSION as shared contract; add cross-repo contract test in af-disaster-assistance-gov-agent that imports a canonical tests/fixtures/ fixture. | Agent owner + mock maintainer | M | Schema-version stabilization |
| P2 | Extract per-page HTML templates from session-editor.js, vehicle-damage.js, personal-info.js, review-application.js, needs.js; lean on js/components/form-field.js. | Mock maintainer | M | — |
| P2 | Split js/pages/session-editor.js into js/components/session-editor/. | Mock maintainer | M | Template extraction |
| P2 | Extract inline SVG eye-toggle (js/pages/personal-info.js:367,370) into components/icon-toggle.js or <svg><use> sprite. | Mock maintainer | S | — |
| P2 | README: document why Node servers are blocked; list known-good CI image. | Mock maintainer | S | — |
| P2 | Write "how to add a FEMA page" runbook to reduce bus-factor risk. | Mock maintainer | S | — |
