Ink & Ember / Arnow Solutions
Verdict: PASS — production-ready personal platform
Honest overall 7.0 / 10 = min(rubric 7.2, quality-gate matrix 7.0). The teaching product itself is excellent — the published app and its source are byte-identical, the curriculum data layer is 100% valid, every live probe (auth, progress, AI critic) passed. Two things hold the score at 7: a dead "Next Passage" button in one game, and zero scheduled backups of the single SQLite store that holds ALL learner progress. Both fixes are small; both are in the 2-week sprint below.Evidence base
- Live production probes: login, RBAC (403 verified), progress persistence, AI critic (4.0s, real JSON)
- Served copy = repo source (md5 verified both HTML and backend)
- Curriculum integrity: 0 violations across 6 tracks / 75 sections / 46 check questions
- Dogfood bug-hunt + 13 views walked at 390×844 · 0 unexpected console errors
- XSS injection probe on user-written drafts: escaped, no execution
Scorecard — 11 dimensions
impeccable_detect.sh → 47 anti-pattern patterns + 1 advisory. Waived as deliberate brand identity: cream palette + Fraunces/Inter. Backlogged: 13 sub-AA contrast sites (the runtime miss was one of thirteen soft-ink metatexts at 3.1–3.2:1), 8 kicker-over-heading patterns, 9 left-border "side-tab" cards, 6 bottom-tab labels at 9.5px (below the 11px legibility floor — new P2). Full evidence in artifact 10a; all non-waived findings folded into the sprint.
Findings Heatmap
0
2
6
4
Testing/DX deductions are system-level (no suite, no git), not per-finding. Full dimension×severity detail in artifact 12.
P1 — must fix 2 findings
1 · Find-the-Flaw: "Next Passage" button is dead
The button calls nextFlawPassage(), which no fragment defines. Clicking it after finishing a passage throws ReferenceError and nothing advances — the player must find the "New Passage" ghost button instead. Harvested live as a pageerror during the walkthrough.
Fix: define nextFlawPassage() as clear-score → newFlawPassage(). 15 minutes, include a regression step in the smoke harness.
2 · arnow.db has zero scheduled backups
/root/backup-db.sh (3 AM cron) covers Postgres only. The single SQLite store holding every lesson pass, capstone draft, and reflection is unprotected against host loss. A manual probe confirmed sqlite3 .backup works on the VPS — the fix is one line in an existing script + one restore drill.
P2 — should fix 6 findings
| Finding | Evidence | Where |
|---|---|---|
.author-desc contrast 3.29:1 | Computed pair on author cards — below AA 4.5:1 | Learn grid sublines |
No /api/health on app backend | GET /api/health → 404 (critic backend HAS one) | accounts.py |
| CORS wildcard + allow_credentials=True | accounts.py:30-36 | middleware |
| Empty lesson submit shows no inline feedback | Dogfood D1: submit with zero answers → silence | lesson engine |
| Admin view reachable-but-blank for regular users | Nav shows tab; API correctly 403s; learner lands on a dead screen | admin view |
| Source tree not under git | No history, no rollback path | repo root |
P3 — polish 4 findings
- Seed passwords appear in module docstring (accepted single-user trade-off)
curriculum-v2.jsonunused legacy file in repo root.mpFilterschips slightly under 44 px tap target- Critic fallback echoes raw model output on malformed JSON (truncate instead)
Quick wins (time-boxed)
- Day 1:
nextFlawPassagefix + regression step in harness — 15 min - Day 1: arnow.db cron line in backup-db.sh + one restore drill — 30 min
- Day 2: contrast token darken — 20 min
- Day 2:
/api/healthendpoint — 15 min - Day 3: CORS origins pinned to the 2 known hosts — 15 min
- Day 3:
git init+ first commit — 10 min
Phases: Static audit (00–12)
| Phase | Headline |
|---|---|
| 01–02 Profile & Architecture | Single-file app + 2 small FastAPI services + SQLite; clean boundaries; build contract enforced by build_v3.py |
| 03 Code review | 10 numbered findings; auth & lesson engine praised; the dead-button found here |
| 04 Dependencies | 3 deps, all used, zero supply-chain surface in the browser bundle |
| 05 Test health | No suite/CI; testing anchored at 4.0; harness promoted as sprint item |
| 06 Documentation | Excellent ops docs in the skill; repo needs its own README |
| 07 TypeScript | N/A — vanilla JS equivalent-pass (syntax valid, 0 runtime errors) |
| 08 API surface | All frontend fetches resolve to real routes; 1 API-only route idle |
| 08a Data integrity | Seed-live drift: none; backup gap (P1) sits here |
| 09 Mobile | Zero overflow at 390; all touch targets ≥48; 1 contrast miss |
| 10 Bundle | 795 KB single file = the curriculum content; 286 KB gzip; 735 ms load |
| 10a/10b Design | Warm editorial light theme rules: all 6 PASS; strategy forward is print-artifact + motion-ease |
| 11–12 Roadmap & backlog | 2-week sprint + 6-month vision; 12-point backlog |
Phases: Advanced (13–30)
| Phase | Score | Headline |
|---|---|---|
| 13 Event architecture | 7.5 | Timer hygiene clean; debounced sync races handled |
| 14 Multi-tenancy | 7.0 | Server-side identity threading; RBAC verified live |
| 15 Contract tests | 5.0 | 6/6 API shapes verified manually; schema-snapshot test recommended |
| 16 Accessibility | 7.0 | Strong keyboard/touch/labels; 1 AA contrast miss |
| 17 IaC & deploy | 6.5 | systemd + Traefik solid; deployment procedure skill-only |
| 18 Observability | 5.5 | Logs clean; missing health endpoint + no alerting |
| 19 Compliance | 6.0 | No PHI/PCI; public critic endpoint noted (rate-limit note) |
| 20 Onboarding | 6.5 | In-product excellent; repo README missing |
| 21 Disaster recovery | 5.0 | App stateless-easy; the ONE data store unprotected (P1) |
| 22 Performance | 8.0 | 735 ms / 757 nodes / 0.04 s TTFB measured |
| 23–24 Remediation & loop | — | Fixes deferred to sprint window; loop exits PARTIAL, projected 8.2 post-sprint |
| 25 Quality-gate matrix | 7.0 | 20 checks, 18 PASS, real evidence each — honest anchor of the overall |
| 26 Visualization | 7.5 | All dashboard numbers derived from live state; zero fake-data class |
| 27 UX failures sweep | 7.0 | 1 dead button hit; confirm-protected destructive op correct |
| 28 Above-the-fold | 8.0 | Login + first dashboard self-explanatory, mobile-fit with no scroll |
| 29 Page flow | 7.5 | 7 journeys walked; 6 clean, 1 broken at final step |
| 30 Integration probes | — | 18/20 live checks PASS; md5 deployment-identity proven |
Lenses (31–35)
| Lens | Headline |
|---|---|
| 31 Role experience | Admin-lacks-nav-hiding is the only friction; roles otherwise clean |
| 32 Logic & policy | All teaching-gate math verified (pass rules, capstone gates, flaw scoring = observed values); critic score clamp suggested |
| 33 Operational reporting | Zero fake-data; gap = no export; "time-spent" report is ripe (data already stored) |
| 34 Baseline disposition | Run is the baseline; all skill-history incidents dispositioned RESOLVED with live proof |
| 35 Design & motion (live) | 13 views walked at 390×844 + dogfood; publishing-house identity coherent (9.0); contrast + motion notes; theme-toggle N/A by design |
Strategic Roadmap
2-week sprint
Days 1–3: the six quick wins above. Days 4–5: pytest-ify the smoke harness (3 assertions: lesson pass, flaw-next regression, capstone gate). Days 6–9: inline empty-submit feedback + hide admin nav by role. Day 10: print-capstone stylesheet start.
6-month vision (what to add next)
- Anthology export — capstones become typeset, printable "author's anthology" extracts with shareable no-login links
- Daily commitment layer — 20-minute writing streaks; habit beats intensity for a 10K-books/month goal
- Curriculum tier 2 — 6 more deep lessons per author + a true Suspense track; the content pipeline is proven to scale
- Coach intelligence — index the learner's own drafts: "you lean on filter verbs in high-tension scenes"; author-voice practice prompts
- Time-instrument report — "when you actually write" (event timestamps exist already)
- Multi-learner mode — only when the academy grows; single-learner design is the product's strength
Backlog (12 points)
| ID | Story | Pts | Tag |
|---|---|---|---|
| BL-1 | Learner finishing a flawed passage wants to advance without leaving the tab → define nextFlawPassage() | 1 | P1 |
| BL-2 | Admin wants progress survivable → arnow.db in daily backup + restore drill | 2 | P1 |
| BL-3 | Learner reads author sublines in sun → contrast ≥4.5:1 | 1 | P2 |
| BL-4 | Operator wants status probe → /api/health | 1 | P2 |
| BL-5 | Security → pin CORS origins | 1 | P2 |
| BL-6 | Learner clicks Check too early → inline prompt | 2 | P2 |
| BL-7 | Regular user never sees a blank admin screen → hide tab by role | 1 | P2 |
| BL-8 | Maintainer wants rollback → git init | 1 | P2 |
| P3 batch | Legacy file removal · docstring passwords · chip targets · critic clamp | 2 | P3 |
Forward-looking recommendations
New features (ranked by ROI)
- Anthology/capstone export — the app's signature print artifact; differentiates it from every generic writing app
- Streak habit layer — cheapest behavior lever, data exists
- Second curriculum tier — highest long-term value; pipeline validated
- Draft-pattern intelligence — the "coach that knows your writing" is the differentiator the AI backend enables
UI / design & graphics
- Reading-position rail on lessons-in-progress (bookish, small)
- Lesson-modal 150 ms custom-bezier ease (motion polish)
- Critique results as an "editor's letter" typography treatment (looks like a letter from a mentor)
- Print stylesheet (manuscript look) for any exercise/capstone
- Warm-duotone author portraits — the last text-only surface
Path to 9.0 (Artifact 36 — score-path)
| Stage | What lands | Score | min() held by |
|---|---|---|---|
| 1 · Sprint (2 wks, 12 pts) | BL-1 dead button · BL-2 backup · all 13 contrast sites · /api/health · CORS · git init · tab-label size · contract snapshot test | 8.2 | Correctness 8.5, Testing 5.5 |
| 2 · Verification layer (wk 3) | pytest harness (5 flows) · make verify · kicker/side-tab polish | 8.7 | Testing 7.5, Ops 8.0 |
| 3 · CI + monitoring (wks 4–6) | GitHub Actions (test+build+smoke on push) · uptime/health alerting on /api/health + critic · repo runbook + deploy.sh · critic hardening | 9.0 | Testing 9.0, Ops 8.5 (Conversion waived ≤7.0 by design) |
Explicit non-goals (what 9.0 does NOT require): architecture rebuild, dark mode (brand waiver), compliance suite, redesign — the publishing-house identity already holds 9.0 brand. Full dimension×work math in artifact 36-roadmap-to-9.md.
Ink & Ember / Arnow Solutions
0 P0 · 2 P1 · 6 P2 · 4 P3 · projected 9.0 via artifact 36 score-path (sprint 8.2 → +CI/verification 8.7 → 9.0)
.hermes/ce/full/2026-09-15-ink-and-ember/ · all evidence live-probed 2026-09-15