A multi-connector daily brief: Calendar plus Gmail plus iMessage events flow through the Wake brain into a TLDR plus source-tagged claims, with semantic search via Hindsight and durable per-day persistence. Fixture-mode fallback when WAKE_MCP_URL is unset so the template runs on a fresh checkout with no live Wake stack reachable.

ck new <name> --template morning-brief --host <name>.highline.work
cd ~/Work/<name>
python3 validate_brief.py             # 9/0/0 expected
.venv/bin/uvicorn server.serve:app    # local preview at :18212

What is in the box

PathPurpose
content/fixtures/connectors-2026-05-03.jsonSynthetic multi-connector dump (4 calendar events, 3 gmail threads, 2 iMessage threads) returned in fixture mode by wake_client.collect_connectors.
content/fixtures/brief-2026-05-03.jsonSynthetic synthesized brief (5 source-tagged claims) returned in fixture mode by wake_client.synthesize_brief. Schema creator_kit.morning_brief.v1.
content/fixtures/search-results.jsonCanned semantic-search results returned in fixture mode by wake_client.search. Schema creator_kit.morning_brief.search.v1.
server/wake_client.pyStdlib-only JSON-RPC client. Hits $WAKE_MCP_URL when set; falls back to fixtures when unset, on URL errors, or on JSON-RPC errors. Three calls: collect_connectors, synthesize_brief, search.
server/render.pyPersistence and brief generation. generate_and_persist(user, date) writes to ~/.creator-kit/briefs/<user>/<date>.json (mode 0o600). CK_BRIEFS_DIR overrides the location for hermetic testing.
server/serve.pyFastAPI server, seven routes (see below). Basic Auth pattern matching essay-series and estimator-demo.
server/templates/{base,index,brief,search}.htmlJinja2 templates. /brief and /search content-negotiate (JSON default, HTML on ?format=html or Accept: text/html).
public/_system/{_system,_patterns,_example}.cssThree-stylesheet load order: vendored CDS primitives plus kit-authored semantic patterns plus example-specific shapes (claim tags, search hit list, search form).
validate_brief.pyPer-template validator. Five check groups: fixture integrity, voice rules, /brief route shape, /search route shape, persistence shape (mode 0o600 plus source-tag coverage). FLAG-skips route checks when fastapi is unavailable.
tests/test_brief.py23 stdlib unittest tests across wake_client fixture mode, render persistence, validator, and routes. Routes auto-skip if fastapi is unavailable.
tests/visual-targets.jsonThree visual-regression targets (index, brief, search) for the kit’s harness.
ck.config.jsonTemplate manifest (port 18212 — avoids essay-series 18210 and estimator-demo 18211).

Routes

MethodPathReturns
GET/health{"status":"ok","briefs":<count>,"wake_brain_configured":"0"} or 1. Open.
GET/HTML index. Latest brief inline plus history list plus search form.
GET/brief?date=<iso>Brief JSON for that date. Default: today. Auto-generates today’s brief if absent. Returns HTML on ?format=html.
GET/briefs/<date>Historical brief by date. Never auto-generates; 404 if absent.
GET/search?q=<query>Semantic-search hits via Hindsight. Returns HTML on ?format=html.
POST/generateTrigger today’s brief generation; persist; return the JSON.
GET/_system/<path>Static CDS chrome assets.

Wake-MCP integration

wake_client.py posts JSON-RPC to $WAKE_MCP_URL (default https://wake.highline.work/mcp) and calls three tools:

When WAKE_MCP_URL is unset, or any of those calls fail (transport, JSON-RPC error, parse failure), the client returns canned fixture data and the persisted brief carries wake_brain_used: false. This keeps the template runnable in CI and ship-test without a live Wake stack — the route shapes are exercised even in fixture mode.

To switch to live mode:

export WAKE_MCP_URL=https://wake.highline.work/mcp
.venv/bin/uvicorn server.serve:app --port 18212 --reload
curl -X POST http://localhost:18212/generate

Persistence

Every generated brief is written to ~/.creator-kit/briefs/<user>/<YYYY-MM-DD>.json (mode 0o600; the per-user dir is mode 0o700). CK_BRIEFS_DIR overrides for hermetic tests.

Briefs accumulate forever. The kit ships no expiry policy — the data is small (a few KB per day) and the user controls disk. If you need rotation, write a cron that prunes anything older than N days.

Voice rules (enforced by validate_brief.py)

Every claim in synthesis.claims[] and every hit in hits[] must carry a tag field with one of:

The validator’s route.brief_shape check rejects any persisted brief whose claims carry a tag outside this set. Same for route.search_shape against hits.

In addition: no emoji, no exclamation points outside fenced code, em-dashes preferred over double-hyphens. Standard kit register; matches essay-series and estimator-demo.

Out of scope (v1)

What forking this template gets you

A working FastAPI surface with four kit-standard patterns:

  1. JSON-RPC client with fixture-mode fallback. wake_client.py is the model — every external service call has a canned-data fallback so the template runs without live infrastructure.
  2. Persistent state at ~/.creator-kit/<feature>/. The same pattern applies to any future template that needs durable per-user state.
  3. Source-tagged claims as a contract. Every synthesized claim and every search hit carries a tag. The validator enforces it; the surface chrome renders the tag in colour. A creator forking this template gets the lane discipline by default.
  4. Three-route shape: index plus item plus history. /, /brief, /briefs/<date>. Drop-in for any per-day artifact (digests, recaps, journal entries).

See also