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,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-13T13:05:00Z — Interpretation — ai-draft NFR: moderate latency (Q1=B), strict isolation (Q2=A, only draft brief to cloud), best-effort graceful fallback (Q3=A), light logging (Q4=A), plain Python+uv (Q5=A). As a library unit, only security + tech-stack artifacts apply (perf/scalability/reliability/observability N/A per produces_kinds).
@@ -0,0 +1,74 @@
# NFR Requirements — Questions (unit: ai-draft)
> Fill in each `[Answer]:` tag. Options A-E plus X (Other). The file is the
> authoritative record of the ai-draft unit's NFR targets.
## Q1: Model call latency tolerance
For the ai-draft unit, how long may a single model call (to `deepseek-v4-flash:cloud`) take to produce a draft article before it's a concern?
A) Relaxed — a draft run may take tens of seconds per article; no strict latency SLA needed (this is a one-shot local generation tool, not a live service) (recommended)
B) Moderate — aim for a few seconds per article, log if it's slower
C) Strict — a hard timeout with retry/failure handling
X) Other (please specify)
[Answer]: B
## Q2: Content privacy / security posture
The granted cloud model is the ONE sanctioned off-machine call (all other content stays local). What security posture applies?
A) Strict isolation — only the draft text/brief goes to the cloud model; nothing else leaves the machine; no secrets or wedding content beyond the draft brief is ever transmitted (recommended)
B) Moderate — brief + context allowed, but flag any unusual exposure for review
X) Other (please specify)
[Answer]: A
## Q3: Reliability / graceful degradation
When the cloud model is unavailable or fails (already decided: graceful empty DraftBundle), how should reliability be handled?
A) Best-effort with graceful fallback — run returns quickly, no retry storm; the paper builds without AI, and a clear message is shown (recommended)
B) Retry a bounded number of times before giving up
X) Other (please specify)
[Answer]: A
## Q4: Observability
How much logging/observability does the local ai-draft need?
A) Light — a concise log per draft run (requested types, success/empty status) enough to debug locally; no metrics/tracing (recommended)
B) More — structured per-article logs with durations
X) Other (please specify)
[Answer]: A
## Q5: Tech stack (already Python via uv)
The generator is a Python library-backed tool running under `uv`. Any constraints on the ai-draft tech stack?
A) Plain Python + the system `uv` runtime, no extra heavy deps beyond what's needed to call the model (recommended)
B) Use a thin HTTP client for the model API, stdlib otherwise
X) Other (please specify)
[Answer]: A
## Consolidated Summary Confirmation
> Summary of the five ai-draft NFR answers before the unit NFR artifacts are generated:
>
> - Model latency: **moderate** — aim for a few seconds per article, log if slower (Q1=B)
> - Content privacy/security: **strict isolation** — only the draft brief/text goes to the cloud model; nothing else leaves the machine (Q2=A)
> - Reliability: **best-effort graceful fallback** — paper builds without AI, no retry storm (Q3=A)
> - Observability: **light** — concise per-run log, no metrics/tracing (Q4=A)
> - Tech stack: **plain Python + uv**, no heavy deps (Q5=A)
>
> Human auto-approved this summary (answers read from the file; explicit permission granted).
Does this all look correct before I generate the unit artifacts?
- Looks correct
- Request changes
[Answer]: Looks correct
@@ -0,0 +1,42 @@
# NFR Requirements — Security (unit: ai-draft)
> Security/posture requirements for the `ai-draft` library unit. Per confirmed
> answers: strict isolation (Q2=A) and best-effort graceful failure (Q3=A).
## NFR9.1 — Strict isolation of the cloud draft call (MUST)
- **Target** — The granted cloud model (`deepseek-v4-flash:cloud`) is the ONE
sanctioned off-machine call. Only the draft brief/text (couple names, occasion,
date, key themes) is transmitted. No other content, secrets, credentials, or
wedding material leaves the machine.
- **Rationale** — Confirmed Q2=A (strict isolation). Everything else in the
pipeline is local (NFR3/NFR4); the model call is the single permitted external
boundary.
- **Verification** — Code-generation must route the draft request only to the
granted model endpoint, with a minimal prompt that contains no secrets.
## NFR9.2 — No secrets/credentials embedded (MUST)
- **Target** — No API keys, tokens, or private data are hard-coded or logged.
- **Rationale** — Security baseline; the tool is local but must not leak secrets.
## NFR9.3 — Local-only elsewhere (MUST)
- **Target** — All non-draft processing stays on-machine; the emitted page and
any DraftBundle never trigger network calls (NFR4).
- **Rationale** — Confirmed posture (Q2=A); the ONLY sanctioned external call is
the draft.
## Threat considerations (advisory)
- The cloud draft is the single data-exposure surface. Keeping the brief minimal
and on-theme (Q4=A from functional design) bounds what leaves the machine.
- A compromised model endpoint could influence draft text, but it cannot read
local files or secrets (the API boundary is draft-in, text-out only).
## Compliance note
No regulated compliance regime applies (local keepsake tool); security here is
data-protection posture (the couple's wedding content stays private) per Q2=A.
<!-- nfr ai-draft after-confirm -->
@@ -0,0 +1,19 @@
# NFR Requirements — Tech Stack Decisions (unit: ai-draft)
> Technology selection for the `ai-draft` library unit. Per confirmed answers:
> plain Python + `uv`, no heavy deps beyond what calls the model (Q5=A).
## Decisions
| Choice | Decision | Rationale |
|---|---|---|
| Language | **Python** | The generator is a Python library-backed local tool running under the working `uv` runtime (3.14). |
| Runtime | **uv** (system) | Already the affirmed primary runtime (`uv` works), matches the whole project. |
| Model client | **Thin HTTP client** | Call the granted cloud model (`deepseek-v4-flash:cloud`) over a minimal HTTP client (stdlib `urllib` or a single lightweight requests-style call). No heavy SDK. |
| Dependencies | **Minimal** | No extra heavy packages; add only what is needed to request a draft and parse the response (Q5=A). |
| Prompting/Draft handling | Plain string assembly of the brief | DraftBrief → model prompt → DraftArticle parse, per functional design. |
## Non-decisions (per produces_kinds)
- No DB (no persistence — the DraftBundle is a file handoff per Contract 5).
- No framework (this is a library unit, not a service).
@@ -0,0 +1,16 @@
{
"stage": "nfr-requirements",
"unit": "ai-draft",
"upstream_ids": ["NFR1", "NFR2", "NFR3", "NFR4", "NFR5", "NFR6", "NFR7", "NFR8", "NFR9"],
"coverage": [
{ "id": "NFR1", "status": "N/A", "target": "printability is the generator/emitted-page responsibility, not the drafting library" },
{ "id": "NFR2", "status": "N/A", "target": "layout integrity is the generator layout concern, not the drafting library" },
{ "id": "NFR3", "status": "OK", "target": "NFR9.3" },
{ "id": "NFR4", "status": "N/A", "target": "zero-network emitted page is the generator's job; ai-draft's single sanctioned cloud call is the exception (NFR9.1)" },
{ "id": "NFR5", "status": "N/A", "target": "content-read policy applies to the generator/review-page file-picker, not the drafting library" },
{ "id": "NFR6", "status": "N/A", "target": "pure dependency-free RENDER is a generator/emitted-page property; the drafting library uses a thin client (Q5=A)" },
{ "id": "NFR7", "status": "N/A", "target": "markdown fidelity is the generator transform's concern" },
{ "id": "NFR8", "status": "N/A", "target": "broadsheet aesthetic is a generator/design-system concern" },
{ "id": "NFR9", "status": "OK", "target": "NFR9.1, NFR9.2, NFR9.3" }
]
}