first pass at the newspaper builder
Test / test (push) Has been cancelled

This commit is contained in:
2026-09-14 11:57:22 +10:00
commit bec1eaac87
497 changed files with 178953 additions and 0 deletions
@@ -0,0 +1,38 @@
# Accessibility Checklist — Wedding Newspaper Generator
> Per confirmed decision Q6=C, there is **no formal WCAG requirement** — this is
> primarily a printed keepsake. This checklist records the practical baseline
> that is still maintained (semantic HTML, alt/caption text, readable
> contrast, keyboard-operable on-screen tooling), not a WCAG 2.1 AA pass.
## Baseline (maintained)
| Area | Check | Status |
|---|---|---|
| Document structure | `<header>` for masthead, single H1, article `<section>`s, logical heading hierarchy | Maintain |
| Figures | Caption text on photos; `alt`/`alt=""` for decorative vs meaningful images | Maintain |
| Contrast | Ink (`#111827`) on cream (`#FBF8F1`) — clearly readable | Maintain |
| On-screen tooling | Review-page buttons focusable; Enter/Space activate | Maintain (on-screen only) |
## Not required (per Q6=C)
| Area | Why no formal pass |
|---|---|
| WCAG 2.1 AA conformance | Deemed out of scope — printed keepsake, primary reader is the couple |
| Screen-reader testing (VoiceOver/NVDA/TalkBack) | Not a formal requirement for this project |
| Keyboard-only full navigation pass | The generated page is a printed artifact, not an interactive app (only the on-screen review tool has keyboard basics) |
| 200%/400% zoom layout verification | Printed medium; scale is fixed at A4 |
## Carried into the deliverable
- The generated HTML is valid, self-contained, and semantically structured
(benefits robustness and printing, and keeps the door open for a11y later).
- Any on-screen interactive surface (the per-article review page) keeps the
practical keyboard/label/caption basics recorded above.
## Note for downstream
If a future iteration wants WCAG 2.1 AA, this checklist is the seed: the
missing items are (1) alt-text audit, (2) formal contrast checks, (3)
keyboard-only testing of the review tool, (4) screen-reader smoke test on the
review page. Not blocking current work.
@@ -0,0 +1,78 @@
# Design System Mapping — Wedding Newspaper Generator
> Classic broadsheet design system (NFR8/US6: black ink on white/cream, serif
> headlines/body, column rules). This maps the intended design tokens and
> patterns; there is no heavy UI framework — the output is self-contained
> HTML5 + CSS (NFR6). Component specs live in `interaction-spec.md`.
## Design principles
- **Print-first**: every design decision is made for A4 ink. Elegance on paper,
legibility at arm's length, clean column flow.
- **Classic, not modern**: black ink on white/cream, serif display + text,
hairline rule lines. No modern tabloid color-blocking (Q5=A, NFR8).
- **Honest content**: never fabricate images (FR7.4) or generic-sounding copy;
the funnies are built from the couple's content (Q5=X).
## Colour tokens
| Token | Value | Use |
|---|---|---|
| `--ink` | #111827 (near-black) | Body text, rules, headings |
| `--paper` | #FBF8F1 (cream/off-white) | Page background |
| `--rule` | #d1c9ba (hairline) | Column rules, borders |
| `--muted` | #6b7280 | Byline/credit/meta text |
| `--accent` | optional (default none) | Minor masthead accent (kept neutral by default per NFR8) |
## Typography
| Role | Face style | Notes |
|---|---|---|
| Masthead title | Large serif display (e.g. a Georgia/Playfair-like serif) | Full-height banner |
| Headlines | Bold serif | Clear hierarchy, page-1 lead largest |
| Body | Serif text | Good A4 legibility |
| Small text | Serif, smaller | Bylines, captions, credits, fine print |
No web fonts are fetched; type relies on local system serifs so the page is
zero-network (NFR4) and prints with the fonts actually on the machine.
## Spacing & grid
- **A4 sheet**: 210×297mm; margins ~15–20mm; page defined via `@page` CSS.
- **Columns**: 3–6 per page depending on width (broadsheet-style multi-column
flow), with hairline column rules.
- **Automatic pagination**: content flows across sheets with breaks at
article/section boundaries where possible (US1/FR1, NFR2).
## Components (mapped to interaction-spec.md)
| Component | Where specified | Design role |
|---|---|---|
| Masthead | interaction-spec.md § Masthead | Header / H1, full sheet width |
| Article column | (page layout) | Serif body, byline + headline group |
| Pull quote / sidebar / fact box | (callout styles, FR3.3) | Boxed off from running text |
| Schedule / events table | (FR3.4) | Clearly formatted table |
| Photo embed | interaction-spec.md § Photo embed | Auto-balanced into column |
| Crossword / find-a-word | interaction-spec.md § Funnies | Bounded, A4-printable |
| Comic strip | interaction-spec.md § Funnies | Panels with art + dialogue |
| Review card | interaction-spec.md § review page | On-screen authoring (approved/edit/replace) |
## States
Per-mockup states (empty / loading / error / success / partial) are captured in
`mockups.md` § M5 and in each component's States table in
`interaction-spec.md`. On the generated page, the primary states are default
(full render) and empty (guided first-run, US10/Q7=A).
## Responsive behaviour
The printed artifact is A4 by definition (NFR1, C5). The on-screen generation
tooling (CLI text + review page) adapts: review cards stack on narrow widths,
span wider on large screens.
## Accessibility baseline
Per Q6=C there is no formal WCAG requirement (printed keepsake). A practical
baseline is kept: semantic HTML structure (`<header>`, H1, article sections),
alt/caption text on figures, and clear contrast (ink on cream). See
`accessibility-checklist.md`.
@@ -0,0 +1,202 @@
# Interaction Specification — Wedding Newspaper Generator
> Component-level specifications following the design-agent component template.
> Covers the two surfaces: the CLI authoring flow and the generated newspaper
> page. Per confirmed decisions: CLI-first (Q1=A), content/config input (Q2=A),
> per-article review page (Q3=A), no print preview step (Q4=B), funnies
> generated from content (Q5=X), no formal a11y target (Q6=C), guided empty
> state (Q7=A).
---
## CLI: `generate` command
| Field | Value |
|---|---|
| Component | `generate` CLI command |
| Description | Reads config + content, optionally drafts via local Ollama, emits `newspaper.html` |
| Category | navigation (entry point) |
### States
| State | Description | Trigger |
|---|---|---|
| ready | Prints usage / config guidance | run with no valid input |
| generating | Parses content, builds layout | valid config + content present |
| drafting | Calls local Ollama for lead/fillers | `--draft` flag present |
| reviewing | Hands to review page (US9) | AI draft produced |
| printed | Writes `newspaper.html`, prints success path | render complete |
| error | Content/config/model failure | validation failure |
### Props / Inputs
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `--draft` | boolean | no | — | Whether to run the local Ollama AI draft (US8) |
| `config` | path | no | `config.json` | Masthead metadata + options |
| `content` | path | no | `content/` | Markdown/txt articles + funnies source |
### Responsive behaviour
N/A (CLI). Terminal width-wrapping of help text only.
### Accessibility
| Requirement | Implementation |
|---|---|
| Error clarity | One-line message naming the problem + the exact fix path |
| Reversibility | Generate is idempotent/regenerable; never destroys the couple's content files |
---
## Per-article review page
| Field | Value |
|---|---|
| Component | Article review card |
| Description | Per-article approve / edit / replace control before final render |
| Category | feedback |
### States
| State | Description | Trigger |
|---|---|---|
| pending | Draft ready, not yet decided | AI draft returned |
| approved | Copy kept as-is | user clicks Approve |
| editing | Copy being revised | user clicks Edit |
| replacing | New copy supplied | user clicks Replace |
### Props / Inputs
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `articleId` | string | yes | — | Stable per-article key (the article-level data boundary, US9/AC9.1.5) |
| `status` | pending\|approved\|editing\|replaced | yes | pending | Current review state |
| `copy` | string | yes | — | The article's text |
| `onApprove` | fn | no | — | Keep as-is |
| `onEdit` | fn | no | — | Open inline edit |
| `onReplace` | fn | no | — | Supply replacement copy |
### Responsive behaviour
Stacked cards on narrow; grid of cards on wide. The newspaper is printed, so
this page is on-screen only.
### Accessibility
| Requirement | Implementation |
|---|---|
| Keyboard | Buttons focusable; Enter/Space activate (on-screen tool) |
| Clear status | Each card visibly labelled with its state (pending/approved/…) |
---
## Masthead component (newspaper page)
| Field | Value |
|---|---|
| Component | Masthead |
| Description | Front-page title banner + date / issue / volume (US2/FR2) |
| Category | display / layout |
### States
| State | Description | Trigger |
|---|---|---|
| default | Classic broadsheet masthead | page render |
### Props / Inputs
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `title` | string | yes | — | Newspaper / couple name |
| `dateLine` | string | yes | — | Issue date |
| `issue` | string | yes | — | Issue number |
| `volume` | string | yes | — | "Volume X" |
| `titleStyle` | object | no | serif | Masthead typography |
### Responsive behaviour
Masthead spans the full sheet width on print; on very narrow screen the title
sizes down but the line stays intact (does not wrap awkwardly).
### Accessibility
| Requirement | Implementation |
|---|---|
| Document structure | Masthead is the page's `<header>` / H1 (semantic, matches Q6=C baseline) |
---
## Funnies: crossword & find-a-word
| Field | Value |
|---|---|
| Component | Funnies section |
| Description | Crossword + find-a-word + comics generated from content (US3/FR4/Q5=X) |
| Category | display |
### States
| State | Description | Trigger |
|---|---|---|
| default | Renders crossword grid, clues, solution; find-a-word grid; comic panels | page render |
### Props / Inputs
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `grid` | array | yes | — | Crossword grid (built from content theme words) |
| `cluesAcross` | array | yes | — | Across clues (derived from content) |
| `cluesDown` | array | yes | — | Down clues (derived from content) |
| `findWords` | array | yes | — | Word list for find-a-word (content terms) |
| `comics` | array | no | — | Comic panels + captions |
| `cartoonAlt` | string | no | — | Context note for the xkcd-style cartoon |
### Responsive behaviour
Each puzzle bounded to its column width and an A4-printable height; no
overflow.
### Accessibility
| Requirement | Implementation |
|---|---|
| Clarity | Grid cells have clear borders and legible type (printed keepsake; Q6=C) |
---
## Photo embed (auto-balanced)
| Field | Value |
|---|---|
| Component | Embedded photo |
| Description | Photo embedded in the column layout with caption (FR6/US4) |
| Category | display |
### States
| State | Description | Trigger |
|---|---|---|
| default | Photo + caption fit the column | photo supplied |
| absent | No placeholder; layout renders clean, text-only | no photo supplied (FR7.4) |
### Props / Inputs
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `src` | data/blob | no | — | Locally embedded image |
| `caption` | string | no | — | Caption text |
| `credit` | string | no | — | Credit/byline line |
### Responsive behaviour
Auto-balances into the column structure; falls back to full-width or natural
size if it cannot fit (R-02 from requirements review), never overflowing the
A4 sheet (NFR2).
### Accessibility
| Requirement | Implementation |
|---|---|
| Text alternative | Caption present; decorative `alt` when purely decorative |
@@ -0,0 +1,17 @@
<!-- INVARIANT: examples are single-line HTML comments so a fresh template parses to total=0 (MEMORY_EMPTY). Do NOT un-comment or split across lines. t100 guards this. -->
> This file is kept up to date automatically while the stage runs. Add observations at the review step, not by editing here directly.
## Interpretations
<!-- example: 2026-05-29T10:14:32Z — chose REST over GraphQL; the consuming team only needs CRUD, revisit if subscriptions land -->
## Deviations
<!-- example: 2026-05-29T10:14:32Z — skipped the optional caching layer the stage prose suggested; the dataset is small enough that it adds risk -->
## Tradeoffs
<!-- example: 2026-05-29T10:14:32Z — picked TDD over BDD this run; the team is unit-first and the domain is well-understood -->
## Open questions
<!-- example: 2026-05-29T10:14:32Z — confirm the retention window with compliance before the next stage hardens the schema -->
2026-09-13T11:50:00Z — Interpretation — Confirmed design: CLI-first authoring, content/config input, per-article review page, no print preview, funnies generated from content (Q5=X), no formal a11y target (Q6=C), guided empty state
2026-09-13T11:50:00Z — Tradeoff — Q5=X moves puzzle/ cartoon authoring from user-supplied data to content-derived generation; richer payoff (hand-crafted feel) at the cost of generation complexity and subjective-search heuristics
2026-09-13T11:50:00Z — Tradeoff — Q6=C drops a formal a11y pass; kept a practical semantic/print baseline, notes how to add WCAG 2.1 AA later
@@ -0,0 +1,102 @@
# Refined Mockups — Wedding Newspaper Generator
> Design-lead refined mockups derived from the user stories (US1–US10),
> requirements (FR1–FR8, NFR1–NFR9), and the seven confirmed design decisions
> (CLI-first authoring, content/config input, per-article review page, no print
> preview, funnies generated from content, no formal a11y target, guided empty
> state). Rough mockups were not produced (classic scope skips Ideation), so
> these are designed directly from requirements + stories.
## M1 — The authoring flow (generator UX)
The generator is **CLI-first** (Q1=A): the couple runs a single command, the
tool reads a content folder + config, optionally invokes local Ollama for the
AI draft, emits `newspaper.html`, and (per US9) presents a per-article review
page before the final render.
```
worktree/
config.json # masthead metadata: title, names, date, issue, volume
content/ # markdown/txt articles + funnies source
newspaper.html # the generated, self-contained printable artifact
```
`newspaper generate [--draft]` flows:
1. Read `config.json` + `content/` (Q2=A).
2. If `--draft`, ask local Ollama to draft lead story + fillers (US8), keeping
everything on-machine (NFR9).
3. If AI used, open the **per-article review page** (US9/Q3=A): approve / edit /
replace any single article before final render.
4. Emit self-contained `newspaper.html`, zero-network, openable via `file://`,
printable to A4 (FR7, NFR1–NFR4, US5).
## M2 — The newspaper page (reader UX)
The output is a classic broadsheet (US6/NFR8): black ink on white/cream, serif
headlines + body, column rules. Content flows across multiple A4 pages with
clean page breaks (US1/FR1). Structural regions (US2/US3/US4/US6/US3-funnies):
```
┌─────────────────────────────────────────────┐ ┌─────────────────────────────┐
│ MASTHEAD (title / date · issue · volume) │ │ interior page header │
├──────────────┬──────────────┬───────────────┤ ├────────┬────────┬──────────┤
│ lead/ front │ article │ xkcd-style │ │ column │ column │ ads/box │
│ page story │ (bylined) │ cartoon │ │ │ │ │
├──────────────┼──────────────┼───────────────┤ ├────────┴────────┴──────────┤
│ article 3 │ pull quote │ schedule / │ │ photo auto-balanced │
│ │ │ events block │ │ │
├──────────────┴──────────────┴───────────────┤ ├──────────────┬─────────────┤
│ funnies: crossword · find-a-word · comics │ │ article cont.│ well-wishes │
└─────────────────────────────────────────────┘ └──────────────┴─────────────┘
page 1 page 2+
```
- **Page 1**: masthead (US2), lead story (FR3.1), interior articles with
bylines (FR3.2), xkcd-style cartoon tied to the day (FR5), schedule/events
block (FR3.4), pull quotes/sidebars (FR3.3).
- **Pages 2+**: flowing interior columns, photo auto-balancing (FR6/FR4/US4),
the lighter well-wishes corner (FR3.5), and the funnies section (US3/FR4):
crossword, find-a-word, comics.
- **Funnies generated from content** (Q5=X): the generator builds the puzzles
from theme words/clues drawn out of the content and uses content-derived
context to search for / insert a relevant cartoon.
## M3 — AI per-article review page (US9 / Q3=A)
After the AI draft, the couple sees a review page. Present **one article per
card**, each with three actions:
| Card action | Behaviour |
|---|---|
| **Approve** | Keep this article's copy unchanged; others can still be revised |
| **Edit** | Edit this article's copy in place; only it re-renders |
| **Replace** | Supply new copy; it replaces the draft for this article only |
A "Render final newspaper" button appears once every article is approved (or
the couple opts to keep unapproved ones as-is). Approved articles never change
when others are edited (US9/AC9.1.2–3).
## M4 — First-run / empty state (US10 / Q7=A)
With no content yet, the couple sees a friendly guided state:
- A short message ("Welcome — let's make your wedding newspaper").
- A pointer to the `content/` folder + `config.json`.
- A **sample issue** they can generate to see the layout (Q7=A), then replace
with their real content.
This protects the first-run experience (per the designer contribution in
user-stories) without fabricating content in the real output (FR7.4).
## M5 — States handled
Per stage Step 2, the generator's surface must handle these states:
- **empty** — no content: guided empty state (US10).
- **loading** — AI draft running: simple progress/status line (CLI) or spinner
(review page).
- **error** — missing content/config, model unavailable, template failure:
clear one-line message naming the problem + fix path.
- **success** — generated `newspaper.html`, path shown.
- **partial** — AI draft done, some articles reviewed, others pending (the
review page's natural in-between state).
<!-- refined-mockups-after-confirm -->
@@ -0,0 +1,100 @@
# Refined Mockups — Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your UX design decisions.
## Q1: Authoring entry point
How do you want to drive the newspaper generation?
A) A single command run (e.g. `newspaper generate`) that reads a config + content folder and emits `newspaper.html` — CLI-first (recommended)
B) A local browser page that picks content and generates live (no shell, all in-browser)
C) Both — a small CLI plus an optional browser "generate" page
X) Other (please specify)
[Answer]: A
## Q2: Content & metadata input shape
How should you provide content and dynamic issue metadata (masthead title, couple names, date, volume)?
A) A `content/` folder of markdown/txt files plus a small `config.json`/front-matter holding masthead metadata (recommended)
B) One markdown "issue file" that contains everything (articles + metadata front-matter) in a single document
C) Paste text into the browser page, metadata typed in a small form
X) Other (please specify)
[Answer]: A
## Q3: AI per-article review surface
Your stories call for a whole-first-pass AI draft then per-article human review (US9). How should that review be surfaced?
A) After the AI draft, emit a review page/step listing every article with approve / edit / replace controls per article before final render (recommended)
B) Emit a single editable draft markdown file the couple edits directly, then re-render
C) A CLI prompt loop: for each article, ask approve / edit / replace in the terminal
X) Other (please specify)
[Answer]: A
## Q4: Print preview
Do you want a print-preview step to see how the pages break before printing?
A) Yes — a browser "preview + print" page showing the whole newspaper with a Print button (recommended)
B) No — generate straight to the printable HTML and print it directly
C) Preview only in the same generated page; the couple opens it and prints
X) Other (please specify)
[Answer]: B
## Q5: Funnies input format
For crosswords / find-a-word / comics: how should you supply the source so the generator can build the fun section?
A) Structured data (JSON) per puzzle — grid words + clues for crossword, word list for find-a-word; comics as image files + dialogue (recommended)
B) Natural-language markdown describing the fun items, AI turns it into a puzzle
C) Both — structured for puzzles, markdown for describing comics
X) Other (please specify)
[Answer]: X - I want you to generate it from the content. the content will build the articles and the funnies and provide the context for what cartoons to search for and try to insert
## Q6: Accessibility level for the generated page
What accessibility target should the generated newspaper page meet?
A) WCAG 2.1 AA — semantic HTML, heading hierarchy, alt text on images, keyboard-operable where interactive (recommended)
B) Practical baseline — valid semantic HTML + alt text + readable contrast, without a formal WCAG pass
C) No formal accessibility requirement — this is primarily a printed keepsake
X) Other (please specify)
[Answer]: C
## Q7: Empty / first-run experience
When the couple opens the generator with no content yet, what should they see?
A) A friendly guided empty state: short message + a pointer to the content folder / config, plus a small sample issue they can generate to see the layout (recommended)
B) A friendly empty state with guidance only (no auto sample)
C) Just generate a stub page so they see the masthead/layout structure
X) Other (please specify)
[Answer]: A
## Consolidated Summary Confirmation
> Summary of your seven design answers before the mockup artifacts are generated:
>
> - Authoring entry: **CLI-first** — a single `newspaper generate` command reads a config + content folder and emits `newspaper.html` (Q1=A)
> - Content/metadata input: **`content/` folder of markdown/txt + a small `config.json`** (or front-matter) holding masthead metadata (Q2=A)
> - AI per-article review: **a review page/step** listing every article with approve / edit / replace controls per article before final render (Q3=A)
> - Print preview: **no separate preview step** — generate straight to the printable HTML (Q4=B)
> - Funnies input: **generated from the content itself** — the content builds the articles AND the funnies, and provides the context used to search for / insert cartoons (Q5=X)
> - Accessibility: **no formal WCAG requirement** — primarily a printed keepsake (Q6=C)
> - First-run: **friendly guided empty state** with a pointer to content/config plus a small sample issue (Q7=A)
Does this all look correct before I generate the mockup artifacts?
- Looks correct
- Request changes
[Answer]: Looks correct