6.2 KiB
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.
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.
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.
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:cloudmay 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 EMITTEDnewspaper.htmlremains 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.
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).
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 | — | — |