Files
2026-09-14 11:57:22 +10:00

6.2 KiB
Raw Permalink Blame History

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: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.

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 — —