Skip to content

Commit 7870ab0

Browse files
authored
Merge pull request #2008 from Hack23/copilot/update-agentic-workflows-and-prompts
Fix drift in agentic workflow contract, prompts, and docs
2 parents e33e22c + f956aa6 commit 7870ab0

78 files changed

Lines changed: 756 additions & 1437 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: 'News workflow pre-warm & pre-flight'
2+
description: |
3+
Shared setup for every `.github/workflows/news-*.md` agentic workflow:
4+
1. Set up Node.js 25 (matches `runtimes.node.version` in every workflow).
5+
2. `npm ci --prefer-offline --no-audit`.
6+
3. Pre-warm the riksdag-regering MCP server (Render.com cold-start mitigation).
7+
4. Pre-flight DNS + HTTPS reachability + MCP `tools/list` probe for the
8+
upstream services consumed by the news pipeline.
9+
10+
This action exists to deduplicate ~80 lines of identical YAML across the 11
11+
news workflow files. Edit it in one place.
12+
inputs:
13+
node-version:
14+
description: 'Node.js version (must match the workflow `runtimes.node.version`).'
15+
required: false
16+
default: '25'
17+
mcp-url:
18+
description: 'Riksdag-regering MCP HTTP endpoint to pre-warm.'
19+
required: false
20+
default: 'https://riksdag-regering-ai.onrender.com/mcp'
21+
runs:
22+
using: 'composite'
23+
steps:
24+
- name: Setup Node.js
25+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
26+
with:
27+
node-version: ${{ inputs.node-version }}
28+
29+
- name: Install dependencies
30+
shell: bash
31+
run: npm ci --prefer-offline --no-audit
32+
33+
- name: Pre-warm MCP server (Render.com cold start mitigation)
34+
shell: bash
35+
env:
36+
MCP_URL: ${{ inputs.mcp-url }}
37+
run: |
38+
echo "🔥 Pre-warming riksdag-regering MCP server via MCP protocol..."
39+
WARM=false
40+
for i in 1 2 3 4 5 6; do
41+
RESP=$(curl -sf --max-time 30 -X POST \
42+
-H "Content-Type: application/json" \
43+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
44+
"$MCP_URL" 2>/dev/null) || true
45+
if echo "$RESP" | grep -q '"tools"'; then
46+
TOOL_COUNT=$(echo "$RESP" | grep -o '"name"' | wc -l)
47+
echo "✅ MCP server responded on attempt $i with $TOOL_COUNT tools registered"
48+
WARM=true
49+
break
50+
fi
51+
echo "⏳ Attempt $i/6 — server may be cold-starting, waiting 20s..."
52+
sleep 20
53+
done
54+
if [ "$WARM" = "false" ]; then
55+
echo "⚠️ MCP server did not respond after 6 attempts — agent will retry via in-prompt health gate"
56+
fi
57+
58+
- name: Pre-flight external endpoint reachability check (runs before MCP Gateway)
59+
shell: bash
60+
env:
61+
MCP_URL: ${{ inputs.mcp-url }}
62+
run: |
63+
echo "🔍 Network Diagnostics — $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
64+
echo "═══════════════════════════════════════════"
65+
echo ""
66+
echo "📡 DNS Resolution Tests:"
67+
for domain in riksdag-regering-ai.onrender.com api.scb.se api.worldbank.org data.riksdagen.se www.riksdagen.se www.regeringen.se www.statskontoret.se statskontoret.se; do
68+
if nslookup "$domain" >/dev/null 2>&1; then
69+
IP=$(nslookup "$domain" 2>/dev/null | grep -A1 "Name:" | grep "Address:" | head -1 | awk '{print $2}')
70+
echo " ✅ $domain → $IP"
71+
else
72+
echo " ❌ $domain — DNS FAILED"
73+
fi
74+
done
75+
echo ""
76+
echo "🌐 HTTPS Connectivity Tests:"
77+
for url in \
78+
"$MCP_URL" \
79+
"https://api.scb.se/OV0104/v2beta" \
80+
"https://api.worldbank.org/v2/country/SE?format=json" \
81+
"https://data.riksdagen.se/dokumentlista/?sok=test&doktyp=bet&utformat=json&a=1" \
82+
; do
83+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>/dev/null || echo "000")
84+
DOMAIN=$(echo "$url" | sed 's|https://||' | cut -d/ -f1)
85+
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 400 ]; then
86+
echo " ✅ $DOMAIN → HTTP $HTTP_CODE"
87+
elif [ "$HTTP_CODE" = "000" ]; then
88+
echo " ❌ $DOMAIN → TIMEOUT/UNREACHABLE"
89+
else
90+
echo " ⚠️ $DOMAIN → HTTP $HTTP_CODE"
91+
fi
92+
done
93+
echo ""
94+
echo "🔌 MCP Server Tool Count:"
95+
TOOL_RESP=$(curl -sf --max-time 15 -X POST \
96+
-H "Content-Type: application/json" \
97+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
98+
"$MCP_URL" 2>/dev/null) || TOOL_RESP=""
99+
if echo "$TOOL_RESP" | grep -q '"tools"'; then
100+
TOOL_COUNT=$(echo "$TOOL_RESP" | grep -o '"name"' | wc -l)
101+
echo " ✅ riksdag-regering MCP: $TOOL_COUNT tools registered"
102+
else
103+
echo " ❌ riksdag-regering MCP: No tools response (server may still be starting)"
104+
fi
105+
echo ""
106+
echo "═══════════════════════════════════════════"

.github/agents/intelligence-operative.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ tools: ["*"]
1212
2. [`.github/copilot-mcp.json`](/.github/copilot-mcp.json) — MCP servers available
1313
3. [`README.md`](/README.md) — project mission, features, architecture
1414
4. [`SECURITY_ARCHITECTURE.md`](/SECURITY_ARCHITECTURE.md) & [`THREAT_MODEL.md`](/THREAT_MODEL.md)
15-
5. [`.github/skills/`](/.github/skills/) — auto-loaded skills library (92 skills)
15+
5. [`.github/skills/`](/.github/skills/) — auto-loaded skills library (91 skills)
1616

1717
---
1818

.github/aw/actions-lock.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
"version": "v6.3.0",
3131
"sha": "53b83947a5a98c8d113130e565377fae1a50d02f"
3232
},
33+
"actions/setup-node@v6.4.0": {
34+
"repo": "actions/setup-node",
35+
"version": "v6.4.0",
36+
"sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e"
37+
},
3338
"actions/upload-artifact@v7": {
3439
"repo": "actions/upload-artifact",
3540
"version": "v7",

.github/copilot-instructions.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
## 📋 Repository Context
44

55
**Project**: Riksdagsmonitor — Swedish Parliament (Riksdag) monitoring platform
6-
**Stack**: HTML5, CSS3, TypeScript 6.0.2, Vite 8.0.3, Vitest 4.1.2, Cypress 15.13.0
6+
**Stack**: HTML5, CSS3, TypeScript 6.0.3, Vite 8.0.10, Vitest 4.1.5, Cypress 15.14.1
77
**Runtime**: Node.js 25, ES2025 target, ESNext modules
88
**Deploy**: GitHub Pages + AWS S3 dual deployment
99
**Languages**: 14-language support (EN, SV, DA, NB, FI, DE, FR, ES, NL, AR, HE, JA, KO, ZH)
1010
**Security**: ISO 27001:2022, NIST CSF 2.0, CIS Controls v8.1 compliant
1111
**Organization**: Hack23 AB
1212
**ISMS**: [Hack23 ISMS-PUBLIC](https://github.com/Hack23/ISMS-PUBLIC)
13-
**Version**: 0.8.17
13+
**Version**: 0.8.56
1414
**Agents**: 24 agent files (14 persona + 9 workflow-specialist + 1 developer-instructions) in `.github/agents/`
1515
**Skills**: 91 skills in `.github/skills/` (including 13 gh-aw skills)
16-
**Workflows**: 45 workflow files (21 standard `.yml` + 12 agentic `.md` sources + 12 compiled `.lock.yml`)
16+
**Workflows**: 43 workflow files (21 standard `.yml` + 11 agentic `.md` sources + 11 compiled `.lock.yml`)
1717
**MCP Servers**: 8 configured (riksdag-regering, scb, world-bank, github, filesystem, memory, sequential-thinking, playwright)
1818

1919
## 🎯 Core Rules
@@ -112,7 +112,7 @@ Map every security-relevant control to **ISO 27001:2022 Annex A**, **NIST CSF 2.
112112

113113
## 🤖 GitHub Agentic Workflows
114114

115-
This repo uses [GitHub Agentic Workflows](https://github.github.com/gh-aw/) (gh-aw v0.69.3, pinned via `gh-aw-actions/setup-cli@v0.69.3`) for AI-powered news generation. 12 agentic workflows in `.github/workflows/` produce daily political intelligence articles with five-layer security:
115+
This repo uses [GitHub Agentic Workflows](https://github.github.com/gh-aw/) (gh-aw v0.69.3, pinned via `gh-aw-actions/setup-cli@v0.69.3`) for AI-powered news generation. 11 agentic workflows in `.github/workflows/` produce daily political intelligence articles with five-layer security:
116116

117117
1. **Read-only tokens** — Agent gets only read permissions
118118
2. **Zero secrets in agent** — Write tokens isolated in separate jobs
@@ -289,5 +289,5 @@ tsx scripts/imf-fetch.ts list-indicators
289289

290290
---
291291

292-
**Last Updated**: 2026-04-24
293-
**Version**: 3.3
292+
**Last Updated**: 2026-04-26
293+
**Version**: 3.4

.github/prompts/00-base-contract.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,15 @@ Stage analysis + article.md + news/*.html → Commit → ONE create_pull_request
5555

5656
## Session keepalive requirement
5757

58-
> ⚠️ **Critical**: The Copilot API creates a server-side session when the agent starts. That session is bound to the `github.token` baked in at step start — it is **never refreshed** mid-run. The session expires at approximately **60 minutes** (gh-aw issue #24920). After expiry, all tool calls and inference requests fail silently. The workflow appears to run but makes zero progress, and **the PR is never created**.
59-
60-
To mitigate MCP idle-connection drops, workflows set `sandbox.mcp.keepalive-interval: 300` (5-minute ping). This keeps MCP connections alive but does **not** refresh the Copilot API token.
61-
62-
**The reliable mitigation is to ensure `safeoutputs___create_pull_request` is called well before the session approaches expiry.** A second, shorter-firing clock — the Safe Outputs HTTP MCP idle session (~25–30 min observed) — now drives the operative deadline. Plan the run so the PR is created **within 22–27 minutes** (hard deadline **30 minutes**) of agent start. See `07-commit-and-pr.md §Deadline enforcement` for the authoritative PR-timing procedure, including the full two-timer explanation; that section supersedes any older ~45-minute guidance that predated the 23-artifact pipeline.
58+
> ⚠️ **Critical — three timers**: Plan every run for the **shortest** of the three.
59+
>
60+
> 1. **Job timeout (45 min)** — every news workflow declares `timeout-minutes: 45`. After 45 min the GitHub Actions runner kills the agent unconditionally.
61+
> 2. **Copilot API session (~60 min)** — bound to the `github.token` baked in at step start; never refreshed mid-run (gh-aw issue #24920). After expiry every tool call and inference fails silently.
62+
> 3. **Safe Outputs MCP idle session (~25–30 min observed)** — drops if the agent goes idle toward `safeoutputs___*` for 25+ minutes; every subsequent safe-output call returns `session not found`.
63+
>
64+
> The operative deadline is therefore Timer 3. To mitigate MCP-side idle drops, workflows set `sandbox.mcp.keepalive-interval: 300` (5-minute ping). That keeps upstream MCPs alive but does **not** refresh the Copilot session and does **not** keep the safeoutputs HTTP session alive.
65+
66+
**The reliable mitigation is to ensure `safeoutputs___create_pull_request` is called well before the safeoutputs idle session approaches expiry.** Plan the run so the PR is created **within 22–27 minutes** (hard deadline **30 minutes**) of agent start. The remaining 15+ minutes of the 45-min job budget exist solely as a safety margin for the safeoutputs runner to publish the PR — do **not** schedule additional analysis after the PR call. See `07-commit-and-pr.md §Deadline enforcement` for the authoritative PR-timing procedure.
6367

6468
Do not add per-phase checkpoint PRs or repo-memory push steps.
6569

.github/prompts/02-mcp-access.md

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,6 @@ Authoritative per-workflow surface: the `mcp-servers:` + `tools:` blocks in that
66

77
News workflows declare three data MCP servers + the built-in `github` toolset (via `tools.github.toolsets: [all]`) + `bash` + `agentic-workflows`.
88

9-
| Server | Transport | Declared in | Tool-name style | Example tools |
10-
|--------|-----------|-------------|-----------------|---------------|
11-
| `riksdag-regering` | HTTP (Render) | workflow `mcp-servers:` | `snake_case` | `get_sync_status`, `search_dokument`, `get_voteringar`, `get_dokument_innehall` |
12-
| `scb` | container (`@jarib/pxweb-mcp`) | workflow `mcp-servers:` | `snake_case` | `search_tables`, `get_table_info`, `query_table` |
13-
| `world-bank` | container (`worldbank-mcp`) | workflow `mcp-servers:` | `kebab-case` | `get-economic-data`, `get-country-info`, `search-indicators` |
14-
| `github` | HTTP (Copilot MCP) | workflow `tools.github` | standard | full GitHub MCP toolset |
15-
| `bash` | local helper | workflow `tools.bash` | standard | shell execution |
16-
| `safeoutputs` | runner | always available | `snake_case` | `safeoutputs___create_pull_request`, `safeoutputs___noop`, `safeoutputs___dispatch_workflow` |
17-
18-
`filesystem`, `memory`, and `sequential-thinking` are declared in [`.github/copilot-mcp.json`](../copilot-mcp.json) for the **local Copilot / `assign_copilot_to_issue`** channel and are **not** available to news workflows unless the workflow itself declares them under `mcp-servers:`.
19-
20-
`playwright` must be treated separately: in news workflows it is available as the built-in workflow tool `tools.playwright` when that workflow declares it under `tools:` (e.g. `news-evening-analysis`, `news-realtime-monitor`). In that case it is **not** an MCP server, so do **not** infer its availability from `mcp-servers:` alone and do **not** skip Playwright/browser validation steps when `tools.playwright` is present in workflow frontmatter.
21-
22-
Authoritative inventory: [`.github/copilot-mcp.json`](../copilot-mcp.json) for the local Copilot MCP surface, and each workflow's `mcp-servers:` plus `tools:` frontmatter for the actual per-run surface.
23-
24-
# 02 — MCP Access
25-
26-
Authoritative per-workflow surface: the `mcp-servers:` + `tools:` blocks in that workflow's frontmatter. `.github/copilot-mcp.json` is the **local Copilot** surface (used by `assign_copilot_to_issue` / agent files in `.github/agents/`), not by news workflow runs.
27-
28-
## Servers & tool naming
29-
30-
News workflows declare three data MCP servers + the built-in `github` toolset (via `tools.github.toolsets: [all]`) + `bash` + `agentic-workflows`.
31-
329
| Server | Transport | Declared in | Tool-name style | Example tools |
3310
|--------|-----------|-------------|-----------------|---------------|
3411
| `riksdag-regering` | HTTP (Render) | workflow `mcp-servers:` | `snake_case` | `get_sync_status`, `search_dokument`, `get_voteringar`, `get_dokument_innehall` |

.github/prompts/03-data-download.md

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,53 @@ Run this check as the **first action** after MCP pre-warm, before any download:
77
```bash
88
ANALYSIS_DIR="analysis/daily/$ARTICLE_DATE/$SUBFOLDER"
99

10-
# 23 required artifacts (Families A+B+C+D) — every workflow, every run
11-
REQ=(
12-
# Family A — Core Synthesis (9)
13-
README.md executive-brief.md synthesis-summary.md significance-scoring.md \
14-
classification-results.md swot-analysis.md risk-assessment.md \
15-
threat-analysis.md stakeholder-perspectives.md \
16-
# Family B — Structural Metadata (2)
17-
data-download-manifest.md cross-reference-map.md \
18-
# Family C — Strategic Extensions (5)
19-
scenario-analysis.md comparative-international.md devils-advocate.md \
20-
intelligence-assessment.md methodology-reflection.md \
21-
# Family D — Electoral & Domain Lenses (7)
22-
election-2026-analysis.md voter-segmentation.md coalition-mathematics.md \
23-
historical-parallels.md media-framing-analysis.md \
24-
implementation-feasibility.md forward-indicators.md)
25-
26-
# Tier-C workflows add no new files — all 23 are already mandatory. What Tier-C
27-
# adds is the cross-type synthesis + period multipliers enforced by
28-
# ext/tier-c-aggregation.md and the gate in 05-analysis-gate.md.
29-
3010
SKIP_ANALYSIS=false
3111
ALL_PRESENT=true
32-
for f in "${REQ[@]}"; do
12+
EXPECTED=23
13+
CHECKED=0
14+
# 23 required artifacts (Families A+B+C+D) — every workflow, every run.
15+
# We feed them via a here-doc so the loop never builds an inline bash array
16+
# (the AWF sandbox flags `REQ=(...); for f in "${REQ[@]}"`; see
17+
# 01-bash-and-shell-safety.md §Banned expansion patterns).
18+
# The loop short-circuits on the first missing artifact, so $CHECKED is the
19+
# number examined up to that point — never larger than $EXPECTED. We log
20+
# both numbers so the difference is unambiguous in the run output.
21+
while IFS= read -r f; do
22+
[ -z "$f" ] && continue
23+
CHECKED=$((CHECKED + 1))
3324
[ -s "$ANALYSIS_DIR/$f" ] || { ALL_PRESENT=false; break; }
34-
done
25+
done <<'REQUIRED_ARTIFACTS'
26+
README.md
27+
executive-brief.md
28+
synthesis-summary.md
29+
significance-scoring.md
30+
classification-results.md
31+
swot-analysis.md
32+
risk-assessment.md
33+
threat-analysis.md
34+
stakeholder-perspectives.md
35+
data-download-manifest.md
36+
cross-reference-map.md
37+
scenario-analysis.md
38+
comparative-international.md
39+
devils-advocate.md
40+
intelligence-assessment.md
41+
methodology-reflection.md
42+
election-2026-analysis.md
43+
voter-segmentation.md
44+
coalition-mathematics.md
45+
historical-parallels.md
46+
media-framing-analysis.md
47+
implementation-feasibility.md
48+
forward-indicators.md
49+
REQUIRED_ARTIFACTS
50+
51+
# Tier-C workflows add no new files — all 23 are already mandatory. What
52+
# Tier-C adds is the cross-type synthesis + period multipliers enforced by
53+
# ext/tier-c-aggregation.md and the gate in 05-analysis-gate.md.
54+
3555
[ "$ALL_PRESENT" = "true" ] && SKIP_ANALYSIS=true
36-
echo "SKIP_ANALYSIS=$SKIP_ANALYSIS (required artifacts present: $ALL_PRESENT, count: ${#REQ[@]})"
56+
echo "SKIP_ANALYSIS=$SKIP_ANALYSIS (required artifacts present: $ALL_PRESENT, checked: $CHECKED of $EXPECTED)"
3757
```
3858

3959
| `SKIP_ANALYSIS` | Behaviour |
@@ -60,8 +80,7 @@ Populate `analysis/daily/$ARTICLE_DATE/$SUBFOLDER/` with raw Riksdag/Regering da
6080
| news-weekly-review | `weekly-review` |
6181
| news-monthly-review | `monthly-review` |
6282
| news-evening-analysis | `evening-analysis` |
63-
| news-realtime-monitor | `realtime-$HHMM` |
64-
| news-realtime-monitor | `realtime-pulse` |
83+
| news-realtime-monitor | `realtime-$HHMM` (per-event) or `realtime-pulse` (rolling 4-hour pulse) |
6584

6685
If `force_generation=true` is supplied on a day whose base subfolder already contains `synthesis-summary.md` from a prior merged run, auto-suffix the subfolder (`propositions-2`, `propositions-3`, …) so the forced rerun does not overwrite the merged analysis. Under the default `force_generation=false`, the same base subfolder is reused across runs — see §Pre-flight above.
6786

@@ -117,5 +136,3 @@ Always produce `analysis/daily/$ARTICLE_DATE/$SUBFOLDER/data-download-manifest.m
117136
## Next step
118137

119138
On success, proceed to `04-analysis-pipeline.md`. Never start analysis while `data-download-manifest.md` is missing or empty.
120-
121-
After the manifest is written, run the **phase checkpoint** from `00-base-contract.md` with label `phase-03-download` so the download provenance is persisted to repo memory before any analysis begins.

.github/prompts/04-analysis-pipeline.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,6 @@ For each article with charts, produce accompanying JSON under `analysis/daily/$A
154154

155155
Proceed to `05-analysis-gate.md`. Do not start article generation until the gate passes against all 23 artifacts.
156156

157-
After completing Pass 1 (before Pass 2), run the **phase checkpoint** from `00-base-contract.md` with label `phase-04-pass1`. After completing Pass 2 (before the gate), run it again with label `phase-04-pass2`. This guarantees both iterations survive even if the gate, article, or commit phase later fails.
158-
159157
## External references
160158

161159
- gh-aw runtime (v0.69.3): [abridged](https://github.github.com/gh-aw/llms-small.txt) · [complete](https://github.github.com/gh-aw/llms-full.txt) · [agentic-workflows blog](https://github.github.com/gh-aw/_llms-txt/agentic-workflows.txt) · [source](https://github.com/github/gh-aw)

0 commit comments

Comments
 (0)