This workflow runs Stages A → B → C → D → E in a single agent session and
ships one PR containing both the analysis artifacts and the rendered
article(s) for the importing workflow's article type. There is no paired
article workflow — Stage D is a deterministic CLI invocation
(npm run generate-article), not an agent prose pass.
.github/prompts/00-scope-and-ground-rules.md— workspace scope, forbidden/allowed edits, neutrality, agent never writes article prose.github/prompts/08-infrastructure.md— frontmatter, MCP gateway, stable folder layout,--analysis-onlyflag,npm run generate-article.github/prompts/01-data-collection.md— Stage A.github/prompts/07-mcp-reference.md— canonical tool tables.github/prompts/02-analysis-protocol.md— Stage B (2 passes; §2 re-run merge rule; §3 time budgets).github/prompts/03-analysis-completeness-gate.md— Stage C (blocking); §6b resuming a same-day folder.github/prompts/04-article-generation.md— Stage D (deterministic CLI; metadata/SEO contract; agents do not author prose).github/prompts/05-analysis-to-article-contract.md— artifact-to-article contract and read-before-render dutiesArticle-Generation.md— end-to-end article pipeline reference, UI/UX export contract, andarticle.mdprovenance.github/prompts/06-pr-and-safe-outputs.md— single-PR rule, unified-workflow PR contract- On error →
.github/prompts/09-troubleshooting.md
Stage A · Data Collection (per-slug budget — see article-horizons.ts)
→ Stage B · Analysis (Pass 1 + Pass 2, hard ceiling per article-horizons.ts)
→ Stage C · Completeness Gate (≤ 4 min) — BLOCKING; elapsed-time
tripwire (per-slug) forces ANALYSIS_ONLY before Stage D
→ Stage D · Article Render (npm run generate-article — deterministic, ≤ 2 min)
→ Stage E · Single PR (≤ 2 min — by minute ≤ 45; exactly once)
Per-slug minute boundaries are derived from
src/config/article-horizons.ts(stageBudgets) and surfaced in the importing workflow's Workflow-Parameters table. The full table of per-family tripwires lives in.github/prompts/02-analysis-protocol.md§3.
Background. The Copilot CAPI enforces a hard cap of 100 LLM
invocations per workflow session. Run 25799686522 (news-propositions)
exhausted that cap with only 2 artifacts short of completion:
~50% of invocations were EP MCP data-gathering (track_legislation ×7
plus 15 other EP MCP calls); the remainder went to artifact writing,
which is 38+ files × ~1.5 invocations/file. Total budget = ~57+ ~50
= 107 > 100. See .github/prompts/09-troubleshooting.md §5
(run 25799686522 row) for the audit-confirmed forensics.
Every file the agent writes follows this strict priority order:
create— every new file (analysis prose, JSON manifests, templates). Passpath+file_textexplicitly. Bypasses the bash-safety filter; no context-window truncation.edit— every existing file. AtomicoldText→newText; no clobbering siblings.jq→cat > file— short keyword-free structured writes only (manifest.json,prefetch-status.json, status flags). Body must be JSON / SPDX header, never prose.node scripts/extend-artifacts.js --spec-file <path>— Stage B Pass 2 batch extend across multiple under-floor artifacts.
Banned:
python3/ruby/perlheredocs for any file — toolchain is Node.js + TypeScript only and heredocs silently truncate at the context window.cat > file << 'EOF'heredocs for prose / SWOT / stakeholder / risk / article content — the bash-safety filter false-positives on the literal "kill" (endemic in political analysis).echo "…" >> fileextend loops to fix a too-short artifact — pre-size every write to floor on the firstcreateusingruns/thresholds-cache.json.
edit "No match found" recovery: re-read the file with view, copy the exact oldText (including leading whitespace) into a fresh edit, or rewrite the whole file with one create. Never escalate to a heredoc.
Drift guards: 00-scope-and-ground-rules.md §4; 02-analysis-protocol.md §2a; test/unit/news-translate-workflow-contract.test.js.
Every article workflow runs bash scripts/prefetch-ep-feeds.sh <slug> <feeds…>
in a deterministic pre-agent step. The script writes one JSON file per feed
to ${ANALYSIS_DIR}/data/<feed>-feed.json (with {"items":[]} placeholder
on fetch failure). On the agent's first Stage A bash block:
- List
${ANALYSIS_DIR}/data/and inventory which feed files exist. - Skip the corresponding MCP call for any feed with a non-placeholder file. Read the JSON directly from disk instead.
- Only call MCP for feed endpoints not pre-fetched, or for deep-fetch
tools (
track_legislation,get_voting_records,get_meeting_decisions).
After accounting for pre-fetched feeds, cap total Stage A EP MCP calls
at 5. On the 5th call, stop data collection and proceed to Stage B
immediately. Prefer ≤ 3 track_legislation deep-fetches and ≤ 2
supplementary feed/search calls for slugs where pre-fetched coverage is
complete.
If a 6th EP MCP call is genuinely required (e.g. a required deep-fetch that has no pre-fetched equivalent), log an explicit acknowledged exception:
# INVOCATION_CAP_ACKNOWLEDGED: 6th EP MCP call required for <reason>
This exception must appear in intelligence/mcp-reliability-audit.md.
The following EP API feeds are documented as persistently degraded
across intelligence/mcp-reliability-audit.md runs in April–May 2026
(see analysis/daily/2026-05-2*/). Do not spend Stage A invocations
re-probing these feeds when their pre-fetched placeholder is already on
disk — instead call the named fallback tool once to recover the
missing data, then proceed to Stage B.
| Degraded feed (placeholder on disk) | Failure mode | Canonical Stage A fallback (1 MCP call) |
|---|---|---|
procedures-feed.json |
Historical-tail ordering — items dated 1972–1990 (STALENESS_WARNING) |
get_adopted_texts(year=YYYY) — cross-reference procedureReference field on each adopted text |
events-feed.json |
HTTP 404 from /events/?view-version=v2.1 |
get_plenary_sessions(dateFrom=D-14) — paginated non-feed endpoint, unaffected by the events 404 |
committee-documents-feed.json |
HTTP 404 or empty fixed-window response | get_committee_documents(limit=50) — direct paginated endpoint |
documents-feed.json |
HTTP 404 from enrichment layer | get_adopted_texts_feed(timeframe=one-week) for adopted output; get_external_documents(limit=50) for Council/Commission docs |
external-documents-feed.json |
Zero-item windows (freshness ambiguity) | get_external_documents(limit=50) paginated endpoint |
| DOCEO roll-call votes (any week within last 4 weeks) | Expected 2–4 week DOCEO XML publication lag — not a failure | Do not retry within window; declare degraded-voting in manifest.dataMode |
Highest-reliability EP endpoint (A2 grade, ~90% success):
get_adopted_texts(year=YYYY, limit=50) — direct, paginated, non-feed.
Every analysis run that lost primary feed coverage in May 2026 recovered
its analytical floor by calling this tool once. The breaking,
committee-reports, motions, propositions, month-in-review,
quarter-in-review, week-in-review, and year-in-review slugs already
pre-fetch the adopted-texts feed. Backward-looking review slugs that
need legislative substance and currently rely on procedures/documents
feeds alone should add adopted-texts to their prefetch list.
Do not spend a 6th invocation on the same degraded feed after the prefetch placeholder is on disk — the placeholder is the authoritative "this feed is unavailable for this run" signal. The fallback tool above is the only call that recovers analytical floor in degraded-feeds mode.
At the start of Stage B (before writing any artifact), call:
bash scripts/cache-analysis-thresholds.sh "${ANALYSIS_DIR}" "<article-type-slug>"This writes ${ANALYSIS_DIR}/runs/thresholds-cache.json — a filtered snapshot
of analysis/methodologies/reference-quality-thresholds.json for the active
article type. Then, for each artifact:
- Read the thresholds from the cache file (
runs/thresholds-cache.json) — not fromreference-quality-thresholds.jsondirectly. - Read the matching template from
analysis/templates/<artifact>.mdonce. - Pre-size each artifact write to meet the floor on the first attempt.
Banned patterns (each wastes ≥ 2 invocations and is the proximate cause of cap-exhaustion on long runs):
- Write short stub →
wc -l→ realize it's short →cat >> fileextend. - Repeated
wc -lcalls across artifacts to verify floors after writing. - Re-reading
reference-quality-thresholds.jsonper artifact (use the cache).
Pass 2 (deepening) MUST still happen, but it MUST be a single coherent extension pass per artifact — never a discovery + fix loop.
At the end of Stage A, declare the run's data availability mode. This flows
into manifest.json and then into npm run validate-analysis (Stage C),
which auto-adjusts per-artifact line floors rather than relying on agent
self-declaration.
The pre-agent step wrote ${ANALYSIS_DIR}/data/prefetch-status.json with:
{
"prefetchMode": "full|degraded-feeds|minimal",
"fetched": N,
"placeholders": M,
"total": T
}Read this file at Stage A start to understand EP API availability.
Combine the prefetch result with live Stage A probes:
| Condition | dataMode | Line-floor factor |
|---|---|---|
| All feeds fetched + IMF OK + voting OK | full |
1.00 |
| 1+ feeds unavailable (after 3 retries) | degraded-feeds |
0.80 |
| IMF data unavailable / missing | degraded-imf |
0.85 |
| EP roll-call data missing (0 voting records) | degraded-voting |
0.85 |
| Only article title/metadata available | title-only |
0.75 |
| Most EP feeds unavailable + IMF absent | minimal |
0.65 |
When multiple degradations apply simultaneously, pick the single most-severe
single-axis mode whose trigger independently applies. Never compose modes —
e.g. if feeds are degraded AND IMF is unavailable, declare degraded-feeds
(0.80) because its trigger ("1+ feeds unavailable") independently applies and
has a lower factor than degraded-imf (0.85). Only choose minimal (0.65)
when its own trigger ("most EP feeds unavailable + IMF absent") independently
applies as a single observed condition; do not infer minimal by combining
two single-axis degradations.
At the end of Stage A, add dataMode to manifest.json:
{
"articleType": "<slug>",
"dataMode": "degraded-feeds",
...
}Stage C (npm run validate-analysis "${ANALYSIS_DIR}") reads manifest.dataMode
and applies the corresponding factor to all per-artifact line floors automatically.
Structural checks (Mermaid diagrams, WEP bands, Admiralty grades, SAT ≥ 10) are
never reduced regardless of data mode.