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,78 @@
# Contract Design — Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your contract decisions.
## Q1: Contract representation
How should the inter-unit contracts (Generator ↔ AiDraft, Generator ↔ Funnies, and the DraftBundle handoff) be specified?
A) Shared-schema contracts in Python (data classes / pydantic-style models) that live with the producer unit and are referenced by consumers — ideal for a local library-backed tool (recommended)
B) A standalone JSON-schema file per contract, imported by all units
C) Formal OpenAPI/AsyncAPI specs — heavier than a local tool needs
X) Other (please specify)
[Answer]: A
## Q2: Integration mechanism
How do the units call each other at the library boundary?
A) Direct module/function calls (the `newspaper` CLI imports the ai-draft and funnies libraries and calls their entry functions in-process) (recommended)
B) Each unit is a separate process, communicating via JSON over a pipe/CLI
C) A message/event bus between units
X) Other (please specify)
[Answer]: A
## Q3: Contract ownership
Who owns each contract spec?
A) Each producer unit owns the contract for data it emits — AiDraft owns the DraftBundle shape, Funnies owns the puzzle/cartoon output shape, Generator owns content/config input shapes (recommended)
B) A shared "contracts" home owned jointly
C) The CLI (Generator) owns all contracts centrally
X) Other (please specify)
[Answer]: A
## Q4: Error / timeout / fallback behaviour
How should the boundaries handle failure (e.g. Ollama unavailable, Funnies finds no cartoon, missing content)?
A) Fail gracefully: each unit returns a clear error/fallback (Ollama down → skip drafting with a message; no cartoon → tasteful placeholder; missing content → guided empty state), never a crash or blank page (recommended)
B) Hard-fail loudly with a clear message and non-zero exit
C) Best-effort only — ignore failures silently
X) Other (please specify)
[Answer]: A
## Q5: Versioning policy
How should internal contract changes be handled (this is a one-shot local tool)?
A) No formal versioning — additive, backward-compatible changes only within/after a build; breaking changes are re-generated in one go (recommended)
B) Semantic versioning of the library API, since content/consumers could persist between runs
C) Locked/immutable — contracts cannot change after review
X) Other (please specify)
[Answer]: A
## Consolidated Summary Confirmation
> Summary of your five contract answers before the contract summary is generated:
>
> - Contract representation: **shared-schema contracts in Python** (data classes / pydantic-style), referenced by consumers (Q1=A)
> - Integration mechanism: **in-process module/function calls** — the `newspaper` CLI imports the ai-draft and funnies libraries (Q2=A)
> - Contract ownership: **each producer owns its contract** — AiDraft owns the DraftBundle shape, Funnies the puzzle/cartoon output, Generator the content/config input shapes (Q3=A)
> - Error/fallback: **fail gracefully** — Ollama down → skip draft with a message, no cartoon → tasteful placeholder, missing content → guided empty state; never crash or blank (Q4=A)
> - Versioning: **no formal versioning** — additive backward-compatible changes; breaking changes regenerated in one go (Q5=A)
>
> Human pre-approved this summary (answers recorded via the file, self-guided mode).
Does this all look correct before I generate the contract summary?
- Looks correct
- Request changes
[Answer]: Looks correct
@@ -0,0 +1,197 @@
# Contract Summary — Wedding Newspaper Generator
> Contracts across the unit boundaries, per confirmed decisions (Q1–Q5 all = A):
> shared-schema contracts in Python (data classes / pydantic-style), in-process
> module/function calls, producer-owned specs, graceful failure with clear
> fallbacks, no formal versioning. All boundaries are internal library
> interfaces for a one-shot local tool — there are no public/external API
> boundaries (nothing is served or deployed).
## Contracts table
| # | Provider Unit | Consumer | Mechanism | Owner |
|---|---|---|---|---|
| 1 | AiDraft (U2) | Generator (U1) | in-process call → returns Draft | AiDraft |
| 2 | Funnies (U3) | Generator (U1) | in-process call → returns FunniesResult | Funnies |
| 3 | Generator (U1) | AiDraft (U2) | in-process call → DraftBrief (input contract) | Generator |
| 4 | Generator (U1) | Funnies (U3) | in-process call → FunniesBrief (input contract) | Generator |
| 5 | Generator (U1) | Review page (external-consumer of DraftBundle) | file handoff → DraftBundle JSON read via native file picker | Generator |
## Per-contract spec
Contracts are shared-schema blocks in Python (pydantic-style dataclasses lived
next to the producer). The shapes below document the agreed payloads.
### Contract 1 — Draft (AiDraft → Generator)
Provider: AiDraft. Data emitted after a local-Ollama draft, consumed by Generator for the per-article review handoff.
```yaml
shared-schema: draft
producer: AiDraft
version: 1
fields:
- name: articles
type: list[DraftArticle]
required: true
- name: generatedAt
type: datetime
required: true
sub-shape: DraftArticle
fields:
- name: articleId
type: string
required: true
- name: type
type: string # lead | article | filler | wellwish | ...
required: true
- name: headline
type: string
required: true
- name: byline
type: string
default: ""
- name: body
type: string
required: true
- name: section
type: string
required: false
```
### Contract 2 — FunniesResult (Funnies → Generator)
Provider: Funnies. Emits the content-derived puzzle + cartoon output, consumed by Generator for the funnies section.
```yaml
shared-schema: funnies-result
producer: Funnies
version: 1
fields:
- name: crossword
type: Crossword | null
default: null
- name: findAWord
type: FindAWord | null
default: null
- name: comics
type: list[Comic]
default: []
sub-shape: Crossword
fields:
- name: grid
type: list[list[str]]
- name: cluesAcross
type: list[str]
- name: cluesDown
type: list[str]
- name: solution
type: list[list[str]]
sub-shape: FindAWord
fields:
- name: words
type: list[str]
- name: grid
type: list[list[str]]
sub-shape: Comic
fields:
- name: id
type: string
- name: art
type: string # local/bundled asset URI or data
- name: dialogue
type: list[str]
```
### Contract 3 — DraftBrief (Generator → AiDraft)
Provider: Generator. The input the CLI passes to AiDraft to request a draft.
```yaml
shared-schema: draft-brief
producer: Generator
version: 1
fields:
- name: contentContext
type: string # content-derived brief for the lead/fillers
- name: model
type: string # granted model deepseek-v4-flash:cloud (CLOUD, sanctioned)
default: deepseek-v4-flash:cloud
- name: requestedArticleTypes
type: list[string]
default: [lead, filler]
```
> **Model note (human ruling 2026-09-13):** `deepseek-v4-flash:cloud` may be a
> **cloud** model and is explicitly sanctioned, treated like the internet-pull
> exception for content enrichment. It is faster than a local model and is
> approved for AI drafting at generation time. This is the one sanctioned
> off-machine call in the pipeline; the EMITTED `newspaper.html` remains
> self-contained and zero-network (NFR4).
### Contract 4 — FunniesBrief (Generator → Funnies)
Provider: Generator. The content-derived context Funnies uses to build puzzles + select/embed a cartoon.
```yaml
shared-schema: funnies-brief
producer: Generator
version: 1
fields:
- name: themeWords
type: list[str] # derived from content
- name: context
type: string # couple/occasion context for cartoon search
- name: cartoonSource
type: string # internet-enrich allowed (human ruling) — fetch at build time, embed locally
default: internet-allow
```
### Contract 5 — DraftBundle JSON (Generator → Review page)
Provider: Generator. The on-disk JSON written by the CLI and read by the
per-article review page via the native file picker (NFR5, ADR-004).
```yaml
shared-schema: draft-bundle
producer: Generator
version: 1
format: json-file
fields:
- name: articles
type: list[DraftArticle]
required: true
- name: generatedAt
type: datetime
required: true
consumed-by:
- role: review-page
mechanism: native file picker (user-granted read), never fetch() of a sibling file
```
## Contract ownership rules
- **Owner of each spec** is its producer unit (Q3=A): the owning library holds
the Python dataclass/schema and version.
- **Consumers reference** the producer-owned shape; they never re-declare a
competing copy.
- **Additive, backward-compatible changes only** within normal builds (Q5=A):
consumers ignore unknown fields. Breaking changes to a contract are agreed and
regenerated in one pass for this self-contained tool (no external consumers to
co-ordinate).
- **Failure contract (Q4=A):** grace, never crash. Ollama unreachable → AiDraft
returns an empty/no-draft result with a message, Generator proceeds without AI.
No cartoon found → Funnies returns a tasteful content-derived placeholder, not
a remote fetch. Missing content → Generator surfaces the guided empty state
(US10). Every boundary returns a clear, typed result; the CLI never exits on a
blank page.
## Open questions
| Contract | Question | Blocks |
|---|---|---|
| 1 (Draft) | Exact per-article type taxonomy (lead/article/filler/wellwish) — confirm at Functional Design | AiDraft, Generator |
| 5 (DraftBundle) | Whether the review page expects a pure-JSON vs schema-hint wrapper | Generator (review page) |
| None blocking | — | — |
<!-- contract-summary-after-confirm -->
@@ -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-13T12:20:00Z — Interpretation — Confirmed contract design (all A, self-guided + user auto-approved summary): shared-schema Python contracts, in-process calls, producer-owned specs, graceful failure, no formal versioning
2026-09-13T12:20:00Z — Interpretation — All boundaries are internal library interfaces for a one-shot local tool; no public/external API boundaries
2026-09-13T12:30:00Z — Interpretation — contract-design review R-01 settled by human: deepseek-v4-flash:cloud is a CLOUD model explicitly sanctioned (like the internet-pull exception, faster than local). It is the one approved off-machine call at generation time; the emitted newspaper.html stays zero-network. Contract 3 annotated accordingly.
@@ -0,0 +1,42 @@
# Delivery Planning — Bolt Plan
> Construction sequence for the wedding newspaper generator, per confirmed
> answers (Q1=A value-core first, Q2=A no formal score, Q3=A one unit per Bolt,
> Q4=B parallel ai-draft + funnies, Q5=A no external blockers). A **Bolt** is
> one build pass over a piece of the work, ending in something that runs.
> Topology is from Units Generation (2.7); this stage chooses the economic
> path through it. Walking skeleton is skipped per affirmed team practice.
## Bolt sequence
| Bolt | Units | Notes / walking-skeleton | Definition of Done | Confidence hypothesis | Expected demo |
|---|---|---|---|---|---|
| Bolt 1 | `generator` (core) | Value core first (Q1=A): CLI, content parse, masthead, layout, emit. Simple content, works end-to-end early. | `newspaper generate <simple content>` emits a self-contained `newspaper.html` that prints to A4 with correct masthead + columns | The core render pipeline hangs together and the paper prints end-to-end with hand-written content | A one-page sample newspaper from simple markdown |
| Bolt 2a | `ai-draft` | Parallel (Q4=B) with Bolt 2b — independent of funnies. | `--draft` calls the granted model and returns a DraftBundle review set | The AI drafting loop (cloud model + per-article DraftBundle) works and stays reviewable | A drafted article set ready for per-article review |
| Bolt 2b | `funnies` | Parallel (Q4=B) with Bolt 2a — independent of ai-draft. | `funnies` builds crossword + find-a-word from content and embeds/selects a cartoon; falls back when none found | Content-derived puzzles + cartoon are produceable and A4-printable | A crossword, a find-a-word, and a selected cartoon block |
| Bolt 3 | `generator` integration | Fold in ai-draft + funnies; add per-article review page + photo embedding. | Full newspaper: AI draft → per-article review → funnies section → photos → multi-page A4 print | The whole chain works end-to-end with all sections and the review handoff | A full multi-page newspaper with funnies, photos, AI drafts, reviewed |
## Why this order (summary)
- **Bolt 1 first** = value core / end-to-end early (Q1=A): the highest-confidence,
central capability lands first, matching the affirmed "no separate walking
skeleton" (the first Bolt is naturally a thin end-to-end slice anyway).
- **Bolts 2a/2b parallel** = the independent, higher-unknown pair (funnies and AI
drafting — the user's stated worries, Q6=B,C) after the core works, so they
can be built and de-risked concurrently.
- **Bolt 3 last** = integration: wire the funnies + AI review into the core and
polish multi-page fidelity. Tackles the print-fidelity worry (Q6=A) as the
finish pass.
## Confidence hypotheses (per Bolt)
- Bolt 1: "The core render pipeline produces a correct, A4-printable paper from
hand-written markdown."
- Bolt 2a: "The AI draft produces acceptable wedding-appropriate copy and the
per-article DraftBundle is reviewable off file://."
- Bolt 2b: "Content-derived crosswords/find-a-words and a selected cartoon print
correctly on A4 with a graceful fallback."
- Bolt 3: "The full newspaper — AI drafts, review, funnies, photos, multi-page
print — works end-to-end with all sections."
<!-- delivery-planning-after-confirm -->
@@ -0,0 +1,90 @@
# Delivery Planning — Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your construction-sequencing decisions.
## Q1: Build-first strategy
We have three units of work: `generator` (the CLI + layout + emit, which depends on the other two), `ai-draft` (optional local/cloud Ollama drafting), and `funnies` (crossword/find-a-word/cartoon). What should we build first?
A) The value core first — `generator` layout/render/emit first (with simple content), then add `ai-draft` and `funnies` later; the paper works end-to-end early (recommended given we skip the separate walking skeleton)
B) The risky parts first — `ai-draft` and `funnies` first (they have the most unknowns: Ollama/cloud model, cartoon search, puzzle generation), then wire them into `generator`
C) The dependencies first — build `ai-draft` and `funnies` first (topologically), then `generator` last
D) A thin end-to-end slice first — one markdown file → one A4 page proving the whole chain, despite practices saying we skip a formal walking skeleton
X) Other (please specify)
[Answer]: A
## Q2: Scoring the work
Should we rank the build with a formal scoring model (value + urgency against size — WSJF-style: higher score ships first)?
A) No formal score — order by a pragmatic mix we decide in the plan (value core first, then the riskier funnies/AI), simplest for a solo local tool (recommended)
B) Yes — a WSJF-style score per unit (weighted: value, risk-reduction, size) so the sequence is evidence-based
X) Other (please specify)
[Answer]: A
## Q3: Bolt size
How should we bundle the work into Bolts (a Bolt is one build pass over a piece of the work, ending in something that runs)?
A) One unit per Bolt — three Bolts (`generator`, `ai-draft`, `funnies`), each independently built and reviewed (recommended)
B) Fewer, larger Bolts — e.g. bundle all three into a couple of passes
C) Thin slices cutting across units (e.g. a minimal crossword in the first pass)
X) Other (please specify)
[Answer]: A
## Q4: Concurrency
Can several Bolts be built at the same time, or one after another?
A) One at a time, reviewed as we go (single-session, you approve each — recommended)
B) Parallel — build `ai-draft` and `funnies` concurrently (they're independent), then `generator` integrates them
X) Other (please specify)
[Answer]: B
## Q5: External blockers
Is anything outside the team going to hold us up (external APIs, data waiting on someone, approvals, another hand-off)?
A) No blockers — the Ollama/cloud model and the internet content-enrichment are things the generator itself calls; we control the timeline (recommended)
B) Yes — there are external dependencies (e.g. specific photos, a particular cartoon license, awaiting content from someone) that could gate a Bolt
X) Other (please specify)
[Answer]: A
## Q6: Biggest worry
What worries you most about this build, so we tackle it early?
A) Print fidelity — getting the broadsheet look, columns, and clean A4 page breaks right (the highest-value surface)
B) The funnies — crossword/puzzle generation from content and the cartoon search are the most unpredictable
C) The AI drafting quality — whether the model produces copy that feels right for the wedding
D) The review handoff — making the per-article review page reliable under the file:// no-server constraint
E) Nothing specific — the scope is clear enough
X) Other (please specify)
[Answer]: B, C, D
## Consolidated Summary Confirmation
> Summary of your six delivery-planning answers before the construction Bolt plan is generated:
>
> - Build-first strategy: **value core first** — the `generator` layout/render/emit first with simple content, then `ai-draft` and `funnies` added later; the paper works end-to-end early (Q1=A)
> - Scoring: **no formal score** — a pragmatic mix we decide in the plan (Q2=A)
> - Bolt size: **one unit per Bolt** — three Bolts (`generator`, `ai-draft`, `funnies`), each independently built and reviewed (Q3=A)
> - Concurrency: **parallel** — `ai-draft` and `funnies` built concurrently (they're independent), then `generator` integrates them (Q4=B)
> - External blockers: **none** — we control the timeline (Q5=A)
> - Biggest worries (tackle early): **funnies unpredictability, AI drafting quality, review-handoff reliability** (Q6=B,C,D)
>
> Human pre-approved this summary (answers read from the file; explicit permission granted to auto-approve).
Does this all look correct before I generate the Bolt plan artifacts?
- Looks correct
- Request changes
[Answer]: Looks correct
@@ -0,0 +1,22 @@
# Delivery Planning — External Dependency Map
> Anything outside the team that could gate a Bolt. Per Q5=A, **no external
> blockers** — we control the timeline.
## Dependency map
| Bolt | External dependency | Owner | Lead time | Slip handling |
|---|---|---|---|---|
| Bolt 2a (`ai-draft`) | The granted AI model (`deepseek-v4-flash:cloud`), called by the generator | The tool itself (sanctioned cloud call, human-approved) | Instant (on demand) | If model unreachable: `ai-draft` returns a clear no-draft result; the paper builds without AI (graceful failure per Q4/A in contracts). Slip does not gate the core. |
| Bolt 2b (`funnies`) | Internet content-enrichment (cartoon search / comic source), a build-time fetch allowed by human ruling | The tool itself | Instant (on demand) | If no suitable cartoon found: tasteful content-derived placeholder, never a remote dependency at print (NFR4). Does not gate the build. |
## Notes
- No approvals, no people, no data windows, and no other teams gate any Bolt.
- The two "external" touchpoints are both things the generator itself calls at
**generation time** and both degrade gracefully, so they are **not** blocking
dependencies — consistent with Q5=A.
- Photos (US4) are user-supplied inputs; if absent the layout renders without
them (FR7.4), so they don't gate the build either.
## (effectively empty — fully AI/self-contained)
@@ -0,0 +1,16 @@
<!-- 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-13T12:40:00Z — Interpretation — Confirmed sequencing: value core (generator) first, then parallel ai-draft+funnies, then integration. No formal WSJF. No external blockers. Worries: funnies, AI quality, review-handoff.
2026-09-13T12:40:00Z — Tradeoff — Bolt order deviates from strict 2.7 topology: generator (which topologically depends on the other two) ships first per Q1=A value-core; captured in risk-and-sequencing-rationale.
@@ -0,0 +1,54 @@
# Delivery Planning — Risk & Sequencing Rationale
> The why behind the Bolt order. **Bolt** is one build pass over a piece of the
> work, ending in something that runs. The economic order chosen here is: value
> core first, then the higher-unknown parallel pair, then integration.
## Sequencing argument
The chosen order balances **early working software** (Q1=A) with **de-risking the
uncertain parts early** (Q6=B,C,D — the user's worries: funnies unpredictability,
AI drafting quality, review-handoff reliability).
- **Bolt 1 — `generator` core first (value/skeleton case).** Builds the central,
highest-confidence capability: CLI → content → layout → emit → A4 print. The
paper works end-to-end with hand-written content early, so the highest-value
surface (the printable broadsheet) is proven before the exotic parts land.
This also naturally satisfies the affirmed "no separate walking skeleton": the
first Bolt IS a thin end-to-end slice (markdown in → A4 page out) without
calling it one.
- **Bolts 2a/2b — `ai-draft` and `funnies` in parallel (risk-reduction).** The
two most unpredictable units — AI copy quality and content-derived puzzle/
cartoon generation — are independent, so they build concurrently (Q4=B). Both
de-risk the user's stated worries (Q6=B,C) before integration. Each is also
independently testable (DraftBundle contract; puzzle+cartoon output).
- **Bolt 3 — integration last.** Wires the two new units into the core and
completes the full-chain fidelity (multi-page print, photo embed, per-article
review handoff). The review-handoff reliability worry (Q6=D) is addressed here
against the file:// no-server constraint.
## Reference heuristic
No formal WSJF score (Q2=A). The order follows a pragmatic mix:
**value-first + risk-reduction + dependency-aware**. It is coherent with
Reinertsen's CD3-style reasoning (deliver value early while sequencing the high
uncertainty soon enough to learn from it), but we did not compute a numeric score.
## Deviation-from-topology note
The Bolt order **does not strictly follow 2.7's topological order**. Topology
says `generator` depends on `ai-draft` and `funnies`; a strict topological walk
would build ai-draft + funnies first. We deviate deliberately: **Bolt 1 builds
`generator` with simple/sample content first** (no ai-draft/funnies needed for
the core hand-written path), then the independent pair in parallel (Bolt 2a/2b),
then integration (Bolt 3). This is justified by Q1=A (value core early) and the
parallelism in Q4=B. The deviation is captured here as the stage requires.
## Rationale
| Bolt | Rationale |
|---|---|
| Bolt 1 | Value core, highest confidence, end-to-end early |
| Bolt 2a | Independent + user worry (AI quality/drafting) — de-risk |
| Bolt 2b | Independent + user worry (funnies) — de-risk |
| Bolt 3 | Integration + full fidelity (incl. print-fidelity worry Q6=A) |
@@ -0,0 +1,29 @@
# Delivery Planning — Team Allocation
> Which team/mob owns which Bolt. A **mob** is a small team owning a Bolt's
> delivery. Per affirmed team practice, this is a solo local project.
## Allocation
This project is a solo local tool (classic scope — the team-formation stage was
skipped). **All Bolts are executed by the single developer agent**
(`aidlc-developer-agent`), in this session, with you approving as each Bolt
completes.
| Bolt | Owner | Notes |
|---|---|---|
| Bolt 1 (`generator` core) | aidlc-developer-agent | build + verify + present |
| Bolt 2a (`ai-draft`) | aidlc-developer-agent | build + verify + present |
| Bolt 2b (`funnies`) | aidlc-developer-agent | build + verify + present |
| Bolt 3 (integration) | aidlc-developer-agent | build + verify + present |
## Program Board note
Single-team (a solo developer), so there is no Program Board — no
cross-team sequencing or hand-off coordination is required. The Bolt plan's
parallelism (2a/2b) is scheduling, not separate teams.
## Approval rhythm
One approval per Bolt, one at a time as they complete (per Q4=A's "reviewed as we
go" for the core, with 2a/2b built in parallel and each reviewed on return).
@@ -0,0 +1,179 @@
# Domain Design — Component Catalogue
> Components for the wedding newspaper generator, per confirmed decomposition
> (Q1–Q5 all = A): a single `Generator` orchestrator, a distinct optional
> `AiDraft` component (local Ollama only), a distinct `Funnies` component
> (puzzles + cartoon), the review-page handoff via a draft JSON bundle read
> through the native file picker, and lightweight plain-data entities with the
> filesystem as the store.
## Part A — Machine-readable catalogue
```yaml
components:
- name: Generator
summary: >
The CLI entry point and orchestrator: reads config + content, coordinates
layout and rendering, and emits the self-contained printable newspaper.html.
behaviour: >
Reads config.json (masthead metadata) and the content/ folder. Owns content
parsing (markdown/txt), the classic broadsheet layout model, multi-page A4
pagination with clean page breaks, article/section placement, the optional
per-article review handoff, and final zero-network HTML emission. Never sends
content off-machine. Optional AI draft and funnies are delegated to AiDraft
and Funnies respectively.
responsibilities:
- Read + validate config (masthead metadata) and content
- Parse markdown/txt into Article entities
- Build the newspaper layout (columns, pagination, masthead)
- Assemble the funnies section output from Funnies
- Emit self-contained newspaper.html (zero-network, file:// printable)
- Write the AI-draft JSON bundle for the review page (review handoff)
depends_on:
- component: AiDraft
interaction: calls optional local-Ollama draft when --draft is used; receives draft Article set
style: sync
- component: Funnies
interaction: asks for crossword/find-a-word/cartoon built from content theme words + context
style: sync
dependents: []
external_dependencies:
- name: Local filesystem (content/, config.json)
kind: other
purpose: the authoritative content store (Q5=A — filesystem is the store)
entities:
- name: Issue
identifier: issueNumber
attributes: [title, dateLine, issueNumber, volume, coupleNames]
- name: Article
identifier: articleId
attributes: [type, headline, byline, body, section]
- name: MastheadConfig
identifier: configPath
attributes: [title, coupleNames, dateLine, issueNumber, volume]
references:
- entity: Issue
owned_by: Generator
relationship: "each Issue carries the resolved masthead identity"
- name: DraftBundle
identifier: bundlePath
attributes: [articles, generatedAt]
references:
- entity: Article
owned_by: Generator
relationship: "the DraftBundle holds the AI-drafted Article set for review"
- name: AiDraft
summary: >
Optional component that drafts newspaper copy via the local Ollama model.
behaviour: >
Talks only to local Ollama. Given the content-derived context and brief,
it drafts the lead story and filler articles. Produces a DraftBundle the
review page reads. Fully optional — when --draft is absent it does nothing
and the render uses only human-authored content. Never contacts any
external/cloud service; content stays on-machine.
responsibilities:
- Accept the authoring brief (content-derived context + --draft flag)
- Call local Ollama (granted model deepseek-v4-flash:cloud) to draft copy
- Return a DraftBundle (article set) for per-article review
depends_on: []
dependents:
- component: Generator
interaction: invoked by the generator when the couple requests an AI draft
external_dependencies:
- name: Local Ollama
kind: third-party-api
purpose: local model inference for copy drafting (NFR9, local-only)
entities: []
- name: Funnies
summary: >
Component that builds the "fun" section — crossword, find-a-word, and
comic/cartoon placement — generated from the content itself.
behaviour: >
Takes theme words and context drawn from the content (US3/FR4/Q5=X),
produces the crossword grid + across/down clues, a find-a-word grid, and
selects/embeds an xkcd-style cartoon using content-derived context for the
search. A build-time network fetch to enrich content is allowed and
encouraged (human ruling 2026-09-13: no server, not deployed, but internet
data may enhance content) — so a genuine xkcd may be fetched at generation
time and embedded locally. Emits print-ready, A4-bounded, self-contained
outputs; the EMITTED page itself makes zero network requests. If no
cartoon is found, it falls back to a tasteful content-derived decorative
strip rather than blocking the run (mockups R-02 resolution).
responsibilities:
- Derive theme words and context from Content/article text
- Build crossword grid + clues
- Build find-a-word grid
- Select/embed a cartoon (content-context search, local embed)
- Emit A4-printable, self-contained puzzle/comic blocks
depends_on: []
dependents:
- component: Generator
interaction: generator requests funnies output during the layout pass
external_dependencies:
- name: Local filesystem (xcomic assets / bundled art)
kind: other
purpose: source for comics and the embedded xkcd cartoon (no network at print)
entities:
- name: Puzzle
identifier: puzzleId
attributes: [type, grid, cluesAcross, cluesDown, solution, findWords]
```
## Part B — Human-readable view
### Component Diagram
```mermaid
graph LR
Generator -->|"--draft drafts copy"| AiDraft
Generator -->|"theme words and context"| Funnies
AiDraft -->|"DraftBundle"| Generator
Funnies -->|"puzzles and cartoon"| Generator
```
### Component Summary
| Component | Purpose | Depends On | Dependents | Entities Owned |
|---|---|---|---|---|
| Generator | CLI orchestrator: config/content → self-contained newspaper.html | AiDraft, Funnies | — | Issue, Article, MastheadConfig, DraftBundle |
| AiDraft | Optional local-Ollama copy drafting | — | Generator | — |
| Funnies | Content-derived crossword/find-a-word/cartoon | — | Generator | Puzzle |
### Entity Ownership
| Entity | Owning Component | Identifier | Attributes | References |
|---|---|---|---|---|
| Issue | Generator | issueNumber | title, dateLine, issueNumber, volume, coupleNames | — |
| Article | Generator | articleId | type, headline, byline, body, section | — |
| MastheadConfig | Generator | configPath | title, coupleNames, dateLine, issueNumber, volume | Issue |
| DraftBundle | Generator | bundlePath | articles, generatedAt | Article |
| Puzzle | Funnies | puzzleId | type, grid, cluesAcross, cluesDown, solution, findWords | — |
### External Dependencies
| Component | Dependency | Kind | Purpose |
|---|---|---|---|
| Generator | Local filesystem (content/, config.json) | other | authoritative content store (Q5=A) |
| AiDraft | Local Ollama | third-party-api | local model inference (NFR9) |
| Funnies | Local filesystem (comic/cartoon assets) | other | local comic + embedded cartoon source |
### Rationale
| Component | Why a separate building block |
|---|---|
| Generator | Distinct concern (parsing → layout → emit) and distinct lifecycle; its own data ownership and change rate (layout/print changes land here) |
| AiDraft | Distinct concern and change rate (AI integration is optional and isolated); keeping it separate means no-draft runs never touch it |
| Funnies | Distinct concern (content→puzzle heuristics are non-trivial) and change rate; isolated so layout changes don't destabilize puzzle generation |
### Alternatives Rejected (decomposition)
- **Option B (multiple fine-grained components: ContentLoader, FunniesBuilder, LayoutEngine, Renderer)** — rejected: over-fragmentation for a local single-purpose tool; Q1=A chose cohesion. Reconsider if the generator grows into a larger system.
- **Option C (minimal thin script)** — rejected: would collapse puzzle/AI/layout responsibilities into un-testable ball of code; the three-component split keeps testability without over-splitting.
## Deliberate-cycles note
None. The dependency graph is acyclic: Generator → { AiDraft, Funnies }, with the DraftBundle back-reference being a data artifact (written then read via the file picker), not a synchronous call, so it is not a dependency cycle.
<!-- domain-design-after-confirm -->
@@ -0,0 +1,77 @@
# Architecture Decision Records — Wedding Newspaper Generator
> Durable ADR log for the significant design choices made in Domain Design.
## ADR-001: Cohesive Generator component over fine-grained splitting
- **Context** — The generator is a single-purpose, fully-local tool. The team
had to choose between one cohesive orchestrator with separated internal
responsibilities (Q1=A) and several fine-grained independent components
(ContentLoader, FunniesBuilder, LayoutEngine, Renderer).
- **Decision** — Adopt a single `Generator` component that owns content
parsing, the layout model, multi-page rendering, and emission, with its
funnies and AI-draft concerns delegated to two distinct components
(`Funnies`, `AiDraft`).
- **Consequences** — (+) Simpler for a local tool; fewer moving parts;
layout/print changes land in one place. (−) The Generator is larger; if the
tool grows into a larger system this may need splitting later.
- **Alternatives Rejected** — Option B (fine-grained components): over-fragmented
for this scope. Option C (minimal thin script): collapses concerns and loses
testability.
## ADR-002: Distinct optional AiDraft component (local Ollama only)
- **Context** — AI copy generation (US8/US9/FR8) uses local Ollama and must be
fully optional and stay on-machine (NFR9). The team chose whether to isolate it.
- **Decision** — Model `AiDraft` as a distinct, fully-optional component that
talks only to local Ollama and owns the draft + per-article review contract.
When `--draft` is absent it does nothing.
- **Consequences** — (+) No-draft runs never touch AI; the AI integration is
isolated and easier to test/disable; content stays local. (−) One more
component boundary to document.
- **Alternatives Rejected** — Folding Ollama calls into the generator: couples
the optional AI path into the core and makes "no AI" runs carry AI code.
## ADR-003: Funnies generation owned by a distinct component
- **Context** — The funnies (crossword, find-a-word, comics, xkcd cartoon) are
generated from the content itself (US3/FR4/Q5=X), which is non-trivial work.
- **Decision** — Give `Funnies` its own component that derives theme
words/context from content and produces the crossword/find-a-word grids and
the embedded cartoon. It emits A4-printable, self-contained blocks.
- **Consequences** — (+) Puzzle logic is isolated from layout so layout changes
don't destabilize generation; content-derived puzzles feel hand-crafted. (−)
The content→puzzle heuristic is genuinely complex and must handle a no-cartoon
fallback (mockups R-02).
- **Alternatives Rejected** — Folding funnies into the generator: couples puzzle
heuristics into layout; separate puzzle-gen and cartoon-fetch sub-components:
over-split for now.
## ADR-004: Draft bundle read via native file picker (no-server review handoff)
- **Context** — The per-article review page (US9/Q3=A) is a single `file://`
page, and the no-server/zero-network constraints (NFR3–NFR5) forbid `fetch()`
of sibling files and forbid running a server during review (Q1=A / Q4=B).
- **Decision** — The CLI writes the AI draft as a JSON `DraftBundle`; the review
page loads it through the **native file picker** (user-granted read, NFR5).
This resolves the mockups R-01 finding.
- **Consequences** — (+) Review works fully offline, no server, no CORS workaround
(§12 of the empirical file:// research). (−) The couple must pick the draft
bundle in the file dialog once per run.
- **Alternatives Rejected** — Serving a temporary localhost just during review
(violates the no-server decision and adds a process to the flow); CLI prompt
loop instead of an HTML review page (departs from confirmed Q3=A).
## ADR-005: Lightweight plain-data entities, filesystem as store
- **Context** — The project is a local generator with no deployment; the
entities are Issue, Article, MastheadConfig, Puzzle, DraftBundle.
- **Decision** — Model them as lightweight plain-data shapes owned by the
generator; the content `config.json` + `content/` filesystem is the store.
No database.
- **Consequences** — (+) No infra to run; entities are trivially serializable
(the DraftBundle JSON is one shape); fits the static-generator model. (−) No
built-in indexing/querying — not needed at this scale.
- **Alternatives Rejected** — A formalized strict-schema data layer: unnecessary
overhead for a static local tool. No explicit entity model: would make the
review handoff and traceability harder to test.
@@ -0,0 +1,75 @@
# Domain Design — Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your component-decomposition decisions.
## Q1: Generator monolith vs. separated components
How should the generator's build-time work be split as reusable components?
A) A single `Generator` component that reads content and orchestrates rendering, with clearly separated internal responsibilities (config, content, funnies, layout) — cohesive, simple for a local tool (recommended)
B) Split into multiple independent components (ContentLoader, FunniesBuilder, LayoutEngine, Renderer) that the CLI calls in sequence
C) Minimal: one thin script + the emitted static page only
X) Other (please specify)
[Answer]: A
## Q2: AI draft client boundary
The AI copy generation (US8/US9) uses local Ollama. Should it be its own component?
A) Yes — a distinct `AiDraft` component that talks only to local Ollama, owns the draft + per-article review contract, and is fully optional (recommended)
B) No — fold the Ollama calls into the generator as an optional step
X) Other (please specify)
[Answer]: A
## Q3: Funnies generation ownership
The funnies (crossword, find-a-word, comics, xkcd cartoon) are generated from content (Q5=X). Which component should own this?
A) A distinct `Funnies` component that takes theme words/context from the content and produces the crossword/find-a-word grids + selects/embeds a cartoon (recommended)
B) Fold funnies generation into the main generator as a sub-step
C) Separate into puzzle-gen and cartoon-fetch sub-components
X) Other (please specify)
[Answer]: A
## Q4: Review page data handoff (R-01 from mockups)
Under the no-server constraint (Q1=A/Q4=B), how should the per-article review page receive the AI draft for a single file:// page to read it?
A) The CLI writes a draft bundle (JSON) the review-page loads through the native file picker (user-granted, NFR5) — recommended
B) The review page is served briefly by a temporary localhost server just during review, then the final static HTML is produced
C) Merge review into the CLI prompt loop (no HTML review page) — Q3=A in mockups says review page, so this departs from it
X) Other (please specify)
[Answer]: A
## Q5: Entity model for content & issue
For a purely local generator, how should we model the domain entities (an "issue", "article", "config", "puzzle")?
A) Lightweight plain data shapes (Issue, Article, MastheadConfig, Puzzle) owned by the generator, no database — the content filesystem is the store (recommended)
B) A formalized issue/entity model with strict schemas up front
C) No explicit entity model — keep it all file/template-driven
X) Other (please specify)
[Answer]: A
## Consolidated Summary Confirmation
> Summary of your five architecture answers before the component catalogue and ADRs are generated:
>
> - Generator decomposition: **single Generator component** with clearly separated internal responsibilities (config, content, funnies, layout) (Q1=A)
> - AI draft client: **distinct, optional AiDraft component** that talks only to local Ollama and owns the draft + per-article review contract (Q2=A)
> - Funnies ownership: **distinct Funnies component** that takes theme words/context from content and produces the crossword/find-a-word grids + selects/embeds a cartoon (Q3=A)
> - Review-page data handoff: **CLI writes a draft JSON bundle** the review page loads through the native file picker (user-granted, NFR5) — resolving mockups R-01 (Q4=A)
> - Entity model: **lightweight plain data shapes** (Issue, Article, MastheadConfig, Puzzle) owned by the generator; the content filesystem is the store (Q5=A)
Does this all look correct before I generate the component catalogue and ADRs?
- Looks correct
- Request changes
[Answer]: Looks correct
@@ -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-13T12:00:00Z — Interpretation — Confirmed decomposition (all A): single Generator orchestrate, distinct optional AiDraft (local Ollama), distinct Funnies (content-derived puzzles + cartoon), draft bundle read via file picker, lightweight entities with filesystem store
2026-09-13T12:00:00Z — Tradeoff — DraftBundle back-reference is a data artifact (written then read via file dialog), not a sync call, so no dependency cycle
2026-09-13T12:05:00Z — Interpretation — Human ruling (learnings): the constraint is no server + not deployed, but pulling data from the internet to ENHANCE content is fine/encouraged (e.g. xkcd cartoons, comics). Resolves review R-01: build-time content enrichment may fetch from the internet; the EMITTED page stays self-contained/printable (fetch happens at generation time, assets embedded locally)
@@ -0,0 +1,16 @@
{
"stage": "domain-design",
"upstream_ids": ["US1", "US2", "US3", "US4", "US5", "US6", "US7", "US8", "US9", "US10"],
"coverage": [
{ "id": "US1", "status": "OK", "target": "Generator" },
{ "id": "US2", "status": "OK", "target": "Generator (MastheadConfig)" },
{ "id": "US3", "status": "OK", "target": "Funnies" },
{ "id": "US4", "status": "OK", "target": "Generator (Article)" },
{ "id": "US5", "status": "OK", "target": "Generator" },
{ "id": "US6", "status": "OK", "target": "Generator" },
{ "id": "US7", "status": "OK", "target": "Generator (MastheadConfig/Issue)" },
{ "id": "US8", "status": "OK", "target": "AiDraft" },
{ "id": "US9", "status": "OK", "target": "AiDraft (DraftBundle), Generator (review handoff)" },
{ "id": "US10", "status": "OK", "target": "Generator (empty state)" }
]
}
@@ -0,0 +1,34 @@
**Collaborator:** aidlc-developer-agent
## Contribution
Assessing code-style conventions, file organization, and layer boundaries.
- The code-style proposal (plain HTML5/CSS/JS, no build step) is sound and matches
the constraint "never published beyond localhost." Avoid introducing any tooling
that would create a build/runtime dependency.
- File organization recommendation for the render pipeline:
- `content/` — one markdown file per newspaper edition (the authored content,
the source of truth).
- `templates/` — the newspaper layout: HTML skeleton + print CSS.
- `templates/print.css` — the A4 print stylesheet (`@page` rules, column
layout, page-breaks); keep it separate from screen styling so print stays
predictable.
- `render.js` (or a small `index.html` that reads content) — the
markdown→HTML transform mapping articles into the newspaper grid.
- `index.html` — the generated/standalone page opened locally.
- Layer boundaries: content (markdown) stays fully separated from presentation
(templates) — one article's text never lives in the stylesheet. The transform
is the only bridge.
- Error handling: markdown-to-HTML must be tolerant of empty sections and
unusual chars without throwing; a missing content file should degrade to a
friendly empty-state, never a blank/broken page.
- Naming: semantic, self-explanatory (`content/`, `templates/`, `render.js`), no
framework conventions needed since there is no framework.
## Positions
- AGREE: language-idiomatic plain HTML5/CSS/JS, no bundler or transpiler.
- AGREE: no deployment.
- OBJECT (minor): the draft lists code style as "no build step" but should also
state the file layout that enforces the content/presentation separation, so
the practice survives into construction.
@@ -0,0 +1,34 @@
**Collaborator:** aidlc-devsecops-agent
## Contribution
Assessing lint/format, secret handling, dependency and supply-chain controls.
- Dependency surface: this is the strong point of a no-build, no-framework
approach — zero third-party runtime dependencies means no supply-chain risk,
no vulnerable transitive packages, no lockfile to keep fresh. The proposal
should make "no external runtime dependencies" an explicit constraint and keep
it that way.
- No secrets: the project is static content; there are no credentials, keys, or
tokens. No credential scanning is needed, but the practice should be stated in
case a local script or environment sneaks in later — never commit anything that
looks like a secret.
- Lint/format: with no toolchain, a formatter requirement is disproportionate.
The practical, proportionate posture is a light manual hygiene rule (consistent
indentation, valid HTML5, no inline `style=` that breaks the print layout)
rather than wiring ESLint/Prettier into an otherwise zero-dependency repo.
- Supply-chain: because everything must run from `file://`, the page must make
**zero network requests** (no CDN fonts, no `<script src="https://...">`). This
is both a security posture (no third-party code executed locally) and a
correctness requirement (it must work offline). This is the single most
important rule for this project.
- Local-only trust: never reference content, fonts, or assets by remote URL;
everything ships with the page.
## Positions
- AGREE: skip the skeleton and keep tooling minimal.
- AGREE: no deployment.
- OBJECT: the draft's "no external dependencies" is present, but it must be
sharpened into an explicit, load-bearing rule — this is a security posture
(nothing ships from the network), not just a stylistic preference, and it
should be a Mandated rule in `discovered-rules.md`.
@@ -0,0 +1,35 @@
**Collaborator:** aidlc-quality-agent
## Contribution
Assessing testing posture, coverage, and quality gates for the wedding newspaper generator.
- The proposed test-after posture is appropriate. This is a static render-and-print
deliverable with no production runtime, so an exhaustive unit suite would be
disproportionate.
- The quality-critical surfaces worth locking:
1. **Render validity** — sample article markdown must always map to complete,
correctly-structured HTML (no dropped sections, no malformed article rows).
2. **Print integrity** — the print stylesheet must keep every newspaper section
inside the A4 sheet with no clipping, no orphaned columns, and correct
page-break behaviour for multi-page output.
3. **Content fidelity** — markdown source → displayed text must round-trip
without mangled escaping (quotes, em-dashes, apostrophes).
- Recommend a small automated check that: parses a fixture markdown set, asserts
the expected article count and section headings appear in the rendered HTML,
and (where a headless print can run) verifies the A4 box model has no overflow.
- Keep coverage expectations modest. The 80% line-coverage floor classic scope
implies applies to runnable logic, not markup; for a no-build HTML generator
that floor is best interpreted as "every transformation function exercised",
not "80% of the stylesheet".
- Quality gate: the deliverable is judged on whether it prints cleanly to A4 and
renders the authored markdown faithfully — that is the acceptance signal, not
a CI pipeline.
## Positions
- AGREE: test-after posture for this project — the value is in render/print
verification, not a broad unit suite.
- AGREE: skipping the walking skeleton — no integration surface to prove.
- OBJECT: the draft should be more explicit that the "testable layer" is the
markdown→HTML transform and the print CSS, so the testing posture reads as
concrete rather than generic.
@@ -0,0 +1,26 @@
# Discovered Rules — Finalized
> Hard constraints surfaced from the project description, the empirical
> research, and the interview. Promoted to `memory/project.md` at the
> affirmation gate.
## Mandated
- ALWAYS generate pure HTML5 + CSS with no build step and no external
dependencies, so the result runs entirely from the local filesystem
(never beyond localhost).
- ALWAYS make the rendered page printable cleanly to A4 from the browser's
print dialog.
- ALWAYS fetch or read content only through user-granted means (native file
picker / drag-and-drop) when the page runs from `file://` — never rely on
`fetch()` of sibling local files, because the opaque-origin policy blocks it
in stock browsers (empirically verified: plain Chromium blocks
`fetch('article.txt')` from a `file://` page, while the native file picker
works).
- ALWAYS make the page work with zero network requests (no CDN fonts, no
remote `<script>`/`<style>`), so it is fully functional offline from `file://`.
## Forbidden
- NEVER reference content, fonts, or assets by remote URL; everything ships
with the page.
@@ -0,0 +1,48 @@
# Evidence
## Project Type
Greenfield. No prior workspace content; the repo is a freshly initialized git
repo with no commits.
## Sources Scanned
- `aidlc/spaces/default/memory/org.md` — framework defaults for the five
practice areas (way of working, walking skeleton, testing posture,
deployment, code style).
- Initial project description: "wedding newspaper generator — author content as
markdown, render as a printable newspaper in pure HTML5+CSS, print to A4 from
the browser. Fully local, never deployed beyond localhost."
- Primary-source web research (whatwg fetch spec, MDN same-origin policy,
CORS handbook) on whether a `file://` page can read sibling local files.
- **Empirical browser test** (2026-09-13): real Chromium driven via node
+ playwright-core. Plain Chromium: `fetch('article.txt')` from a `file://`
page → blocked (`TypeError: Failed to fetch`, CORS from origin 'null');
native file picker → works. Chromium with `--allow-file-access-from-files`:
fetch works but requires a non-default, security-weakening launch flag.
## Participant Evidence
- Lead draft: team-practices.md, discovered-rules.md, evidence.md,
practices-discovery-timestamp.md.
- Support contributions (contributed into the artifacts):
- `contributions/aidlc-quality-agent.md` — test-after posture justified;
renders/print as the testable surfaces; keep A4 containment + markdown
fidelity as the acceptance checks.
- `contributions/aidlc-developer-agent.md` — content/presentation separation
(`content/` vs `templates/`), tolerant transform, plain-HTML file layout.
- `contributions/aidlc-devsecops-agent.md` — zero-dependency = zero
supply-chain risk; no-remote-URLs as a load-bearing security rule; the page
must work offline.
## Interview Decisions
- Q1 Way of working: `C` direct to main (solo local project).
- Q2 Walking skeleton: `B` no separate skeleton step.
- Q3 Testing posture: `C` unit tests for the markdown→HTML transform.
- Q4 Deployment/run: `X` resolved via research + confirmation — file-only
static Python generator emitting a self-contained `newspaper.html`, with an
in-page file-picker live-load path; no server, never beyond localhost.
- Q5 Code style: `C` no style rules, minimal and pragmatic.
- Q6 Content/presentation: `A` separate `content/` and `templates/`.
## Unresolved Uncertainty
- None blocking. The browser `file://` constraint is settled empirically: use
user-granted file access (picker/drag-drop), never `fetch()` of local files,
except under the non-default Chromium flag.
@@ -0,0 +1,19 @@
<!-- 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:07:00Z — GREENFIELD practices discovery running inline; authored lead draft + 3 specialist contributions (quality/developer/devsecops) as contributions/*.md
2026-09-13T11:07:00Z — Tradeoff — zero-dependency no-build approach wins for local-only security (nothing ships from network) at the cost of no automated CI gates; verification leans on render/print checks
2026-09-13T11:22:40Z — Interpretation — Q4 resolved to file-only static Python generator + in-page file-picker after empirical Chromium test: fetch() of sibling files is blocked from file:// (opaque origin) but the native file picker works — user-granted reads bypass CORS
2026-09-13T11:22:40Z — Interpretation — Testing posture is unit tests on the markdown->HTML transform, not a broad suite; print-to-A4 stays the real-world acceptance check
2026-09-13T11:22:40Z — Deviation — aidlc-log answer refused (no HUMAN_TURN seam on this custom fork build); recorded answers directly in the authoritative questions file instead
@@ -0,0 +1,89 @@
# Practices Discovery — Interview Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your practice decisions.
## Q1: Way of Working
How should we plan development work for this project — should we use feature branches that merge back into the main line, or work directly on the main line?
A) Trunk-based with short-lived feature branches (2-3 days max, merge to main)
B) Direct to main for everyday edits; branches only for anything larger
C) Direct to main only — no branches, this is a solo local project
D) Feature branches required for everything, no direct-to-main
E) (depends on project size)
X) Other (please specify)
[Answer]: C
## Q2: Walking Skeleton
Build a thin end-to-end slice first? A walking skeleton is a minimal version that runs the whole way through — one markdown file turning into one printed A4 page — built first to prove the pieces connect before the real features (full layout, many articles, styling) go in.
A) Yes — prove the slice end-to-end before building the full layout
B) No — the layout and content can be built together; no separate skeleton step
X) Other (please specify)
[Answer]: B
## Q3: Testing Posture
What testing posture should this project use? The realistic surfaces worth verifying are that sample markdown renders into correct newspaper HTML and that the print stylesheet stays inside an A4 sheet.
A) Test-after with a small render/print verification check (recommended)
B) No tests — open the page and eyeball it; the print is the acceptance check
C) Unit tests for the markdown→HTML transform only
D) Behaviour-driven: write the article → printed-page scenarios first
X) Other (please specify)
[Answer]: C
## Q4: Deployment
How should this project be "released" or run? This site is local-only by requirement and will never be published beyond localhost.
A) Open the generated HTML directly from the filesystem (file://) — nothing served
B) Serve from a tiny local HTTP server (http://localhost) when viewing
C) Both — static file works, and a start script for a localhost server
X) Other (please specify)
[Answer]: X - RESOLVED (research + user confirmation 2026-09-13): File-only static generator. A Python script (the primary path, using the working `uv` runtime) reads content from content/ and emits a self-contained `newspaper.html`; the page is opened directly via file:// and printed to A4. The generated page also includes a native file-picker / drag-and-drop load path so content can be swapped in live without regenerating — empirically verified working in stock Chromium from file:// (the file picker is user-granted and bypasses the opaque-origin CORS restriction that blocks fetch()). No server, no browser flags, never deployed beyond localhost. The generator may optionally call AI to draft newspaper copy before rendering.
## Q5: Code Style
What code-style and tooling conventions should we keep? The project is pure HTML5 + CSS + vanilla JS with no framework and no build step.
A) Plain HTML5/CSS/JS, no tooling; consistent formatting by hand
B) Plain HTML5/CSS/JS plus a lightweight formatter (e.g. Prettier) if available
C) No style rules — keep it minimal and pragmatic
X) Other (please specify)
[Answer]: C
## Q6: Content ↔ Presentation Separation
Content (markdown articles) and presentation (the newspaper layout + print stylesheet) — should they be kept in separate folders so the layout never gets tangled up with article text?
A) Yes — separate `content/` and `templates/` (recommended)
B) No — keep it all inline; simplest possible layout
X) Other (please specify)
[Answer]: A
## Consolidated Summary Confirmation
> Summary of your six answers before the final practices artifacts are locked:
>
> - Way of working: direct to main (Q1 = C)
> - Walking skeleton: no separate skeleton step (Q2 = B)
> - Testing posture: unit tests for the markdown→HTML transform (Q3 = C)
> - Deployment/run: file-only static Python generator + in-page file-picker live path, no server (Q4 = X, resolved)
> - Code style: minimal/pragmatic, no style rules (Q5 = C)
> - Content/presentation: separate `content/` and `templates/` (Q6 = A)
Does this all look correct before I generate the artifact?
- Looks correct
- Request changes
[Answer]: Looks correct
@@ -0,0 +1 @@
Discovered: 2026-09-13T11:22:40Z at commit (no commits yet — initial working tree)
@@ -0,0 +1,55 @@
# Team Practices — Finalized
> Affirmed team practices for the wedding newspaper generator, produced from
> the interview answers + three specialist reviews, pending affirmation gate.
## Way of Working
Direct to main. This is a solo local project, so everyday edits go straight to
the main line; we use branches only if something larger ever warrants a review
boundary. No feature-branch ceremony for routine work.
## Walking Skeleton
Skip the separate walking-skeleton step. The layout and content are built
together — there is no integration surface to prove, and a thin end-to-end slice
(one markdown file → one rendered A4 page) emerges naturally as the first piece
anyway.
## Testing Posture
Unit tests for the markdown→HTML transform. The primary testable surface is the
renderer: sample article markdown must always map to complete, correctly
structured newspaper HTML with no dropped sections or mangled escaping, and the
print stylesheet must keep content inside an A4 sheet. We write focused tests for
the transform logic rather than an exhaustive suite; the print result remains the
real-world acceptance check.
Methodology: test-after
Ordering: implement the transform and layout, then write and run tests that
verify markdown→HTML fidelity and A4 print containment.
## Deployment
File-only, no server. A Python static generator (primary path, run with the
working `uv` runtime) reads content files and emits a self-contained
`newspaper.html`. The page is opened directly via `file://` and printed to A4
from the browser print dialog. The generated page also includes a native
file-picker / drag-and-drop load path so content can be swapped in live without
regenerating. No server, no browser flags, never deployed beyond localhost. The
generator may optionally call AI to draft newspaper copy before rendering.
## Code Style
Minimal and pragmatic, no style rules beyond staying consistent. Pure HTML5 +
CSS + vanilla JS with no build step and no framework. Keep formatting clean by
hand; do not introduce tooling, transpilers, or bundlers.
## Forbidden
(none affirmed by the team)
## Mandated
- ALWAYS keep the output runnable purely from the local filesystem with no
network requests (affirmed)
@@ -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
@@ -0,0 +1,20 @@
<!-- 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:31:00Z — Interpretation — Multi-page (FR1) is a must-have; scope expanded by Q3 to crosswords, comics, and an xkcd-style front-page cartoon tied to the occasion
2026-09-13T11:31:00Z — Interpretation — AI route is local Ollama, model granted deepseek-v4-flash:cloud; AI draft is optional (FR8) and must never send content to a cloud service
2026-09-13T11:31:00Z — Tradeoff — Photo auto-balancing (Q4=b) gives a natural newspaper flow but adds layout complexity; kept as FR6.2 with text-safe fallback
2026-09-13T11:34:00Z — Interpretation — User resolved OQ1-OQ4 during the learnings turn: crosswords built from user content; genuine xkcd (searched on the real site) as the front-page cartoon; dynamic masthead metadata; whole-first-pass AI then per-article human review
2026-09-13T11:34:00Z — Tradeoff — OQ1 wants a genuine xkcd fetched at generation time, which is a network read at authoring time; reconciled with the local-only/zero-network constraints by embedding the fetched cartoon as a local asset so the delivered page stays self-contained and offline-capable
2026-09-13T11:34:00Z — Deviation — requirements.md edited after the advisory review receipt (relaxed change control: CHANGE_ACCEPTED, no recovery review needed)
@@ -0,0 +1,102 @@
# Requirements Analysis — Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your requirements decisions.
## Q1: Edition length
Should the generator produce a single self-contained page, or support multi-page newspapers that flow across printed A4 sheets?
A) Single page — the whole newspaper is one page, printed on one A4 sheet
B) Multi-page — content flows across multiple A4 sheets with automatic page breaks (recommended for a real newspaper feel)
C) Single long scrolling page that the browser splits across sheets when printing
X) Other (please specify)
[Answer]: B its a must have I want this to work across pages
## Q2: Masthead & issue identity
Does the newspaper need a classic newspaper masthead (title banner, date/issue line, volume)?
A) Yes — a title banner plus a date / issue / "Volume X" line (recommended)
B) Title banner only, no date/issue metadata
C) No masthead — just the article layout
X) Other (please specify)
[Answer]: A
## Q3: Article types / sections
Which content types should the layout support? (choose all that apply)
A) Lead/front-page story
B) Interior news articles with headings + bylines
C) Pull quotes / sidebars / fact boxes
D) A "schedule" or "events" block (e.g. ceremony / reception timetable)
E) A lighter section (messages/letters, well-wishes, memory corner)
X) Other (please specify)
[Answer]: A, B, C, D, E, and X - i want crosswords and comics as well please, I'd love a little xkcd that somehow relates to the main page (you'll have to pull this obvoiusly with context)
## Q4: Photos and images
Should articles support embedded photos/images, or is the newspaper text-focused?
A) Photos supported — images placed per article with captions (recommended)
B) Photos supported, layout auto-balances them into columns
C) Text only — no image support needed for now
X) Other (please specify)
[Answer]: B
## Q5: Visual theme
What visual style should the newspaper have? (This drives the print CSS.)
A) Classic broadsheet: black ink on white/cream, serif headlines and body, column rules (recommended)
B) Cream/off-white paper tint with ink text — a warmer, vintage wedding feel
C) A color-accented modern tabloid (one accent color for section headers)
X) Other (please specify)
[Answer]: A
## Q6: Auto-generated copy
The generator may optionally call AI to draft the newspaper copy before rendering. Which AI route should we use, given the project must stay fully local with no external services?
A) Local AI via Ollama (runs on the LAN, never leaves the machine) — recommended
B) No AI generation — the script only renders copy I provide
C) An external AI API (cloud LLM) — requires a key and sends content off-machine
X) Other (please specify)
[Answer]: A ollama is on this laptop and ready to go. You are free to use deepseek-v4-flash:cloud
## Q7: Print fidelity
Any fixed target for the printed output (page count, margins, print quality)?
A) Fluid — content auto-flows across as many A4 pages as it needs, with sensible margins and clean page breaks (recommended)
B) Fixed target — fit within a set page count (e.g. exactly 2 A4 pages) regardless of content
C) Tune margins/typography for maximum A4 ink-jet crispness
X) Other (please specify)
[Answer]: Fluid
## Consolidated Summary Confirmation
> Summary of your seven requirements answers before the requirements document is generated:
>
> - Edition length: **multi-page** — content flows across multiple A4 sheets with automatic page breaks (must-have)
> - Masthead: **yes** — title banner plus date / issue / "Volume X" line
> - Article types (all selected): lead story, interior articles with bylines, pull quotes/sidebars/fact boxes, schedule/events block, lighter messages/well-wishes corner — plus **crosswords and comics**, and an **xkcd-style front-page cartoon** relating to the day
> - Photos: **auto-balanced into columns** per article
> - Visual theme: **classic broadsheet** — black ink on white/cream, serif headlines and body, column rules
> - AI route: **local Ollama** (granted `deepseek-v4-flash:cloud` for copy generation)
> - Print fidelity: **fluid** — flows across as many A4 pages as needed, sensible margins, clean page breaks
Does this all look correct before I generate the requirements artifact?
- Looks correct
- Request changes
[Answer]: Looks correct
@@ -0,0 +1,197 @@
# Requirements — Wedding Newspaper Generator
## Intent Analysis
The user is building a wedding-newspaper generator: a tool to take authored
content — the stories, schedules, messages, and inside jokes of a wedding — and
render them as a genuine-feeling newspaper. The deep goal is not just "generate
HTML" but to produce a keepsake that reads, on real paper, like a newspaper
published the day of the wedding: classic broadsheet look, a proper masthead,
crosswords and comics, and a front-page cartoon that ties the occasion together.
The product must be fully local: the generator runs on the couple's laptop,
content is private, and nothing is published beyond the machine. The output is
a self-contained HTML page opened directly (`file://`) and printed to A4 from
the browser's own print dialog. Because a printed multi-page newspaper is the
central deliverable, layout fidelity on paper — clean automatic page breaks,
content flowing across sheets — is a first-class, non-negotiable requirement.
## Functional Requirements
### FR1 — Multi-page newspaper output (MUST)
The generator SHALL produce a newspaper that flows across multiple A4 sheets
with automatic page breaks when content exceeds one page; it MUST NOT be
limited to a single page.
- FR1.1 — When the total content exceeds one A4 sheet, the output MUST
continue onto subsequent sheets with a clean break between pages.
- FR1.2 — Page breaks MUST occur at section or article boundaries where
possible (never splitting a single article's body awkwardly mid-paragraph
unless unavoidable).
- FR1.3 — Each printed page MUST be self-consistent: page numbers, running
headers/footers, and masthead continuation render correctly across sheets.
### FR2 — Classic newspaper masthead (MUST)
- FR2.1 — The front page SHALL render a title (masthead) banner.
- FR2.2 — The masthead SHALL include a date line, an issue number, and a
"Volume X" line.
- FR2.3 — The masthead SHALL be styled in the classic broadsheet manner
(large serif title, rule lines above and below).
### FR3 — Article types (MUST)
The layout SHALL support all of the following content types, mapped from
markdown into newspaper sections:
- FR3.1 — Lead / front-page story (a large featured article on page one).
- FR3.2 — Interior news articles with headings and bylines.
- FR3.3 — Pull quotes, sidebars, and fact boxes (standalone callout styling,
not just body paragraphs).
- FR3.4 — A "schedule" or "events" block (e.g. ceremony / reception
timetable), rendered as a clearly formatted table.
- FR3.5 — A lighter section (messages / letters / well-wishes / memory
corner) distinct in visual weight from hard news.
### FR4 — Crosswords and comics ("the funnies") (MUST)
- FR4.1 — The generator SHALL build crossword puzzles **from content the user
provides** (grid + across/down clues derived from user-supplied words/theme
words and clues), not from a fixed built-in set.
- FR4.2 — The generator SHALL support comic-strip blocks (one or more panels
with art + dialogue), including beyond a single cartoon.
- FR4.3 — Crossword and comic blocks MUST remain printable and legible on A4.
- FR4.4 — The "fun" section is first-class: it may include other word puzzles
(e.g. word search / "find-a-word") and other comics (e.g. Dilbert, Garfield,
or a tasteful equivalent) so long as they are funny and print cleanly.
### FR5 — Front-page cartoon tied to the occasion (MUST)
- FR5.1 — The front page SHALL render an xkcd-style or genuine single-panel
cartoon that relates to the wedding / the couple / the day.
- FR5.2 — The cartoon SHALL be generated with awareness of context: the
couple, the occasion and the day's details inform its subject, so it feels
like it belongs rather than a generic cartoon.
- FR5.3 — The cartoon SHALL be rendered entirely on-machine (see NFR3
Local-only and NFR4 Zero-network); the final printed page never depends on a
remote resource.
- FR5.4 — A genuine xkcd cartoon MAY be sourced from the real xkcd site
(searched for one related to the occasion) as a generation-time input; if
one is fetched, it SHALL be embedded locally so the delivered page remains
self-contained and print-capable offline.
### FR6 — Photos and images (MUST)
- FR6.1 — Articles SHALL support embedded photos/images placed per article.
- FR6.2 — The layout SHALL auto-balance images into the column structure so
they flow naturally with text.
- FR6.3 — Images SHALL support optional captions and byline/credit lines.
### FR7 — Content ingestion (MUST)
- FR7.1 — Content is authored as Markdown (and/or plain text) files and
read by the generator.
- FR7.2 — The generator SHALL produce a single self-contained `newspaper.html` —
the printed artifact — from the ingested content.
- FR7.3 — The generated page SHALL provide a live-load path (native file
picker / drag-and-drop) so alternate content can be swapped in and rendered
without regenerating (matches affirmed practice: user-granted reads only).
- FR7.4 — Photos are optional inputs: when photos are supplied, the layout
SHALL embed them (per FR6); when none are supplied, the layout SHALL generate
cleanly without photographs (never fabricate placeholder images).
- FR7.5 — Newspaper metadata (masthead title, couple names, date, issue
number, volume) is dynamic and SHALL be supplied at generation time rather
than hard-coded, so a new issue can be produced without editing the code.
### FR8 — Optional AI draft support (SHOULD)
- FR8.1 — The generator MAY call a local AI to draft newspaper copy (lead
story, filler articles, pull quotes) before the final render.
- FR8.2 — AI-drafted copy MUST be authored into content exactly as if
hand-written, then flow through the same layout pipeline.
- FR8.3 — The AI route MUST be local Ollama only; the granted model is
`deepseek-v4-flash:cloud`. No content SHALL be sent to an external/cloud
service.
- FR8.4 — AI generation SHALL run the whole first-pass newspaper, then STOP
for per-article human review: the user SHALL be able to approve, request
edits, or replace any single article's copy before the final render. This
review is per-article; approved articles stay unchanged while others are
revised and re-rendered.
## Non-Functional Requirements
- NFR1 (Printability) — The page MUST print cleanly to A4 from the browser's
own print dialog, with no clipping and correct margins. (MUST)
- NFR2 (Layout integrity) — Print CSS MUST keep every section, article,
image, and puzzle inside the sheet with no overflow, orphaned columns, or
broken page breaks. (MUST)
- NFR3 (Local-only) — Everything must run purely from the local filesystem;
the page MUST never be served or deployed beyond localhost. (MUST)
- NFR4 (Zero network) — The page SHALL make zero network requests (no CDN
fonts, no remote `<script>`/`<style>`/assets), so it is fully functional
offline from `file://`. Fonts, images, and any asset ship with the page.
(MUST)
- NFR5 (Content read policy) — Content SHALL be read only through
user-granted means (native file picker / drag-and-drop); the page SHALL NOT
rely on `fetch()` of sibling local files (opaque-origin CORS blocks it in
stock browsers). (MUST)
- NFR6 (Pure, dependency-free render) — The output SHALL be pure HTML5 + CSS
+ vanilla JS with no build step and no external dependencies. (MUST)
- NFR7 (Fidelity to markdown) — The markdown→HTML transform SHALL preserve
content exactly (no mangled quotes, em-dashes, escaping, or dropped
sections). (MUST)
- NFR8 (Classic aesthetic) — The visual theme SHALL be classic broadsheet:
black ink on white/cream, serif headlines and body, and column rules.
(SHOULD, from Q5=a)
- NFR9 (AI isolation) — Any AI generation SHALL be an optional, opt-in step
that never runs when not requested, and SHALL keep all content local
(Ollama). (MUST)
## Constraints
- C1 — The project runs on the wedding couple's local machine, never published.
- C2 — Node.js is available but the Python (3.14) + `uv` runtime is the
affirmed primary path; the generator is a Python static generator.
- C3 — Output must be a self-contained static HTML file; no server process is
required or desired at print time.
- C4 — Only local Ollama is permitted for AI; no external/cloud LLM API.
- C5 — A4 is the fixed print format.
## Assumptions
- A1 — The couple prefers print realism (classic broadsheet) over modern web
styling for the printed keepsake.
- A2 — "Fluid" pagination (flow onto as many A4 pages as needed) is preferred
over forcing a fixed page count (Q7=a).
- A3 — Photos will be supplied as local image files referenced from content;
the generator embeds them (self-contained output).
- A4 — The xkcd-style cartoon is generated by the local AI with contextual
awareness of the couple and day, then rendered as an SVG/embedded image.
- A5 — Crosswords are authored as structured puzzle data (grid + clues)
provided in content, then laid out by the generator.
- A6 — A default visual identity (title, accent) can be supplied by the user
but is not required for the generator to produce a valid first output.
## Out of Scope
- OS1 — Deploying or hosting the newspaper anywhere (web, static host,
printer service).
- OS2 — Sending any content or generation to an external/cloud AI service.
- OS3 — Dynamic server-side rendering or a persistent runtime service.
- OS4 — A full WYSIWYG editor UI (content stays in markdown/text files).
- OS5 — Non-A4 print formats (US Letter, tabloid) in this iteration.
## Open Questions
- OQ1 (RESOLVED) — Crosswords and other "fun" puzzles are **built from user-provided
content**; the fun section may include other comics that are funny, and the
xkcd-style cartoon should be a genuine xkcd searched on the real site when
possible.
- OQ2 (RESOLVED) — Photos are handled when supplied; if none are supplied,
no photos are generated or fabricated.
- OQ3 (RESOLVED) — Newspaper masthead metadata (title, names, date, issue,
volume) is dynamic and brought in at generation time.
- OQ4 (RESOLVED) — AI generation runs the whole first-pass newspaper, then
provides a per-article human review mechanism (approve / request edits /
replace any single article) before the final render.
@@ -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-13T12:10:00Z — Interpretation — Confirmed decomposition (all A, self-guided + user pre-approved continuation): one coarse unit per component (generator/ai-draft/funnies), defined interfaces (DraftBundle JSON handoff), embedded/static single deliverable
2026-09-13T12:10:00Z — Interpretation — DAG: generator depends on ai-draft + funnies; ai-draft and funnies are independent (parallel buildable); acyclic
2026-09-13T12:15:00Z — Interpretation — Human ruling on review R-01: U1 is a library-backed one-shot local tool, never a service/server. Retagged U1 kind service→library in unit-of-work.md + dependency edge block.
@@ -0,0 +1,16 @@
{
"stage": "units-generation",
"upstream_ids": ["US1", "US2", "US3", "US4", "US5", "US6", "US7", "US8", "US9", "US10"],
"coverage": [
{ "id": "US1", "status": "OK", "target": "U1" },
{ "id": "US2", "status": "OK", "target": "U1" },
{ "id": "US3", "status": "OK", "target": "U3" },
{ "id": "US4", "status": "OK", "target": "U1" },
{ "id": "US5", "status": "OK", "target": "U1" },
{ "id": "US6", "status": "OK", "target": "U1" },
{ "id": "US7", "status": "OK", "target": "U1" },
{ "id": "US8", "status": "OK", "target": "U2" },
{ "id": "US9", "status": "OK", "target": "U2, U1" },
{ "id": "US10", "status": "OK", "target": "U1" }
]
}
@@ -0,0 +1,47 @@
# Units Generation — Unit Dependency DAG
> Topology only. Describes what can depend on what; the economic build path
> (Bolt sequence) is decided in Delivery Planning (Stage 2.9).
## Dependency Graph
```
generator ──depends on──▶ ai-draft (optional AI draft, invoked by the CLI)
generator ──depends on──▶ funnies (funnies output, invoked during layout)
ai-draft (no dependencies)
funnies (no dependencies)
```
U2 and U3 are independent libraries the U1 CLI orchestrates. There is no
dependency edge between U2 and U3; they never call each other. The graph is acyclic.
## Integration Points
| From | To | Integration | Style |
|---|---|---|---|
| generator | ai-draft | CLI invokes optional Ollama draft (`--draft`); receives DraftBundle article set | sync |
| generator | funnies | CLI asks for crossword/find-a-word/cartoon built from content theme words + context | sync |
| generator → review page | — | writes DraftBundle JSON; review page reads it via native file picker (ADR-004, NFR5) — data artifact, not a call | file |
## Parallel Development Opportunities
U2 and U3 are fully independent of each other (no dependency edge), so they can
be built (and reviewed) in parallel. U1 depends on both and can be built in
parallel with them up to the integration seams; the DAG admits multiple valid
topological orders. The economic choice of which Bolt ships first is Delivery
Planning's call, not this stage's.
## Machine-readable edge block
```yaml
units:
- name: "generator"
kind: library
depends_on: ["ai-draft", "funnies"]
- name: "ai-draft"
kind: library
depends_on: []
- name: "funnies"
kind: library
depends_on: []
```
@@ -0,0 +1,44 @@
# Units Generation — Unit-of-Work Story Map
> Every user story mapped to the unit that implements it. Cross-cutting stories
> are flagged. Coverage: every story assigned; every unit has stories.
## Story → Unit Map
| Story | Unit | Unit Directory | Notes |
|---|---|---|---|
| US1 — Multi-page newspaper output | U1 generator | u1-generator | layout + pagination |
| US2 — Classic masthead | U1 generator | u1-generator | masthead render |
| US3 — Funnies (crossword/comics/xkcd) | U3 funnies | u3-funnies | puzzle + cartoon build |
| US4 — Photos embedded | U1 generator | u1-generator | photo embed + auto-balance |
| US5 — Content ingestion & self-contained output | U1 generator | u1-generator | content parse + emit |
| US6 — Classic broadsheet aesthetic | U1 generator | u1-generator | print CSS/theme |
| US7 — Dynamic issue metadata | U1 generator | u1-generator | MastheadConfig/Issue |
| US8 — AI copy via local Ollama | U2 ai-draft | u2-ai-draft | local draft |
| US9 — Per-article human review of AI drafts | U2 ai-draft (DraftBundle) + U1 generator (review handoff/file-picker) | u2-ai-draft, u1-generator | cross-cutting: draft data + review page handoff |
| US10 — Friendly first-run / empty state | U1 generator | u1-generator | empty-state |
## Cross-cutting stories
- **US9** spans U2 (produces the DraftBundle) and U1 (writes + hand off the
bundle to the review page via the native file picker). The per-article
review contract lives with U2; the file-picker read handoff lives with U1.
## Story implementation order (within each unit)
- **U1 generator** (natural build order): US7 (metadata) → US2 (masthead) →
US5 (ingestion/emit) → US6 (aesthetic) → US1 (multi-page) → US4 (photos) →
US10 (empty state) → US9-handoff (review page data path).
- **U2 ai-draft**: US8 (local Ollama draft) → US9 (DraftBundle + per-article
review contract).
- **U3 funnies**: US3 (crossword → find-a-word → comics → cartoon embed).
> Implementation *ordering* shown here is a suggested construction sequence
> within each unit; the economic Bolt order across units is Delivery Planning's
> decision (2.9).
## Coverage verification
- Every story (US1–US10) is assigned to one or more units.
- Every unit has at least one story: U1 → US1,2,4,5,6,7,9(handoff),10; U2 →
US8,9; U3 → US3.
@@ -0,0 +1,76 @@
# Units Generation — Unit of Work
> Unit decomposition per confirmed answers (Q1–Q4 = A): one coarse unit per
> component (`generator`, `ai-draft`, `funnies`), defined internal interface
> boundaries (DraftBundle JSON as the review handoff), embedded/static single
> local deliverable. This stage produces the dependency DAG (topology) only;
> economic build ordering is decided in Delivery Planning.
## Unit Definitions
| Unit ID | Directory | Unit | Kind | Complexity |
|---|---|---|---|---|
| U1 | `u1-generator` | Generator | library (one-shot local CLI tool, not a deployable/server) | L |
| U2 | `u2-ai-draft` | AiDraft | library (optional, no standalone runtime) | M |
| U3 | `u3-funnies` | Funnies | library (no standalone runtime) | M |
**Deployment model per unit:** embedded/static — one `newspaper` command plus
templates, all bundled into a single local deliverable. U2 and U3 are libraries
the U1 CLI invokes; there is no separate deployable. Per the human ruling on
architecture-review R-01 (2026-09-13): U1 is a **library-backed one-shot local
tool**, never run as a service or server.
## Unit Responsibilities
### U1 — generator (kind: library)
- Owns the `newspaper` CLI entry point and the full build orchestrator.
- Is a one-shot local library-backed tool — never run as a service or server
(human ruling on review R-01).
- Reads + validates `config.json` (masthead metadata) and the `content/` folder.
- Parses markdown/txt into `Article` entities; owns the classic broadsheet
layout model, multi-page A4 pagination with clean page breaks, article/section
placement, and masthead rendering (US1, US2, US4, US5, US6, US7, US10).
- Assembles the funnies section output produced by U3.
- Writes the AI-draft `DraftBundle` JSON for the review page handoff (US9).
- Emits the self-contained zero-network `newspaper.html`, openable via
`file://` and printable to A4.
- Owns entities: Issue, Article, MastheadConfig, DraftBundle.
### U2 — ai-draft (kind: library)
- Optional local-Ollama copy drafting (US8, US9).
- Talks only to local Ollama (granted model `deepseek-v4-flash:cloud`); never
contacts any external/cloud service; content stays on-machine (NFR9).
- Produces the `DraftBundle` (article set) for per-article review.
- Fully optional — when `--draft` is absent it does nothing.
### U3 — funnies (kind: library)
- Builds the "fun" section from the content itself (US3, FR4, Q5=X).
- Derives theme words + context from content; produces the crossword grid +
across/down clues, a find-a-word grid, and selects/embeds an xkcd-style cartoon
using content-derived context. A build-time network fetch to enrich content is
allowed (human ruling 2026-09-13); the EMITTED page makes zero network
requests. Falls back to a tasteful content-derived strip if no cartoon found.
- Emits A4-printable, self-contained puzzle/comic blocks.
- Owns entity: Puzzle.
## Implementation Notes / Constraints per Unit
- **U1**: must keep the emitted page file://-printable and zero-network
(NFR1–NFR6). Review-page handoff: the per-article review page reads the
DraftBundle via the native file picker (not fetch of siblings) (NFR5, ADR-004).
- **U2**: local-only; must never leak content off-machine (NFR9, ADR-002). The
granted model is `deepseek-v4-flash:cloud`.
- **U3**: puzzle heuristics handle a no-cartoon fallback deterministically
(mockups R-02 / domain-design R-01 resolution). A build-time internet fetch to
enrich content is fine; the shipped page stays self-contained (human ruling).
## Deployment Model
Embedded/static (Q4=A): all three units bundle into single local deliverable
(`newspaper` CLI + templates). No servers, no independent deploys, never
published beyond localhost.
<!-- units-after-confirm -->
@@ -0,0 +1,65 @@
# Units Generation — Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your unit-decomposition decisions.
## Q1: Unit boundary strategy
How should the build units be drawn from the three components (Generator, AiDraft, Funnies)?
A) One unit per component: `generator`, `ai-draft`, `funnies` — each is a separately buildable piece (recommended; clean mapping to Domain Design)
B) One single unit containing all three components (coarse — simplest, one deliverable)
C) Split finer: e.g. `layout`, `render`, `content-loader`, `puzzle-gen`, `cartoon` sub-units
X) Other (please specify)
[Answer]: A
## Q2: Unit granularity
How fine-grained should the units be at build time?
A) Coarse — a handful of units corresponding to the components (3 units) (recommended for a small local tool)
B) Fine-grained — split each component into smaller buildable pieces for maximum reuse/testing granularity
X) Other (please specify)
[Answer]: A
## Q3: Integration points / contracts
How should the units share data and integrate (the contracts between them)?
A) A shared internal interface/module boundary per component (Generator calls AiDraft and Funnies via defined entry points; the DraftBundle JSON is the review handoff contract) (recommended)
B) Loose — units communicate only through files on disk, no shared code interface
C) A package-manager-style library dependency (one unit imports another as a package)
X) Other (please specify)
[Answer]: A
## Q4: Deployment model
For this fully-local tool, what deployment shape should the units take?
A) Embedded/static — one `newspaper` command + templates, all bundled; single local deliverable (recommended, matches CLI-first file-only posture)
B) Split executables that each run independently (no benefit here, but if preferred)
C) Library + thin CLI wrapper (the generator logic as a reusable library, plus a thin `newspaper` script)
X) Other (please specify)
[Answer]: A
## Consolidated Summary Confirmation
> Summary of your four decomposition answers before the unit artifacts are generated:
>
> - Boundary strategy: **one unit per component** — `generator`, `ai-draft`, `funnies` (Q1=A)
> - Granularity: **coarse** — 3 units corresponding to the components (Q2=A)
> - Integration/contracts: **defined internal interface boundaries**; the DraftBundle JSON is the review handoff contract (Q3=A)
> - Deployment: **embedded/static** — one `newspaper` command + templates, single local deliverable (Q4=A)
>
> Plan approval: the human pre-approved the decomposition plan (answers recorded via the file, self-guided mode).
Does this all look correct before I generate the unit artifacts?
- Looks correct
- Request changes
[Answer]: Looks correct
@@ -0,0 +1,25 @@
**Collaborator:** aidlc-design-agent
## Contribution
Reviewing the personas and story draft for UX and persona fidelity.
- The two-persona split (P1 the couple, P2 the guest-as-constraint) is the right
call for a self-contained local tool — the reader is genuinely the printed
output, not an interface user. Keeping P2 as quality attributes on the P1
stories avoids inventing fake guest "tasks" that don't map to real usage.
- Strong points in the draft: US6 locks the classic broadsheet aesthetic as a
testable constraint; US3 gives the funnies first-class weight; US9 makes the
AI-review a first-class flow rather than a background detail.
- Design gap worth folding in: the stories don't yet capture a "no-content"
or "empty issue" authoring state — what the couple sees when they open the
generator with no content yet. A small, friendly empty-state story would
protect the first-run experience.
- Recommend a "first-run empty state" story (Won't/could) so the couple isn't
met with a blank page on first launch.
## Positions
- AGREE: two-persona model with guest as reader-constraint — honest to real usage.
- AGREE: US6 aesthetic-as-requirement — design intent is testable, not hand-wavy.
- OBJECT (minor): missing an empty-state / first-run experience story; consider
a small Could-Have story so first launch is friendly, not blank.
@@ -0,0 +1,36 @@
**Collaborator:** aidlc-developer-agent
## Contribution
Reviewing the personas and stories for implementability and story sizing.
- Story sizing at the FR-group level (Q2=B) reads correctly against this
project's surface area: US1 (multi-page), US3 (funnies), US8/US9 (AI+
review) are the meaty ones, and grouping them this way keeps each story
independently deliverable without splitting hairs.
- Dependencies are well-flagged (US7 feeds US2 for the masthead; US5 feeds
everything; US9 depends on US8). The dependency notes are accurate.
- Implementability notes worth locking:
- US3 (funnies) is the largest chunk — crossword building from user content,
word-search, comic placement, plus the xkcd fetch-and-embed. Consider
whether the crossword and the comics could be accepted as separate stories
if sizing gets tight, though the user's plan keeps them grouped; honoring
the current plan, I note it as a single Must-Have that is genuinely
composite.
- US5.AC5.1.3 (file-picker live load) needs the native file picker path —
this aligns with the affirmed practice and is known-possible, but it is
real work; calling it out so it isn't underestimated.
- US9 (per-article review) implies the AI draft output must be addressable
per article (a data model), not a single blob — the story's ACs already
capture this requirement implicitly; making the per-article boundary
explicit helps the implementer.
## Positions
- AGREE: FR-group story granularity is right for this scope — each US is
independently shippable.
- AGREE: dependency graph is accurate and useful.
- OBJECT (minor): US3 is composite (crossword + word-search + comics + xkcd
fetch/embed) and deserves an explicit note that it may need internal
splitting at Delivery Planning if the effort is large; and US5.3 / US9's
per-article boundary should be made explicit in the ACs so implementers
don't underestimate them.
@@ -0,0 +1,31 @@
**Collaborator:** aidlc-quality-agent
## Contribution
Reviewing the story draft for testability of the acceptance criteria.
- The BDD Given/When/Then acceptance criteria are generally well-formed and
each maps to a concrete verifiable outcome. US6, US1, and US5 are the most
testable — they map directly to observable print/layout behavior.
- Gaps in testability worth flagging:
- US2.AC2.1.3 and US7.AC7.1.2 (dynamic metadata) need a clear "observed
changed output" assertion — the criterion reads well but should explicitly
note that the change is verified by re-running generation with new values,
not by any code change.
- US3.AC3.1.4 ("genuine xkcd related to the occasion") is the weakest
criterion — "related" and "funny" are subjective. Recommend the story note
verification as a human-visible check (the couple judges the cartoon before
print) rather than an automated assertion, and keep the technical AC
(embedded locally, AC3.1.5) as the automated one.
- US9's per-article criteria (AC9.1.2–3) are strong — approving one and
revising another maps cleanly to a test.
- Recommend strengthening US3.AC3.1.4 by splitting the subjective "fits the
occasion" judgment to a human gate step, and keeping the verifiable
embed/local AC automated.
## Positions
- AGREE: most ACs are concrete and testable; the layout/print ones are best.
- AGREE: US9's per-article acceptance criteria are well-scoped.
- OBJECT (minor): US3.AC3.1.4 couples a subjective ("related/funny") judgment
with an objective (embedded local) assertion in one criterion; split them so
the human-visible check is separate from the machine-checkable one.
@@ -0,0 +1,16 @@
<!-- 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:40:00Z — Interpretation — Plan: 2 personas, story-per-FR-group, editor-focus, dedicated AI-review group (user answers B,B,B,A)
2026-09-13T11:40:00Z — Tradeoff — Mob contributed 3 minors: designer (empty-state Could-Have), developer (US3 composite + per-article boundary), quality (split subjective/objective in AC3.1.4); all integrated, no mid-stage triage needed
@@ -0,0 +1,55 @@
# Personas
> Two personas affirmed for the wedding newspaper generator (Q1=B). Per the
> plan (Q3=B), the editor/generator flow drives the stories; the reader
> experience is a quality constraint, not a separate story set.
## P1 — The Couple (editor & publisher)
**Role**: The primary user. One (or both) of the newlyweds authors the content,
runs the generator, reviews the AI draft, and prints the newspaper.
**Goals**
- Produce a keepsake newspaper that captures the wedding day, their story, and
their guests' messages in one printed artifact.
- Make it feel like a real newspaper (classic broadsheet, masthead, funnies).
- Keep it private and fully local — nothing about their wedding leaves the machine.
- Generate a whole first pass quickly, then polish individual articles.
**Pain points**
- Time pressure around the wedding — wants drafting accelerated by AI, but
control over each article's final copy.
- Not a developer — needs a simple "drop content in, run, print" flow.
- The fun section (crosswords, comics) must feel hand-crafted for *them*, not
generic.
**Context**: Uses the local Ollama AI (granted `deepseek-v4-flash:cloud`) for
drafts; supplies markdown/txt content, optional photos, and dynamic masthead
metadata per issue.
## P2 — The Guest (reader)
**Role**: The person who receives/flips through the printed newspaper. Not a
user of the generator — a reader of its output. Represented as a quality
constraint on the editor flow (Q3=B).
**Goals**
- Enjoy reading it: clear layout, legible print, natural column flow, no clipped
articles.
- Recognize the couple and the occasion in the content and the cartoon.
- Be able to attempt the crossword and enjoy the comics.
**Pain points**
- Clipped text, broken page breaks, or unreadable small print would ruin the
keepsake.
- A cartoon or comic that feels unrelated to the couple would feel off.
**Context**: Receives the finished, printed A4 newspaper. Their experience is
locked in via print/readability acceptance criteria on the editor stories.
## Persona Relationships & Priority
- P1 (the couple) is the **primary** persona — all stories serve their authoring
flow. This is definitive for this project.
- P2 (the guest) is **secondary**, enforced as quality attributes (legibility,
clean page breaks, content fidelity) on the P1 stories, not as separate
guest-facing stories.
@@ -0,0 +1,181 @@
# User Stories — Wedding Newspaper Generator
> Two personas: **P1 The Couple (editor/publisher)**, **P2 The Guest (reader,
> quality constraint)**. Story-per-FR-group granularity (Q2=B), editor-flow
> focus (Q3=B), dedicated AI per-article-review group (Q4=A). INVEST-compliant,
> BDD acceptance criteria, MoSCoW priority.
## US1 — Multi-page newspaper output (FR1) — Must Have
**As** the couple (P1), **I want** the generator to flow my content across as
many A4 pages as it needs with clean automatic page breaks, **so that** my
newspaper can be as long as the content deserves and prints as a real
multi-page broadsheet.
- **AC1.1.1** — Given content that exceeds one A4 sheet, when I generate and print,
then the output continues onto subsequent sheets with a clean break between pages.
- **AC1.1.2** — Given a long article, when pagination runs, then the break falls at
a section/article boundary, never splitting a single article awkwardly mid-paragraph
unless unavoidable.
- **AC1.1.3** — Given a multi-page issue, when I print, then each page renders page
numbers and running headers/footers correctly and the masthead continues cleanly.
**Dependencies**: none. **INVEST**: Independent (self-contained), Small, Testable.
## US2 — Classic masthead (FR2) — Must Have
**As** the couple (P1), **I want** a classic newspaper masthead with title, date,
issue number, and volume line, **so that** the front page reads as an authentic
broadsheet.
- **AC2.1.1** — Given a generation run, when I supply dynamic masthead metadata
(title, couple names, date, issue, volume), then the front page renders the title
banner.
- **AC2.1.2** — Given the masthead metadata, when the page renders, then a date line,
issue number, and "Volume X" line all appear under the title.
- **AC2.1.3** — Given a new issue with different metadata, when I generate again, then
the masthead reflects the new values without any code change (dynamic, per FR7.5).
**Dependencies**: US7 (dynamic metadata). **INVEST**: Negotiable, Testable.
## US3 — The funnies: crosswords, word puzzles, and comics (FR4, FR5) — Must Have
**As** the couple (P1), **I want** the generator to build crosswords from content I
provide and to place funny comics (including an xkcd-style front-page cartoon tied
to the occasion), **so that** the fun section feels hand-crafted for our wedding,
not generic.
- **AC3.1.1** — Given content I supply (theme words and clues), when I generate, then
a crossword puzzle block renders with a grid, across/down clues, and a solution area.
- **AC3.1.2** — Given the fun section is requested, when I generate, then a word-search /
"find-a-word" puzzle also renders (FR4.4).
- **AC3.1.3** — Given a comic strip is requested, when I generate, then one or more
panels with art and dialogue render and print legibly on A4.
- **AC3.1.4** — Given the front page, when it renders, then a genuine xkcd cartoon
is placed and embedded locally (FR5, machine-checkable: the asset is local, not
a remote URL).
- **AC3.1.5** — Given the couple's review of the cartoon before print, then they
judge whether it fits the occasion and a comic in the fun section is funny before
final render (FR5.2, FR4.4 — human-visible check at the per-article/print review).
- **AC3.1.6** — Given an xkcd is sourced from the real site, when the page is built,
then it is embedded as a local asset so the delivered page remains self-contained
and print-capable offline (FR5.4, NFR4).
**Dependencies**: US1 (print), US7 (content/metadata). **INVEST**: Valuable, Testable.
## US4 — Photos embedded when supplied (FR6, FR7.4) — Must Have
**As** the couple (P1), **I want** photos I supply to be embedded and auto-balanced
into the newspaper columns, **so that** the layout looks natural and, if I supply no
photos, the paper still generates cleanly without them.
- **AC4.1.1** — Given I supply photo files, when I generate, then the photos embed in
the article layout and auto-balance into the column structure without overflowing
the A4 sheet.
- **AC4.1.2** — Given an embedded photo with a caption/credit, when the page renders,
then the caption and credit line render below it.
- **AC4.1.3** — Given I supply no photos, when I generate, then the newspaper renders
cleanly with **no** fabricated or placeholder images (FR7.4).
**Dependencies**: US1. **INVEST**: Independent, Testable.
## US5 — Content ingestion & self-contained output (FR7) — Must Have
**As** the couple (P1), **I want** to drop my markdown/text content in and have the
generator emit one self-contained `newspaper.html`, **so that** I can open it anywhere
from the local filesystem and print it with no server.
- **AC5.1.1** — Given markdown/text content files, when I run the generator, then a
single self-contained `newspaper.html` is produced.
- **AC5.1.2** — Given the generated page, when I open it from `file://` and print, then
it works with zero network requests and no server (NFR3, NFR4).
- **AC5.1.3** — Given the page open locally, when I want to swap in different content,
then I can load it via the native file picker / drag-and-drop without regenerating
(user-granted reads only, NFR5, FR7.3).
**Dependencies**: none. **INVEST**: Independent, Small, Testable.
## US6 — Classic broadsheet aesthetic (NFR8, NFR1, NFR2) — Must Have
**As** the couple (P1), **I want** the newspaper styled as classic broadsheet (black ink
on white/cream, serif headlines/body, column rules) that prints cleanly to A4, **so
that** the keepsake looks authentic and is legible on paper.
- **AC6.1.1** — Given the generated page, when I view it, then the theme is classic
broadsheet: black ink on white/cream, serif headlines and body, column rules (Q5=A).
- **AC6.1.2** — Given the page in the browser, when I choose Print → A4, then every
section, image, and puzzle stays inside the sheet with no clipping, orphaned columns,
or broken page breaks (NFR1, NFR2).
- **AC6.1.3** — Given the print CSS, when content is present, then margins render
correctly and the page uses sensible A4 print margins (NFR1).
**Dependencies**: US1. **INVEST**: Testable.
## US7 — Dynamic issue metadata (FR7.5, OQ3) — Must Have
**As** the couple (P1), **I want** issue metadata (title, couple names, date, issue
number, volume) to be supplied at generation time, **so that** each new issue is
produced without editing code.
- **AC7.1.1** — Given metadata supplied per issue, when I generate, then the masthead
and issue identity use those values.
- **AC7.1.2** — Given a second issue with different metadata, when I generate, then the
output reflects the new values with no code changes.
**Dependencies**: none. **INVEST**: Independent, Small, Testable.
## US8 — AI copy generation via local Ollama (FR8.1–3) — Must Have
**As** the couple (P1), **I want** the generator to draft newspaper copy using local
Ollama (`deepseek-v4-flash:cloud`), **so that** the initial article draft is
accelerated while all content stays on-machine.
- **AC8.1.1** — Given local Ollama is available, when I ask for a draft, then the
generator drafts the lead story and filler articles through the local model.
- **AC8.1.2** — Given a draft is produced, when it is used, then it flows through the
same layout pipeline exactly as hand-written content (FR8.2).
- **AC8.1.3** — Given generation, when AI runs, then **no** content is sent to an
external/cloud service (only local Ollama) (FR8.3, NFR9).
**Dependencies**: US5 (content pipeline). **INVEST**: Valuable, Testable.
## US9 — Per-article human review of AI drafts (FR8.4, OQ4) — Must Have
**As** the couple (P1), **I want** the generator to produce a whole first-pass
newspaper and then let me approve, edit, or replace any single article's copy,
**so that** I keep final control over what goes to print.
- **AC9.1.1** — Given a full first-pass AI newspaper, when generation completes, then
the run stops for per-article review.
- **AC9.1.2** — Given a reviewed article I approve, when it stays in, then its copy is
unchanged while others may still be revised.
- **AC9.1.3** — Given an article I want changed, when I request an edit, then only that
article's copy is revised and re-rendered; approved articles stay unchanged.
- **AC9.1.4** — Given an article I want replaced, when I supply new copy, then the new
content replaces the draft for that article while the rest of the paper is untouched.
- **AC9.1.5** — Given an AI draft was produced, when it is reviewed, then each
article is addressable as its own unit (an article-level data boundary), so any
single article can be approved, edited, or replaced independently (FR8.4).
**Dependencies**: US8 (AI drafts). **INVEST**: Independent, Negotiable, Valuable.
## US10 — Friendly first-run / empty state (design refinement) — Could Have
**As** the couple (P1), **I want** the generator to show a clear, friendly state
when I have no content yet, **so that** my first launch isn't a blank or broken page.
- **AC10.1.1** — Given no content provided, when I run/generate, then the page shows a
short, friendly empty-state message and guidance rather than a blank page.
- **AC10.1.2** — Given I then add content, when I generate again, then the full
newspaper renders normally.
**Dependencies**: US5. **INVEST**: Independent, Small, Testable.
## Won't Have (explicitly out)
- W1 — No remote deployment or hosting of the newspaper.
- W2 — No cloud/external AI.
- W3 — No dynamic server-side rendering service.
- W4 — No full WYSIWYG editor UI.
<!-- finalized-after-confirm -->
@@ -0,0 +1,23 @@
{
"stage": "user-stories",
"upstream_ids": ["FR1", "FR2", "FR3", "FR4", "FR5", "FR6", "FR7", "FR8", "NFR1", "NFR2", "NFR3", "NFR4", "NFR5", "NFR6", "NFR7", "NFR8", "NFR9"],
"coverage": [
{ "id": "FR1", "status": "OK", "target": "US1" },
{ "id": "FR2", "status": "OK", "target": "US2" },
{ "id": "FR3", "status": "OK", "target": "US5, US6" },
{ "id": "FR4", "status": "OK", "target": "US3" },
{ "id": "FR5", "status": "OK", "target": "US3" },
{ "id": "FR6", "status": "OK", "target": "US4" },
{ "id": "FR7", "status": "OK", "target": "US5, US7" },
{ "id": "FR8", "status": "OK", "target": "US8, US9" },
{ "id": "NFR1", "status": "OK", "target": "US6" },
{ "id": "NFR2", "status": "OK", "target": "US6" },
{ "id": "NFR3", "status": "OK", "target": "US5" },
{ "id": "NFR4", "status": "OK", "target": "US3, US5" },
{ "id": "NFR5", "status": "OK", "target": "US5" },
{ "id": "NFR6", "status": "OK", "target": "US5" },
{ "id": "NFR7", "status": "OK", "target": "US5" },
{ "id": "NFR8", "status": "OK", "target": "US6" },
{ "id": "NFR9", "status": "OK", "target": "US8" }
]
}
@@ -0,0 +1,34 @@
# User Stories Assessment
## Decision
**Execute**
## Rationale
User stories add clear value for the wedding newspaper generator. The tool has
multiple distinct user roles with different goals (the couple as editor/publisher,
guests reading/recalling, and the couple as the keepsake owners), user-facing
features throughout, and enough surface area — multi-page layout, the "funnies"
section, crosswords built from content, photo embedding, an AI draft-then-review
loop — that story-level framing improves both scope control and testability well
beyond the flat FR list.
## Factors Considered
- **Project type**: Greenfield user-facing application (a generator + printed artifact).
- **User-facing scope**: High — every feature is user-authored content rendered for a reader.
- **Complexity signals**: Multi-persona (couple/editor, guest reader), multi-page print
layout, puzzle generation from arbitrary content, optional AI pipeline with
per-article review. These need story-level acceptance criteria.
- **Cross-team work**: Internal (design/developer/quality alignment on the layout
and content flow); no external coordination but the mob adds value here.
## Key Areas Where Stories Add the Most Value
1. Authoring and content ingestion (markdown/txt → newspaper sections).
2. The multi-page print behaviour and A4 fidelity (the hardest acceptance surface).
3. The "funnies" section (crosswords built from user content, comics, xkcd cartoon).
4. Photo handling (embedded when supplied, clean when absent).
5. The optional AI draft + per-article human review loop.
6. Dynamic masthead metadata per issue.
## Alternative Coverage if Skipped
Not applicable — the requirements alone are insufficiently testable; this stage
provides the BDD Given/When/Then acceptance criteria that later stages need.
@@ -0,0 +1,77 @@
# User Stories — Story Plan & Questions
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of your story-planning decisions.
## Story Plan Summary
- **Persona development approach**: draft three personas based on the
requirements (the couple-as-editor, the couple-as-keepsake-owners, and the
guest reader), refined here by your answer.
- **Story format**: INVEST (Independent, Negotiable, Valuable, Estimable,
Small, Testable), BDD Given/When/Then acceptance criteria, stable IDs.
- **Prioritization**: MoSCoW (Must / Should / Could / Won't Have) per story.
- **Breakdown approach**: by feature area with story groups aligned to the
FR groups in `requirements.md`.
## Q1: Persona depth
How thoroughly should we define the personas for this project?
A) Three personas (couple-as-editor, couple-as-keepsake-owner, guest reader), each with goals, pain points, and context (recommended)
B) Two personas (the couple, the guest) — keep it lean for a self-contained local tool
C) The couple is the only real persona; guests are implicit readers, not a separate persona
X) Other (please specify)
[Answer]: B
## Q2: Story granularity
How granular should the stories be?
A) Story per FR sub-requirement where meaningful (e.g. one story for "crossword built from user content", one for "xkcd front-page cartoon") — moderately fine-grained (recommended)
B) Story per FR group (fewer, larger stories — e.g. one story covering all of "the funnies")
C) Coarse — a handful of high-level epic stories, expanded later
X) Other (please specify)
[Answer]: B
## Q3: Guest-reader focus
How much weight should the guest-reading experience carry in story definition?
A) Treat guest-reader stories as first-class (printing legibility, nostalgia, easy flipping) alongside the editor flow (recommended)
B) Focus stories on the editor/generator flow; treat reader experience as a quality constraint, not separate stories
C) Reader experience is the whole point — lead with it, editor stories secondary
X) Other (please specify)
[Answer]: B
## Q4: AI-review loop story
The AI draft-then-per-article-review loop (FR8.4) is a notable piece of work. How should we represent it?
A) A dedicated Must-Have story group with an acceptance criterion per mechanism (approve / edit / replace per article) (recommended)
B) Fold it into the general AI-draft story as a Should-Have; the per-article review is a detail, not its own group
C) Keep it explicit but mark it Could-Have if time is tight
X) Other (please specify)
[Answer]: A
## Consolidated Summary Confirmation
> Summary of the four story-plan answers and the plan that follows, before the
> stories and personas artifacts are finalized:
>
> - Persona depth: **two personas** — the couple, and the guest-as-reader (Q1=B)
> - Story granularity: **story per FR group**, fewer larger stories (Q2=B)
> - Guest-reader focus: **editor/generator flow leads**; reader experience is a quality constraint (Q3=B)
> - AI per-article review loop: **dedicated Must-Have story group** (Q4=A)
> - Resulting plan: US1–US9 Must-Have (+ US10 Could-Have empty-state), BDD acceptance criteria, MoSCoW priority, full FR/NFR traceability
Does this all look correct before I finalize the stories and personas artifacts?
- Looks correct
- Request changes
[Answer]: Looks correct