The Warmup
Why "The Warmup"
In the gym, a warmup is not optional. You do not walk under the bar cold. A warmup primes your nervous system, surfaces what is tight, and tells you whether today's session needs to be adjusted before the work begins. It is the ten minutes that makes the next ninety honest.
The Warmup does the same thing for your workday. Before you open your inbox, before you take the first meeting, before you have the conversation that shapes the next quarter — you know what moved. You know who is active. You know what the field looks like this morning.
You do not go under the bar cold.
The name comes from Mission Built, where the principle is: prepare like the result matters, because it does.
When to use this skill
Activate this skill when the user asks you to do any of the following:
- Run their warmup
- Get today's warmup
- Set up the warmup for the first time
- Reconfigure their sources
- Add or remove a source from their brief
- See what sources are in use
Trigger phrases include: "warmup", "run warmup", "run the warmup", "start my warmup", "give me my warmup", "what's in the brief today", "warmup setup", "set up the warmup", "configure the warmup", "warmup config", "add [source] to my warmup", "remove [source] from warmup", "show my warmup sources", "exclude [source] from warmup".
Philosophy
Signal over noise. Every source labeled. Every claim attributed.
The Warmup has a point of view: authoritative sources first, flagged sources clearly marked, nothing buried in volume. A brief that forces you to sort through noise is not a brief — it is a second inbox.
Four principles:
1. Tier before topic. Every item in the brief carries its source's trust tier visually. A Tier 1 government advisory and a Tier 3 community post are not the same weight of evidence. The brief treats them differently because they are different. The user always knows what they are reading.
2. Absence is information. If a source returns nothing today, that is reported. If a source is unavailable, that is reported. A blank section is never padded. Silence from a source that usually speaks is itself a signal.
3. The source panel is not optional. Every run of the brief ends with a full disclosure of every source that was consulted — active, quiet, or excluded. The user must always be able to audit what their brief was built on. This is the contract.
4. Recommendations, not mandates. When the skill recommends sources, it explains why. When it flags a source as lower tier, it says so and says why. The user decides what goes in their brief. The skill's job is to make sure those decisions are informed.
Modes
Three modes. Each is triggered by a natural phrase.
| Mode | When | What this skill does |
|---|---|---|
| SETUP | First time, or reconfiguring from scratch | Establish the user's profile, build their source suite, save to WARMUP.md, run a test brief |
| RUN | Any time the user wants their brief | Read WARMUP.md, fetch live intelligence from each active source, synthesize sections, render the Iron Log artifact |
| CONFIGURE | Modifying sources without full re-setup | Show active sources, apply changes (add / remove / exclude), update WARMUP.md |
Brief types available at SETUP:
| Brief type | Who it's for | Core focus |
|---|---|---|
| CISO | Security executives | Threat actors, CVEs, research, vendor market, regulatory |
| Product Leader | GMs, PMs, anyone steering a product | Company signal, competitors, AI in product, funding, platform risk, vertical-specific |
| Custom | Anyone | User-defined interests, fully flexible source suite |
If no WARMUP.md exists in the project root and the user asks to RUN, prompt
for SETUP first: "No Warmup config found. Run 'warmup setup' to build your
source suite — it takes about two minutes."
SETUP Mode
Trigger: "warmup setup", "set up the warmup", "configure the warmup for the first time"
Step 1 — Choose a starting mode
Ask the user: "Three brief types to choose from — which fits you best? · CISO — cybersecurity executive brief · Product Leader — GM / PM intelligence brief · Custom — describe your own interests and I'll build a source suite around them"
- CISO is the flagship configuration. Pre-loaded with an authoritative source suite curated for security executives. Provide your company name and sector, region, and peer vendors are looked up automatically — one confirmation and you're done.
- Product Leader is built for GMs, PMs, and anyone steering a product. Provide your company name and competitors, vertical, region, and model are researched automatically. Confirm what's right, answer two quick follow-ups, and the brief is ready.
- Custom builds a source suite from scratch around the user's described interests. Takes a few more questions.
- The user can toggle between modes at any time via CONFIGURE.
Step 1b — Get the user's name
Once the mode is chosen, ask: "What's your name? I'll use it in your brief header."
Examples: "Mike", "Sarah", "Alex" — or they can skip with anything like "doesn't matter" or "just leave it blank".
Stored only in the local WARMUP.md, never sent anywhere. Save as name: in the profile. If skipped, leave blank.
Step 2a — If CISO mode
Lead with company name. Auto-fill everything derivable. Ask only what can't be looked up.
Ask: "What's your company name? I'll look up your sector, region, and typical peer vendors automatically. Or say 'skip' and I'll ask instead."
If the user provides a company name:
Call the WebSearch tool with query: "[Company]" company overview industry sector headquarters cybersecurity
From the results, determine:
- Sector — map to one of the Sector Sources table values below (Healthcare, Financial Services, Energy / Utilities, Technology, Government, Manufacturing / OT, Retail, Critical Infrastructure, or the user's own phrasing if it doesn't map cleanly)
- Region — infer from HQ location. Map to: United States, European Union, APAC, Latin America, or Global if multinational
- Peer vendors — based on the company's sector and known competitive landscape, suggest 3–5 security vendors they likely watch (e.g., a healthcare org watches CrowdStrike, Palo Alto, Microsoft Sentinel; a fintech watches Wiz, SentinelOne, Lacework)
Present all findings in a single confirmation message:
"Here's what I found for [Company]: · Sector: [X] · Region: [Y] · Peer vendors to track: [A, B, C] Does that look right? Edit anything or say 'looks good' to continue."
Accept natural-language edits. Update any field the user corrects. Save company, sector, and region to WARMUP.md.
Then ask these two follow-up questions — do not skip them:
Follow-up 1 — People to follow:
"Anyone in the security world you want to follow closely — executives, CISOs, researchers, threat intel voices? I can suggest a few based on [Company]'s space."
Suggest 2–3 relevant names based on sector and company (e.g., known CISOs at peer companies, prominent threat researchers, security journalists). Present as: "People like [A], [B], [C] are worth following if you're in [sector]. Anyone to add, or skip this?"
Save confirmed names to track_people: in WARMUP.md. If skipped, leave blank.
Follow-up 2 — Personal interests:
"Last one — anything you want at the end of your brief that's not work? Sports, markets, a hobby, a team?"
Save to special_interests: in WARMUP.md. If skipped, omit the section. Accept any answer — "skip", "nothing", or an actual interest. Do not pressure.
If the user skips the company name:
Ask these four questions one at a time:
"What industry or sector is your organization in?" Examples: "Healthcare", "Financial Services", "Energy / Utilities", "Technology", "Government", "Manufacturing / OT", "Retail", "Critical Infrastructure" Accept open-form. Map to the Sector Sources table below.
"What region are you primarily operating in?" Examples: "United States", "European Union", "APAC", "Latin America", "Global"
"Any specific vendors or competitors you want to track?" Examples: "Palo Alto, CrowdStrike, Wiz", "Microsoft Sentinel, Splunk, SentinelOne", "skip"
"Anything you want to stay current on outside of work? Totally optional."
Build the source suite from the CISO Source Suite tables below. Add sector-specific sources from the Sector Sources table.
Step 2b — If Product Leader mode
Lead with company name. Auto-fill sector, region, and competitors. Ask only what can't be looked up.
Ask: "What's your company name — and what product or area are you responsible for? I'll look up your sector, region, and top competitors automatically. Or describe it yourself and I'll go from there."
Examples: "Acme Corp, security platform", "Blue Yonder, supply chain", "skip — I'll describe it"
If the user provides a company name:
Call the WebSearch tool with query: "[Company]" product overview market competitors B2B B2C industry
From the results, determine:
- Product focus — what the company primarily builds and sells
- B2B / B2C / platform — infer from business model. Map to:
b2b/b2c/platform/marketplace/hybrid. If ambiguous, note it and ask. - Vertical / customer industry — who the company sells to. Map to the vertical source options below (Security, Fintech, Healthcare, Enterprise SaaS, Developer tools, E-commerce, Logistics, etc.)
- Region — infer from HQ location
- Top competitors — identify 3–5 direct competitors from search results
Present all findings in a single confirmation message:
"Here's what I found for [Company]: · Product: [what they build] · Model: [B2B / B2C / platform] · Vertical: [customer industry] · Region: [Y] · Top competitors: [A, B, C, D] Does that look right? Edit anything or say 'looks good' to continue."
Accept natural-language edits. If B2B/B2C is still unclear after the search, ask: "One quick one — are you selling to businesses, consumers, or is it a platform other developers build on?"
If the user's vertical doesn't map cleanly, ask: "What does a bad week look like for your business — what external event would most disrupt your roadmap?" Use the answer to infer the right vertical section.
Then ask these three follow-up questions — do not skip them:
Follow-up 1 — AI vendors:
"Which AI vendors or tools matter most to your roadmap? I'd default to OpenAI, Anthropic, Google DeepMind, and Meta AI — plus any tools your team uses (Copilot, Cursor, Mistral). Anything to add or drop?"
Save confirmed list to ai_vendors: in WARMUP.md. If skipped, use the default set.
Follow-up 2 — People to follow:
"Anyone you want to track closely — executives, investors, analysts, journalists, or researchers? I can suggest a few based on [Company]'s space."
Suggest 2–3 relevant names based on vertical and company (e.g., for a security platform: relevant VCs, CISOs at peer companies, prominent analysts). Present: "People like [A], [B], [C] are worth following if you're in [vertical]. Anyone to add, or skip this?"
Save confirmed names to track_people: in WARMUP.md. If skipped, leave blank.
Follow-up 3 — Personal interests:
"Last one — anything you want at the end of your brief that's not work? Sports, markets, a hobby, a team?"
Save to special_interests: in WARMUP.md. If skipped, omit the section. Do not pressure.
If the user skips the company name:
Ask these five questions one at a time:
"What are you building and who are you building it for?" Accept open-form. Infer product focus, B2B/B2C, and vertical from the answer.
"What industry or vertical is your customer in — or, if B2C, who is your user?" Map to the vertical source options below.
"Who are your top three to five direct competitors?" Generate a suggested list based on what they've described. Present: "Based on what you've told me, I'd start with: [A, B, C]. Does that look right?"
"Which AI vendors or tools matter most to your roadmap?" Suggest the standard default set and let them edit.
"Any execs or analysts to track personally? Any non-work interests?"
Step 2c — If Custom mode
Ask: "Describe what you want in your warmup. Be specific — topics, companies, industries, markets, regions, anything that matters to how you start your day."
From the described interests, map each to one or more recommended sources using the Custom Mode Source-Building Rules below.
For each mapped source, state:
- What you are recommending and why
- Its trust tier
- A flag if it is Tier 3 or lower: "[Source] is Tier 3 (Community/Unverified). Items from it will be labeled in the brief. Worth including?"
Recommend any sources the user likely wants but did not mention.
Step 3 — Present the source list for review
Show the full proposed source suite before saving. Format:
Tier 1 — Authoritative
● CISA Alerts & Advisories
● CISA Known Exploited Vulnerabilities (KEV)
● NVD / CVE Database
...
Tier 2 — Research
◉ CrowdStrike Intelligence Blog
◉ Palo Alto Unit 42
...
Tier 3 — News
○ Krebs on Security
...
Excluded from this run: (none)
Special Interests (if provided)
○ [Interest 1] — list the 1–3 sources that will cover it
e.g. "Formula 1 → motorsport.com, ESPN F1, AP Sports"
e.g. "SEC football → ESPN, 247Sports, AP Sports"
e.g. "Bourbon releases → Whisky Advocate, BourbonBlog.com"
e.g. "Markets → Reuters Markets, Yahoo Finance"
○ [Interest 2] — same format
If special interests were provided, always list them here. The user deserves to see what sources their brief will pull from — this section is not optional when interests exist.
Ask: "Any sources to add or remove before I save this?"
Step 4 — Ask about the lookback window
Before saving, ask: "Last thing — how far back should I look for your first brief? The default is 1 day, but since this is your first run I'd recommend 7 days to get up to speed, or 30 days for a full month of context. What works for you?"
Accept any natural phrasing: "7 days", "go back two weeks", "just today",
"one month". Save the answer as window_override in WARMUP.md if the user
wants a persistent window, or use it only for this run if they say so.
If the user says "default" or "1 day", use adaptive lookback (no override).
Remind them: "You can always override this at run time — just say 'warmup,
go back 2 weeks' and I'll use that window for that run only."
Also ask about search depth:
"One more setting — search depth. By default I cap each search batch to 5 results and 200 words per article. This keeps the brief fast and token-efficient (typically 40–60K tokens for the fetch phase). If you have more token budget, 'deep' mode doubles both — 10 results per batch, 400 words per article — for broader coverage at roughly 2× the fetch cost. Standard is what I'd recommend for daily use. Which do you prefer?"
Save as search_depth: standard or search_depth: deep in WARMUP.md. If the user skips or has no preference, default to standard.
Step 5 — Save WARMUP.md
Save the config file at the project root using the WARMUP.md Config Format defined below.
Step 6 — Run a test brief
Immediately run a RUN cycle. If any sources return nothing, report: "[Source] returned no signal in the test run. It may be temporarily unavailable or the search found nothing recent. I've kept it in your config — it will be checked each run."
Do not remove sources from config due to a single empty result.
RUN Mode
Trigger: "warmup", "run warmup", "run the warmup", "start my warmup", "give me my warmup", "what's in the brief today"
Override trigger: The user can specify a custom lookback at run time: "run the warmup one month back", "run warmup since April 15", "give me the last two weeks", "warmup — go back 30 days". If a lookback phrase is detected, use that window instead of the computed window and note it in the chat summary line.
Step 1 — Read config and compute lookback window
Use the Read file tool (not bash) to read WARMUP.md from the user's project root. If you do not know the project root path, call list_artifacts first — the html_path from the "the-warmup" artifact reveals the workspace folder, and WARMUP.md lives in that same folder. If no artifact exists and no WARMUP.md is found, stop and prompt for SETUP.
Note: mode (CISO or Custom), user profile, active source list, excluded
sources, last_run date, window_override if set.
Compute the lookback window:
today = current date (YYYY-MM-DD)
last_run_date = parsed from WARMUP.md `last_run` field (YYYY-MM-DD)
gap_days = (today - last_run_date) in calendar days
# Override checks — apply first, skip the rest if matched
if user stated a lookback phrase in this run (e.g. "go back 30 days", "since April 15"):
window = user-specified value # note in summary line, skip remaining logic
elif window_override is set in WARMUP.md:
window = window_override
elif last_run is missing or empty:
window = 30 # first run — bootstrap with a month of context
elif gap_days == 1 AND daily_mode: true in WARMUP.md:
window = 2 # daily fast-path: skip re-fetching a full 7-day window
elif gap_days <= 7:
window = 7 # standard — always covers at least a week
else:
window = min(gap_days, 30) # catch-up run, capped at 30 days
# Weekend bridge (run after computing window above)
region = WARMUP.md `region` field (default: Sat+Sun weekend; IL/Israel: Fri+Sat)
if today == first working day after regional weekend AND gap_days <= 2:
window = max(window, gap_days + 2) # cover full weekend
note in summary: "Lookback extended to cover weekend"
if gap_days > 7 AND interval spans a user-declared holiday (Notes: "holiday: YYYY-MM-DD"):
note in summary: "Catch-up run — verify holiday coverage manually if needed"
# Search date parameter
search_after_date = today - (window + 1) days # +1 catches late-yesterday and early-today
Note the computed window in the summary line. Hard date filter: every item in the brief MUST have a publication date within the lookback window — no exceptions. If a source has no in-window items, mark it "status": "quiet".
Step 2 — Fetch phase
Before starting any searches, output this line in chat: "🔍 Gathering intelligence from [N] sources · [M] search batches · lookback [X] days — this takes a few minutes."
Run all batches concurrently. Do not wait for one batch to complete before starting the next — they are independent. Fire all batches in a single parallel pass, then synthesize after all return.
For each active source, search for recent content using WebSearch.
Search using compound batch queries — not one query per source. This cuts fetch volume from ~38 calls to ~10 without losing coverage. Run batches concurrently where possible; they do not depend on each other.
Batch query table (CISO mode):
| Batch | Sources covered | Query pattern |
|---|---|---|
| Gov pulse | CISA + NSA + FBI + FTC | (site:cisa.gov OR site:nsa.gov OR site:ic3.gov OR site:ftc.gov) advisory alert after:YYYY-MM-DD |
| Research | MSTIC + CrowdStrike + Elastic + Wiz + Unit 42 | (site:microsoft.com/security OR site:crowdstrike.com/blog OR site:elastic.co/security-labs OR site:wiz.io/blog OR site:unit42.paloaltonetworks.com) [sector] threat after:YYYY-MM-DD |
| CVE sweep | NVD + CISA KEV | (site:nvd.nist.gov OR site:cisa.gov/known-exploited-vulnerabilities) CVE critical after:YYYY-MM-DD |
| News | BleepingComputer + SecurityWeek + Krebs + THN + Dark Reading | (site:bleepingcomputer.com OR site:securityweek.com OR site:krebsonsecurity.com OR site:thehackernews.com OR site:darkreading.com) [sector] after:YYYY-MM-DD |
| Market | Reuters + Bloomberg + sector vendors | [company OR sector] acquisition OR breach OR regulatory site:reuters.com OR site:bloomberg.com after:YYYY-MM-DD |
| Social | X + r/netsec + LinkedIn | [sector] security debate OR disclosure site:reddit.com/r/netsec after:YYYY-MM-DD |
| Interests | One per special interest | Targeted query per interest (e.g., Ironman 70.3 results 2026) |
Replace YYYY-MM-DD with the computed lookback start date. Adapt queries to the user's sector and profile (e.g., a Healthcare CISO adds site:hhs.gov hc3 to the gov batch). Run Gov, Research, CVE, News, Market, and Interests batches all concurrently — fire all batches in a single parallel pass, then synthesize after all return. Do not wait for one batch before starting the next.
WebSearch result budget: Check search_depth from WARMUP.md:
standard(default): top 5 results per batch · 200 words per articledeep: top 10 results per batch · 400 words per article
Standard is recommended for daily use — keeps fetch-phase cost at 40–60K tokens. Deep roughly doubles that for broader coverage. If search_depth is not set or unrecognized, use standard.
Record for each found item: source name, trust tier, URL, headline, 2–3 sentence summary, relevant tags (CVE ID, MITRE TTP ID, vendor name, M&A flag, regulatory flag, community flag).
skipScan fast-path: If skip_scan: true is set in WARMUP.md, skip the URL safety check entirely. Set config.skipScan: true, safety.domains: [], and safety.totalUrls: 0 in WARMUP_DATA. Do not call URLScan.io. Do not perform Step A allowlist checks. Proceed directly to synthesis. A friendly disclaimer will render in the artifact in place of the Link Safety panel.
URL safety check — run before adding any item to the brief:
For every URL returned by search, check it against the trusted-domain allowlist and URLScan.io before including it in the report.
Step A — Allowlist check (instant, no API call):
If the URL's registered domain matches the allowlist below → VERIFIED SAFE.
This covers all known-good sources across CISO and Product Leader modes.
CISO allowlist domains:
cisa.gov, nsa.gov, ic3.gov, fbi.gov, ftc.gov, nvd.nist.gov, attack.mitre.org,
crowdstrike.com, unit42.paloaltonetworks.com, elastic.co, cloud.google.com,
microsoft.com, blog.talosintelligence.com, secureworks.com, recordedfuture.com,
wiz.io, krebsonsecurity.com, thehackernews.com, darkreading.com,
securityweek.com, bleepingcomputer.com, arstechnica.com, scmagazine.com,
crn.com, techcrunch.com, cybersecurityventures.com,
hhs.gov, h-isac.org, fsisac.com, occ.gov, eisac.com, cio.gov,
pcisecuritystandards.org, dragos.com, claroty.com, nozominetworks.com
Product Leader allowlist domains:
sec.gov, crunchbase.com, a16z.com, sequoiacap.com, cbinsights.com,
gartner.com, forrester.com, producthunt.com,
techcrunch.com, theverge.com, wired.com, fastcompany.com,
reuters.com, bloomberg.com, lennysnewsletter.com, reforge.com,
news.ycombinator.com, linkedin.com, g2.com, capterra.com, trustpilot.com,
openai.com, anthropic.com, deepmind.google, ai.meta.com, research.facebook.com,
mistral.ai, huggingface.co, arxiv.org,
github.blog, github.com, stackoverflow.com, changelog.com,
paymentsdive.com, pymnts.com, consumerfinance.gov,
rockhealth.com, himss.org, cms.gov, fda.gov,
socialmediatoday.com, saastr.com, chartmogul.com,
digitalcommerce360.com, freightwaves.com, supplychaindive.com,
nfx.com, bvp.com
User-configured sources (from WARMUP.md `competitors:` and `ai_vendors:` fields):
Extract the registered domain from each configured URL. These are user-chosen
sources that passed their initial review at SETUP. Treat as allowlisted.
Example: if competitors includes "https://www.splunk.com", splunk.com is allowlisted.
Step B — URLScan.io check (for domains not on the allowlist):
Query: https://urlscan.io/search/#domain:<domain>
If result exists AND verdict is explicitly clean → VERIFIED SAFE.
All other outcomes → NOT VERIFIED: treat as flagged and exclude.
"All other outcomes" means: suspicious verdict, malicious verdict, no result
found, API timeout, API error, rate limit, ambiguous verdict, or any condition
where a clean result cannot be positively confirmed. There is no partial credit.
A URL is either verified safe or it does not appear in the report.
If a URL is not verified safe (flagged, failed scan, or unknown):
- Do not include the URL anywhere in the report. No hyperlink, no bare URL.
- Record it in a
flagged_urlslist:{url, domain, verdict, source_name}. For scan failures, setverdictto"scan unavailable". - The article's content may still appear in the brief as plain text without a link, attributed to the source name only, if the content is relevant (e.g., "CrowdStrike reported X — link omitted, domain not verified").
- The
flagged_urlslist feeds into the safety WARMUP_DATA block and is surfaced in the scan badge and the Link Safety section at the bottom of the artifact. The scan badge text updates to: "N links scanned · M not verified". - If any URLs were excluded, add a brief note in the chat summary line: "⚠ [M] URL(s) excluded — not verified safe (flagged or scan unavailable)."
- The Link Safety section in the artifact must only list domains that passed verification. It must never show a domain that failed or was not checked.
After all batches complete, output in chat: "🔒 Running link safety verification on [N] URLs..."
Safety check is a Step 2 action — not a render-time assumption. The safety.domains array in WARMUP_DATA must be built from the checks actually performed during this fetch phase. Do not copy verdicts from a previous run, do not assume all sources are clean, and do not fill in the array during synthesis or render. Every domain that appears in safety.domains must have been checked in Step 2 of this run. Verdict values: "ALLOWLISTED" (Step A match, no external call), "CLEAN" (Step B URLScan.io explicitly clean). Anything else is flagged and excluded.
If a batch returns nothing: mark those sources "no signal today." Do not invent content. A quiet source is reported honestly.
Source prioritization: Tier 1 and Tier 2 sources are fetched first and given the most depth. Tier 3 and Tier 4 are supplemental. If you run out of capacity, deprioritize Tier 3 and Tier 4 before cutting Tier 1 or 2.
Step 3 — Synthesize phase
Output in chat before starting synthesis: "⚡ Synthesizing [N] items across [M] sections..."
DATE FILTER — MANDATORY GATE. No item enters WARMUP_DATA without passing this check.
lookback_start = today - lookback_days
BEFORE WRITING EACH ITEM — run this check:
1. Parse item.date as YYYY-MM-DD
2. Is item.date ≥ lookback_start? → INCLUDE
3. Is item.date < lookback_start? → DISCARD immediately. Do not include it "because it's relevant."
Real violations that MUST be caught (window = 7 days, today = 2026-05-16):
lookback_start = 2026-05-09
item.date = 2026-05-08 → 8 days old → REJECT ← one day over. Still REJECT.
item.date = 2026-05-06 → 10 days old → REJECT ← "only two weeks ago" is still REJECT.
item.date = 2026-05-09 → 7 days old → ACCEPT (exactly at boundary = OK)
item.date = 2026-05-10 → 6 days old → ACCEPT
No date = discard. If a result has no parseable publication date, discard it. Do not guess or assume.
"Relevant but old" = still discard. An article can be highly relevant and still violate the date filter. Relevance does not override the date gate.
If after filtering a source has zero in-window items, mark it "status": "quiet" in sources[] and exclude it from safety.domains.
This gate runs before you organize items into sections. Do not route stale items into sections intending to remove them later — discard at first sight, before any further processing.
Organize fetched items into sections using the Section Structure below (CISO mode) or the user's defined interest categories (Custom mode).
Curation rule: quality over quantity. If 25+ items come back from searches, select the 12–18 most signal-dense. Prefer Tier 1 and Tier 2 findings. Within a section, order by relevance to the user's profile, not by source tier.
Step 3b — Special Interests (optional)
If special_interests is set in WARMUP.md, the Interests batch was already fetched concurrently in Step 2. Do not run a second fetch here.
Render the results from the Step 2 Interests batch as a Special Interests section in the artifact, positioned after AI Intelligence (or Industry Intel if the AI section is not present) and before Social Signal. Use general news and sports/topic sources — label all items with ○ (Tier 3 / Community/News).
Tag format: use [NBA], [SEC FOOTBALL], [F1], etc. — whatever matches
the interest. Keep summaries conversational — this is the coffee reading, not
an intelligence item. Two to four sentences is enough.
If special_interests is not set or is empty, omit the section entirely.
Do not add a placeholder or ask about it during RUN.
Step 4 — Render phase
ABSOLUTE RULE — NO EXCEPTIONS:
The brief HTML is ALWAYS built from the engine returned by the warmup_get_template MCP tool (invoked in Step 1b) or from the existing artifact file. Never write HTML from scratch. Never read warmup-template.html from disk. Never use training-data memory of what the brief looks like. The CSS, layout, typography, and PDF builder exist only in the tool response — reproducing them from memory will produce the wrong design and break PDF export.
If you reach this step without having invoked the warmup_get_template MCP tool (first run or engine update) or confirmed the existing artifact file is readable (version match), stop and invoke the warmup_get_template MCP tool now (tool call — not a file read) before continuing.
Rule: only WARMUP_DATA changes between reports. The engine (CSS, JS, PDF builder, renderer) is fixed in warmup-template.html and touched only when the version marker changes. Always deliver via update_artifact / create_artifact — never a computer:// file link. The Save PDF button inside the artifact is the user's only download path.
Inject and render (Step 1b already determined the base HTML):
By the time you reach this step, Step 1b has already:
- Called
list_artifactsand determined first run vs. daily run. - For daily runs with a version match: the file exists at
html_path. - For first runs or engine version mismatches:
warmup_get_templatewas already called and the engine shell is in context.
Do not call list_artifacts or warmup_get_template again here.
Path A — Version match (daily run, no engine update):
Replace only the <script id="warmup-data"> block in the existing HTML. Do NOT read the full file — the engine is 131KB and reading it wastes ~30K tokens. Use Grep to locate the exact line, read only that block, then Edit it in place.
- Grep for
<script id="warmup-data">inhtml_pathto find the line number. - Use the Read tool with
offsetandlimitset to read only the<script id="warmup-data">…</script>block (~10–20 lines). This gives you the exact current text to match. - Use the Edit tool to replace the entire block (from
<script id="warmup-data">through its closing</script>) with exactly:
Touch no other part of the HTML. The Edit tool does a targeted string replacement — the rest of the 131KB engine is never read or re-emitted.<script id="warmup-data"> window.WARMUP_DATA = <FULL JSON of the new WARMUP_DATA dict>; </script> - Call
update_artifactwithid: "the-warmup"and the samehtml_path.
Do not use bash for this step. Bash runs in a Linux sandbox where macOS file paths are remapped and will not resolve. Use the Grep, Read, and Edit file tools only — they accept the real macOS path from
html_pathdirectly.
Path B — First run or engine update:
The MCP server injects WARMUP_DATA server-side. You do NOT write the template then edit a placeholder — the placeholder is gone after injection. Call the tool with your data, get back filled HTML, write it once.
Call
warmup_get_template({ warmup_data: JSON.stringify(WARMUP_DATA) }). The server injectsWARMUP_DATAinto the engine shell before returning. The response is complete, filled HTML — no editing needed.Write the filled HTML to disk. Use the Write tool to write the response to
warmup-artifact.htmlin the user's selected/workspace folder (e.g.~/Documents/Claude/Projects/). Do NOT write to the outputs or temp directory; that path is cleared between sessions. Do not modify the HTML before writing — write it exactly as received.Register the artifact. Call
create_artifact(first run) orupdate_artifact(engine update) withid: "the-warmup"andhtml_pathpointing to the written file.
Never call create_artifact when the artifact already exists — it will fail. Never call update_artifact when the artifact does not exist yet.
Do not use bash for Path B. Bash runs in a Linux sandbox where macOS file paths are remapped and will not resolve. Use the Write file tool only.
Path C — Edit in place (user requests a correction to the existing brief):
Triggered when the user asks to change something in the current report — fix a headline, correct a date, add or remove an item, rewrite a body — without running new searches. No searches. No template fetch. Extract, modify, patch.
Step 1: Use the Read tool to read the full HTML from html_path.
Step 2: Locate the <script id="warmup-data"> … </script> block. Extract the JSON object inside it. Apply the user's requested change to the relevant field(s) in WARMUP_DATA. Touch nothing else.
Step 3: Replace the entire <script id="warmup-data"> block with the modified version and use the Write tool to write the complete HTML back to html_path. Call update_artifact with id: "the-warmup" and the same html_path.
Do not use bash for Path C. Same reason as Path A — bash runs in a Linux sandbox where macOS paths do not resolve. Use the Read and Write file tools only.
Token cost summary:
| Path | Trigger | Token cost |
|---|---|---|
| A — Data patch | Daily run, engine match | ~1–2KB targeted Grep+Read (avoids reading the full 131KB engine) |
| B — Full engine | First run or version mismatch | ~131KB filled HTML from MCP tool (one-time; no file read needed) |
| C — Edit in place | User correction to existing brief | ~1–2KB targeted Grep+Read (same Grep+targeted-Read approach as Path A) |
When an engine bug is fixed:
- Apply the fix to
warmup/warmup-template.html— this is the canonical source. - Apply the same fix to the active
warmup-artifact.html. - Call
update_artifactto push the fix into the live artifact. - The fix propagates to all future reports automatically because new reports start from the template.
WARMUP_DATA schema (v0.3.0 — Morning Edition):
Config field accuracy rule:
mode,sector,company, andregionMUST be copied verbatim fromWARMUP.md. Do not infer, generalize, abbreviate, or replace. If the user said "Security", sector = "Security". If the user said "Global", region = "Global". These values appear as pills the user sees on every run.
{
"config": {
// Required
"name": "Mike",
"mode": "CISO",
"company": "Elastic",
"sector": "Cybersecurity",
"reportDate": "Thursday, 15 May 2026", // REQUIRED — full display string for the masthead date (e.g. "Friday, 15 May 2026").
// Format: "{Weekday}, {DD} {Month} {YYYY}". Use today's date in the user's timezone.
// Without this field the masthead date falls back to template placeholder text.
"updated": "15 May 2026", // Footer display string
"lastRun": "2026-05-14", // ISO date — drives issue number
"dateRange": "May 8 – May 15, 2026", // Lookback window display string
"sourcesActive": 12,
"sourcesQuiet": 4,
"showQuote": true, // REQUIRED — must be true (JSON boolean). Omitting or setting false hides the daily quote.
"scanTime": "HH:MM TZ", // REQUIRED — set to current generation time in 24-hr user-timezone format (e.g. "06:14 ET").
// Use timezone from WARMUP.md; default to UTC and write "HH:MM UTC" if not set.
// THE single timestamp source — populates masthead, signal bar, safety panel, PDF.
// Without this, Generated cell shows "—" and scan timestamp is blank.
"timezone": "ET", // REQUIRED — copy verbatim from WARMUP.md `timezone` field (e.g. "ET", "PT", "UTC").
// CRITICAL: this is the USER'S local timezone — NOT the company's headquarters timezone.
// Read it from WARMUP.md. Do not infer from the company location. Write "UTC" if not set.
// Without this field the time display in the masthead is missing timezone context.
// Optional fields — include even if blank (write "" not omit)
"region": "Global",
"vendors": "CrowdStrike, Palo Alto", // Copy verbatim from WARMUP.md vendors field. Write "" if blank — do not omit.
"interests": "",
"totalLinks": 18, // Count of verified-safe clickable URLs in the rendered brief. Must equal safety.totalUrls.
"skipScan": false, // Optional — set true if skip_scan: true in WARMUP.md. Hides scan badge, skips safety panel, shows friendly disclaimer instead.
"searchDepth": "standard" // "standard" | "deep" — copy from WARMUP.md search_depth. Drives depth indicator in sources panel and configure modal.
},
"sections": [
{
"id": "threat",
"label": "Threat Landscape",
"sub": "Active campaigns and fresh exploitation. What your stack should be watching for today.",
// sub: ALWAYS populated — the section's standing editorial deck, one sentence.
// This renders visibly in the brief as italic text under the section heading.
// NEVER put lookback computation text, agent instructions, or meta-commentary here.
// WRONG: "1-day lookback (first run). Say 'warmup, go back 7 days' for context."
// RIGHT: "Active campaigns and fresh exploitation. What your stack should be watching for today."
// Write it as editorial voice — a standing description of what this section covers.
// note: null unless there is a today-only run caveat (e.g. "Source X was down"). Never repeat sub here.
"note": null,
"items": [
{
// items[0] is the EDITORIAL LEAD — rendered full-width with a large
// headline and an oxblood drop-cap on the first letter of body.
// Choose it deliberately: most important item in the section.
"dot": "d1", "src": "CISA",
"tags": [{"cls": "t-alert", "text": "KEV ADDED"}, {"cls": "t-mitre", "text": "T1190"}],
"url": "https://...",
"hl": "Article headline.",
"deck": "Federal agencies have until June 4 to patch.",
// deck: LEAD ITEMS ONLY. One short italic sentence — the 'so what?'.
// Omit entirely on non-lead items (items[1..N]).
"body": "2–3 sentence summary in plain prose.",
"date": "YYYY-MM-DD"
},
{
// items[1..N] render in a two-column grid. No deck field. Date-sorted.
"dot": "d2", "src": "Source Name",
"tags": [],
"url": "https://...",
"hl": "Article headline",
"body": "2–3 sentence summary.",
"date": "YYYY-MM-DD"
}
]
}
],
"sources
…(truncated)