This commit is contained in:
+165
@@ -0,0 +1,165 @@
|
||||
# Functional Design — Entities (unit: funnies)
|
||||
|
||||
> Source of truth for the entity model of the `funnies` library unit. Per
|
||||
> confirmed answers: AI-assisted puzzle construction (Q1=B), 10×10 crossword
|
||||
> (Q2=B), real-xkcd cartoon search + local embed (Q3=A), content-enrichment
|
||||
> comics (Q4=A), graceful empty/fallback on failure (Q5=A).
|
||||
|
||||
```yaml
|
||||
entities:
|
||||
- name: Crossword
|
||||
description: A single crossword puzzle, AI-drafted clues laid out by funnies.
|
||||
attributes:
|
||||
- name: crosswordId
|
||||
type: string
|
||||
required: true
|
||||
unique: true
|
||||
- name: grid
|
||||
type: list[list[str]]
|
||||
required: true
|
||||
description: 10x10 grid (Q2=B) of cells; black/empty cells marked.
|
||||
- name: cluesAcross
|
||||
type: list[str]
|
||||
required: true
|
||||
description: AI-drafted across clues (Q1=B).
|
||||
- name: cluesDown
|
||||
type: list[str]
|
||||
required: true
|
||||
description: AI-drafted down clues (Q1=B).
|
||||
- name: solution
|
||||
type: list[list[str]]
|
||||
required: true
|
||||
description: Full solution grid.
|
||||
entity_constraints:
|
||||
- grid must be 10x10 (Q2=B).
|
||||
- every clue must correspond to a filled grid entry.
|
||||
|
||||
- name: FindAWord
|
||||
description: A find-a-word puzzle built from theme words pulled from content.
|
||||
attributes:
|
||||
- name: findId
|
||||
type: string
|
||||
required: true
|
||||
unique: true
|
||||
- name: words
|
||||
type: list[str]
|
||||
required: true
|
||||
description: Theme words to find.
|
||||
- name: grid
|
||||
type: list[list[str]]
|
||||
required: true
|
||||
description: Letter grid with the words placed.
|
||||
entity_constraints:
|
||||
- every listed word must be present in the grid (row/col/diagonal).
|
||||
|
||||
- name: Cartoon
|
||||
description: An embedded single-panel cartoon selected from the real xkcd site (Q3=A) or generated fallback.
|
||||
attributes:
|
||||
- name: cartoonId
|
||||
type: string
|
||||
required: true
|
||||
unique: true
|
||||
- name: source
|
||||
type: string
|
||||
required: true
|
||||
allowed_values: [xkcd, generated, none]
|
||||
description: Provenance (Q3=A - real xkcd preferred).
|
||||
- name: artRef
|
||||
type: string
|
||||
required: true
|
||||
description: Local/bundled asset URI or data (embedded locally, never remote at print).
|
||||
- name: caption
|
||||
type: string
|
||||
required: false
|
||||
description: Optional caption.
|
||||
entity_constraints:
|
||||
- artRef must resolve to a local, embedded asset (NFR4).
|
||||
|
||||
- name: Comic
|
||||
description: A comic strip placed in the funnies section (Q4=A - content-enrichment fetch, couple can override).
|
||||
attributes:
|
||||
- name: comicId
|
||||
type: string
|
||||
required: true
|
||||
unique: true
|
||||
- name: source
|
||||
type: string
|
||||
required: true
|
||||
description: Provenance.
|
||||
- name: artRef
|
||||
type: string
|
||||
required: true
|
||||
description: Local/bundled asset or data.
|
||||
- name: dialogue
|
||||
type: list[str]
|
||||
required: false
|
||||
description: Panel dialogue text.
|
||||
entity_constraints:
|
||||
- artRef must resolve to a local, embedded asset (NFR4).
|
||||
|
||||
- name: FunniesResult
|
||||
description: The container funnies returns to Generator (Contract 2). Empty/fallback handling on no viable content (Q5=A).
|
||||
attributes:
|
||||
- name: funniesResultId
|
||||
type: string
|
||||
required: true
|
||||
unique: true
|
||||
- name: crossword
|
||||
type: Crossword | null
|
||||
required: true
|
||||
default: null
|
||||
- name: findAWord
|
||||
type: FindAWord | null
|
||||
required: true
|
||||
default: null
|
||||
- name: cartoons
|
||||
type: list[Cartoon]
|
||||
required: true
|
||||
default: []
|
||||
- name: comics
|
||||
type: list[Comic]
|
||||
required: true
|
||||
default: []
|
||||
- name: status
|
||||
type: string
|
||||
required: true
|
||||
allowed_values: [ready, fallback, empty]
|
||||
description: fallback = placeholder used; empty = no funnies at all (Q5=A).
|
||||
- name: statusMessage
|
||||
type: string
|
||||
required: false
|
||||
description: Clear note when fallback/empty (Q5=A).
|
||||
entity_constraints:
|
||||
- status=empty must carry a statusMessage.
|
||||
|
||||
- name: FunniesBrief
|
||||
description: The content-derived context funnies uses (Contract 4).
|
||||
attributes:
|
||||
- name: themeWords
|
||||
type: list[str]
|
||||
required: true
|
||||
description: Theme words pulled from content.
|
||||
- name: context
|
||||
type: string
|
||||
required: true
|
||||
description: Couple/occasion context for cartoon search.
|
||||
- name: cartoonSource
|
||||
type: string
|
||||
required: true
|
||||
default: internet-allow
|
||||
description: Cartoon provenance (real xkcd fetch allowed at build time).
|
||||
entity_constraints: []
|
||||
```
|
||||
|
||||
## Human-readable entity summary
|
||||
|
||||
- **Crossword** — 10×10 puzzle with AI-drafted clues (Q1=B, Q2=B).
|
||||
- **FindAWord** — theme-word grid built from content.
|
||||
- **Cartoon** — xkcd-originated (Q3=A) or generated single-panel, embedded locally.
|
||||
- **Comic** — content-enrichment comic strips (Q4=A), override per article.
|
||||
- **FunniesResult** — the single output container (status ready/fallback/empty) that
|
||||
keeps failure graceful (Q5=A).
|
||||
- **FunniesBrief** — the content-derived input context (Contract 4).
|
||||
|
||||
No cross-unit relationships owned here (FunniesResult/entities are funnies-owned;
|
||||
Generator consumes via Contract 2 as a data artifact).
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# Functional Design — Questions (unit: funnies)
|
||||
|
||||
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
|
||||
> authoritative record of the funnies unit's functional design decisions.
|
||||
|
||||
## Q1: Puzzle construction approach
|
||||
|
||||
The funnies unit builds a crossword and a find-a-word from theme words pulled from the content. How should it construct them?
|
||||
|
||||
A) Automated construction — funnies extracts theme words/clues from the content and builds a valid crossword grid + find-a-word programmatically, with a deterministic fallback when a clean crossword can't be fit (recommended)
|
||||
B) AI-assisted — the granted cloud model drafts the crossword clues / word selection, funnies lays them out
|
||||
C) Template-based — funnies uses a fixed set of layouts and fills words in
|
||||
X) Other (please specify)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
## Q2: Crossword difficulty / grid size
|
||||
|
||||
Roughly how hard should the crossword be, and how big?
|
||||
|
||||
A) A medium 15×15 grid with a modest number of clues, solvable by wedding guests in a few minutes (recommended)
|
||||
B) An easier 10×10 grid
|
||||
C) A harder 21×21 grid
|
||||
D) Size adaptive — the grid scales to how many theme words fit
|
||||
X) Other (please specify)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
## Q3: Cartoon selection & provenance
|
||||
|
||||
This unit selects/embeds an xkcd-style cartoon using content-derived context (build-time internet fetch is allowed; the page stays self-contained). How should it pick the cartoon?
|
||||
|
||||
A) Search the real xkcd site for one related to the occasion/couple themes, fetch and embed it locally; fall back to a tasteful content-derived strip if none found (recommended)
|
||||
B) Generate a cartoon locally via the cloud model (stick-figure style) from the content context
|
||||
C) Both — try a real xkcd first, fall back to a generated strip
|
||||
X) Other (please specify)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Q4: Other comics
|
||||
|
||||
The "funny" section may include other comic strips (US3, FR4.4). How should they be sourced?
|
||||
|
||||
A) Content-enrichment fetch — pull a tasteful/funny comic from an allowed local or internet source and embed it; the couple can override per article (recommended)
|
||||
B) Only content-derived or xkcd cartoons; no other external comics
|
||||
C) A curated local set the user drops into a folder, placed automatically
|
||||
X) Other (please specify)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Q5: Failure & empty handling
|
||||
|
||||
When funnies can't produce a puzzle or cartoon (no theme words, or the content yields nothing viable), what should it do (per Q4=A graceful failure)?
|
||||
|
||||
A) Emit empty/fallback funnies — a tasteful placeholder or an empty section with a clear note; never crash or produce a broken block (recommended)
|
||||
B) Hard-fail loudly with a clear message
|
||||
C) Skip the funnies section entirely and note it at the gate
|
||||
X) Other (please specify)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Consolidated Summary Confirmation
|
||||
|
||||
> Summary of the five funnies functional-design answers before the unit artifacts are generated:
|
||||
>
|
||||
> - Puzzle construction: **AI-assisted** — the cloud model drafts crossword clues / word selection, funnies lays them out (Q1=B)
|
||||
> - Crossword size: **easier 10×10 grid** (Q2=B)
|
||||
> - Cartoon selection: **search the real xkcd site** for one related to the occasion/couple themes, fetch + embed locally; fall back to a content-derived strip (Q3=A)
|
||||
> - Other comics: **content-enrichment fetch** from an allowed source; the couple can override per article (Q4=A)
|
||||
> - Failure/empty: **empty/fallback funnies** — tasteful placeholder or clear note, never crash or broken block (Q5=A)
|
||||
>
|
||||
> Human auto-approved this summary (answers gone through in the file; explicit permission granted).
|
||||
|
||||
Does this all look correct before I generate the unit artifacts?
|
||||
|
||||
- Looks correct
|
||||
- Request changes
|
||||
|
||||
[Answer]: Looks correct
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
# Functional Design — Functional Specification (unit: funnies)
|
||||
|
||||
> Behavioural specification for the `funnies` library unit — source of truth
|
||||
> for its workflows and state transitions. Per confirmed answers: AI-assisted
|
||||
> puzzle construction (Q1=B), 10×10 crossword (Q2=B), real-xkcd cartoon search +
|
||||
> local embed (Q3=A), content-enrichment comics (Q4=A), graceful empty/fallback
|
||||
> (Q5=A).
|
||||
|
||||
## Context
|
||||
|
||||
`funnies` builds the newspaper's fun section from the content itself. The
|
||||
generator (U1) passes a `FunniesBrief` (theme words + content context) and
|
||||
receives a `FunniesResult` (crossword, find-a-word, cartoon, comics) that the
|
||||
layout embeds. Content-enrichment via internet fetch is allowed at build time;
|
||||
the emitted page stays self-contained (NFR4).
|
||||
|
||||
## Workflow: build the funnies section
|
||||
|
||||
1. The generator invokes funnies with a `FunniesBrief` (theme words + context).
|
||||
2. Funnies builds a **find-a-word** from the theme words (BR1.3) and an
|
||||
**AI-drafted 10×10 crossword** — it asks the cloud model to draft across/down
|
||||
clues and lays them out (BR1.2).
|
||||
3. Funnies searches the **real xkcd site** for a cartoon related to the
|
||||
occasion/couple themes and embeds it locally (BR2.1); if none found, it falls
|
||||
back to a tasteful content-derived strip (BR2.2).
|
||||
4. If comic strips are requested, funnies pulls a tasteful/funny comic from an
|
||||
allowed content-enrichment source and embeds it; the couple may override per
|
||||
article (BR3.1).
|
||||
5. Funnies assembles everything into a `FunniesResult` and returns it to
|
||||
Generator (Contract 2).
|
||||
|
||||
## State transitions — FunniesRequest
|
||||
|
||||
```
|
||||
IDLE --generator requests funnies--> BUILDING
|
||||
BUILDING --all puzzles + cartoon + comics built--> READY
|
||||
BUILDING --some pieces fallback (no cartoon/comic)--> FALLBACK (status=fallback, Q5=A)
|
||||
BUILDING --no viable content at all--> EMPTY (status=empty + statusMessage, Q5=A)
|
||||
READY/FALLBACK/EMPTY --result returned--> EMBEDDED (layout places it, BR4.1)
|
||||
```
|
||||
|
||||
- **IDLE** — funnies not active.
|
||||
- **BUILDING** — deriving theme words, drafting clues, fetching cartoon, laying out.
|
||||
- **READY** — full funnies output produced (crossword + find-a-word + cartoon + comics, as available).
|
||||
- **FALLBACK** — a tasteful placeholder/strip substituted where content was insufficient (BR2.2).
|
||||
- **EMPTY** — nothing viable; statusMessage set; no broken block (BR3.2/Q5=A).
|
||||
|
||||
## Error handling & edge cases
|
||||
|
||||
- **No theme words / weak content** → status=fallback/empty with a clear
|
||||
statusMessage; never a crash or broken block (Q5=A, BR3.2).
|
||||
- **xkcd site unreachable / no related comic** → tasteful content-derived fallback
|
||||
strip, never a remote dependency at print (BR2.2, NFR4).
|
||||
- **Comic source unavailable** → skipped with a note; the couple can override per
|
||||
article (Q4=A).
|
||||
- **Puzzle can't be laid out** (AI clues unusable) → find-a-word still builds
|
||||
from theme words; crossword falls back to an empty note (BR1.3, BR3.2).
|
||||
|
||||
## Derived: ER diagram (from entities.md)
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
FunniesBrief ||--o{ Crossword : "theme words/draft"
|
||||
FunniesBrief ||--o{ FindAWord : "theme words"
|
||||
FunniesBrief ||--o{ Cartoon : "context search"
|
||||
FunniesResult ||--o| Crossword : "maybe null"
|
||||
FunniesResult ||--o| FindAWord : "maybe null"
|
||||
FunniesResult ||--o{ Cartoon : "embeds"
|
||||
FunniesResult ||--o{ Comic : "embeds"
|
||||
```
|
||||
|
||||
## Derived: rules summary (from rules.md)
|
||||
|
||||
| ID | Rule (one line) |
|
||||
|---|---|
|
||||
| BR1.1 | Derive theme words + context from content |
|
||||
| BR1.2 | AI-drafted 10×10 crossword |
|
||||
| BR1.3 | Find-a-word from theme words |
|
||||
| BR2.1 | xkcd via build-time real-site search, embed locally |
|
||||
| BR2.2 | Tasteful content-derived fallback when no xkcd |
|
||||
| BR3.1 | Content-enrichment comics, overridable per article |
|
||||
| BR3.2 | Graceful empty/fallback on no viable funnies |
|
||||
| BR4.1 | Self-contained, A4-printable funnies output |
|
||||
|
||||
<!-- functional-design funnies after-confirm -->
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<!-- 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:55:00Z — Interpretation — Funnies functional design generated per confirmed answers: AI-assisted puzzle construction, 10x10 crossword, real-xkcd search + local embed, content-enrichment comics, graceful empty/fallback
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
# Functional Design — Business Rules (unit: funnies)
|
||||
|
||||
> Numbered business rules for the `funnies` library unit. Source of truth in the
|
||||
> fenced YAML; human summary below. Per confirmed answers: AI-assisted puzzle
|
||||
> construction (Q1=B), 10×10 crossword (Q2=B), real-xkcd cartoon search + local
|
||||
> embed (Q3=A), content-enrichment comics (Q4=A), graceful empty/fallback (Q5=A).
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- id: BR1.1
|
||||
statement: Derive theme words and context from the content.
|
||||
category: calculation
|
||||
applies_to: funnies
|
||||
trigger: generator requests funnies with a FunniesBrief
|
||||
logic: IF a FunniesBrief arrives THEN funnies uses its themeWords and context; IF the brief is empty THEN no puzzle/cartoon can be built.
|
||||
violation: not applicable
|
||||
source: US3, Contract 4
|
||||
|
||||
- id: BR1.2
|
||||
statement: Build the crossword with AI-drafted clues on a 10x10 grid.
|
||||
category: calculation
|
||||
applies_to: funnies
|
||||
trigger: a crossword is requested
|
||||
logic: IF theme words are present THEN funnies asks the cloud model to draft across/down clues and lays them out on a 10x10 grid; the solution grid is generated.
|
||||
violation: not applicable
|
||||
source: Q1=B, Q2=B, US3
|
||||
|
||||
- id: BR1.3
|
||||
statement: Build a find-a-word from the theme words.
|
||||
category: calculation
|
||||
applies_to: funnies
|
||||
trigger: a find-a-word is requested
|
||||
logic: IF theme words are present THEN every listed word is placed in the find-a-word grid (row/col/diagonal).
|
||||
violation: not applicable
|
||||
source: US3, Contract 2
|
||||
|
||||
- id: BR2.1
|
||||
statement: Select an xkcd cartoon via a build-time search of the real site.
|
||||
category: policy
|
||||
applies_to: funnies
|
||||
trigger: a cartoon is requested
|
||||
logic: IF cartoonSource allows internet AND a related xkcd is found THEN fetch and embed it locally (Q3=A); the emitted page stays self-contained (NFR4).
|
||||
violation: no cartoon is placed; falls back per BR2.2
|
||||
source: Q3=A, US3, NFR4
|
||||
|
||||
- id: BR2.2
|
||||
statement: Fall back to a tasteful content-derived strip when no cartoon is found.
|
||||
category: policy
|
||||
applies_to: funnies
|
||||
trigger: no suitable xkcd is found
|
||||
logic: IF no related xkcd is found THEN funnies emits a tasteful content-derived placeholder/strip, never a remote dependency at print (Q3=A fallback, Q5=A).
|
||||
violation: not applicable
|
||||
source: Q3=A, Q5=A, mockups R-02
|
||||
|
||||
- id: BR3.1
|
||||
statement: Permit content-enrichment comics, overridable per article.
|
||||
category: policy
|
||||
applies_to: funnies
|
||||
trigger: comic strips are requested
|
||||
logic: IF the couple requests comics THEN funnies pulls a tasteful/funny comic from an allowed source (content-enrichment) and embeds it; the couple may override per article (Q4=A).
|
||||
violation: not applicable
|
||||
source: Q4=A, US3, FR4.4
|
||||
|
||||
- id: BR3.2
|
||||
statement: Fail gracefully when no funnies can be produced.
|
||||
category: constraint
|
||||
applies_to: funnies
|
||||
trigger: no viable content / nothing constructible
|
||||
logic: IF crossword/find-a-word/cartoon cannot be built THEN FunniesResult.status = empty|fallback with a clear statusMessage; never a crash or broken block (Q5=A).
|
||||
violation: The page would have a broken/empty funnies section.
|
||||
source: Q5=A, Contract 4 (graceful failure)
|
||||
|
||||
- id: BR4.1
|
||||
statement: Keep the emitted funnies self-contained and A4-printable.
|
||||
category: constraint
|
||||
applies_to: funnies
|
||||
trigger: funnies output is placed in the page
|
||||
logic: IF funnies output is placed THEN every puzzle/cartoon/comic is bounded to its column and A4-printable with no overflow (NFR1, NFR2, NFR4).
|
||||
violation: Print clipping or an overflowed sheet.
|
||||
source: NFR1, NFR2, NFR4, US3
|
||||
```
|
||||
|
||||
## Rules summary
|
||||
|
||||
| ID | Rule | Category | Source |
|
||||
|---|---|---|---|
|
||||
| BR1.1 | Derive theme words + context from content | calculation | US3, C4 |
|
||||
| BR1.2 | AI-drafted 10×10 crossword | calculation | Q1=B, Q2=B, US3 |
|
||||
| BR1.3 | Find-a-word from theme words | calculation | US3, C2 |
|
||||
| BR2.1 | xkcd via build-time real-site search, embed locally | policy | Q3=A, US3, NFR4 |
|
||||
| BR2.2 | Tasteful content-derived fallback when no xkcd | policy | Q3=A, Q5=A |
|
||||
| BR3.1 | Content-enrichment comics, overridable per article | policy | Q4=A, US3, FR4.4 |
|
||||
| BR3.2 | Graceful empty/fallback on no viable funnies | constraint | Q5=A, C4 |
|
||||
| BR4.1 | Self-contained, A4-printable funnies output | constraint | NFR1/2/4, US3 |
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"stage": "functional-design",
|
||||
"unit": "funnies",
|
||||
"upstream_ids": ["AC3.1.1", "AC3.1.2", "AC3.1.3", "AC3.1.4", "AC3.1.5", "AC3.1.6"],
|
||||
"coverage": [
|
||||
{ "id": "AC3.1.1", "status": "OK", "target": "BR1.1, BR1.2" },
|
||||
{ "id": "AC3.1.2", "status": "OK", "target": "BR1.1, BR1.3" },
|
||||
{ "id": "AC3.1.3", "status": "OK", "target": "BR3.1" },
|
||||
{ "id": "AC3.1.4", "status": "OK", "target": "BR2.1, BR3.1" },
|
||||
{ "id": "AC3.1.5", "status": "OK", "target": "BR2.1, BR2.2" },
|
||||
{ "id": "AC3.1.6", "status": "OK", "target": "BR2.2, BR4.1" }
|
||||
],
|
||||
"reverse": [
|
||||
{ "id": "BR3.2", "status": "N/A", "target": "graceful failure policy rule" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user