diff --git a/.github/agentic-refresh/README.md b/.github/agentic-refresh/README.md
index 91d9e3b..553295f 100644
--- a/.github/agentic-refresh/README.md
+++ b/.github/agentic-refresh/README.md
@@ -1,8 +1,10 @@
# Agentic refresh controls
This directory contains the governance boundary for quest refresh automation.
-Phase 0 established the controls. Phase 1 adds manually dispatched, read-only
-discovery workflows without enabling schedules or repository writes.
+Phase 0 established the controls. Phase 1 added manually dispatched, read-only
+discovery workflows. Phase 2A adds reusable staged reporting, structured
+artifacts, and deterministic parent/child previews without enabling schedules or
+repository writes.
- [`capabilities.json`](./capabilities.json) keeps Buildathon writes disabled
until the repository-scoped token and dispatch spikes pass. External
@@ -23,22 +25,31 @@ discovery workflows without enabling schedules or repository writes.
- The five Quest Master profiles under [`../agents`](../agents) fix each quest's
source and path boundary.
- The five `discover-*.md` workflows under [`../workflows`](../workflows) run
- manual Phase 1 audits. Their shared policy permits only read operations and
- returns reports in the agent response. Every automatic issue path is disabled,
- and safe-output processing is forced into staged preview mode.
+ manual or orchestrated audits. Their shared policy permits only read
+ operations, and `shared/report-output.md` materializes a validated run-scoped
+ JSON artifact. Every automatic issue path is disabled, and safe-output
+ processing is forced into staged preview mode.
+- `weekly-refresh-orchestrator.yml` fans out to all five reusable compiled
+ workflows only through manual dispatch. `weekly-refresh-digest.md` downloads
+ available quest artifacts, converts missing scopes to blocked results, and
+ uploads deterministic parent and child issue previews.
+- `scripts/weekly-refresh-report.mjs` is the machine-readable authority for ISO
+ week context, schema validation, lifecycle/source/path boundaries, materiality,
+ markers, finding keys, deduplication keys, and preview rendering.
Run the local Phase 0 checks with:
```bash
node scripts/validate-refresh-config.mjs
+node scripts/weekly-refresh-report.mjs self-test
node scripts/validate-content-links.mjs
mkdocs build --strict
gh aw compile --strict --validate
```
-No scheduled or write-capable agentic workflow is enabled. Phase 1 workflows
-cannot be live-trialed through `workflow_dispatch` until their compiled
-definitions are available on the default branch.
+No scheduled or write-capable agentic workflow is enabled. The discovery and
+reporting workflows cannot be live-trialed through `workflow_dispatch` until
+their compiled definitions are available on the default branch.
gh-aw v0.77.5 normally reserves `issues: write` for generated failure-reporting
jobs even when issue handlers are disabled. Phase 1 additionally sets
diff --git a/.github/agentic-refresh/governance.md b/.github/agentic-refresh/governance.md
index 19d235d..6af971d1 100644
--- a/.github/agentic-refresh/governance.md
+++ b/.github/agentic-refresh/governance.md
@@ -96,6 +96,23 @@ With staging enabled, the compiled Phase 1 locks contain no repository write
permission. Phase 2 must deliberately remove staging and revisit permissions
before enabling real issue reporting.
+## Phase 2A staged reporting guard
+
+The weekly reporting layer remains manual-only and report-only:
+
+- the deterministic orchestrator has `workflow_dispatch` but no schedule;
+- all five Quest Master workflows accept reusable `workflow_call` inputs for an
+ ISO week and prior ISO week;
+- the only agent-requested output is a custom job that validates and uploads a
+ run-scoped JSON artifact;
+- the digest tolerates missing quest artifacts by recording the scope as
+ blocked, then creates deterministic parent and child previews as artifacts;
+- no compiled or traditional workflow grants issue, branch, pull-request,
+ workflow-dispatch, or content write permission.
+
+The intended `0 8 * * 1` schedule and real issue safe outputs remain Phase 2B
+activation work and require default-branch trials plus the bounded token spike.
+
## SAML authorization
SAML authorization is needed only for local audits and human GitHub operations.
diff --git a/.github/agentic-refresh/report-contract.md b/.github/agentic-refresh/report-contract.md
index 58a69d3..05bebc1 100644
--- a/.github/agentic-refresh/report-contract.md
+++ b/.github/agentic-refresh/report-contract.md
@@ -3,6 +3,25 @@
Each weekly run produces one digest covering all five quests. It creates a child
plan only for a material change.
+Every quest run first emits one version 1 JSON artifact with:
+
+- ISO week key, prior ISO week key, and exact quest slug;
+- exactly one result: `material-change`, `no-material-change`, or `blocked`;
+- source facts separated from repository observations and proposed changes;
+- lifecycle, allowlisted evidence URL, evidence location, wording, and source
+ fingerprint for every official-source delta;
+- learner impact, owned-path drift, asset impact, report-only CodeTour impact,
+ validation, risks, human decisions, and prior finding keys;
+- deterministic parent key, child key, markers, source fingerprints, and finding
+ keys added by `scripts/weekly-refresh-report.mjs`.
+
+A completed report must include source fingerprints. A blocked report must state
+one concise reason and cannot propose changes. A no-change report cannot propose
+changes. Invalid or missing quest artifacts become blocked scopes in the digest.
+Source and path validation rejects adjacent URL-prefix matches, absolute paths,
+backslashes, normalization changes, and traversal segments before materializing
+an artifact.
+
A child plan must contain:
1. Quest and ISO week key.
@@ -23,3 +42,13 @@ Reports must distinguish source facts, repository observations, inferred
impact, proposed changes, and unresolved uncertainty. Search snippets are not
evidence. No implementation may start from a digest or an unapproved child
plan.
+
+Phase 2A produces only run-scoped artifacts:
+
+- one validated JSON artifact per successful quest output;
+- one deterministic digest JSON file;
+- one parent issue Markdown preview;
+- one child issue Markdown preview only for each material-change result.
+
+No Phase 2A workflow creates or updates an issue, comment, branch, pull request,
+workflow dispatch, schedule, or content file.
diff --git a/.github/agentic-refresh/tooling.json b/.github/agentic-refresh/tooling.json
index d4f3180..c7da0f9 100644
--- a/.github/agentic-refresh/tooling.json
+++ b/.github/agentic-refresh/tooling.json
@@ -3,5 +3,7 @@
"ghAwVersion": "v0.77.5",
"ghAwSetupAction": "github/gh-aw-actions/setup-cli@3ea13c02d765410340d533515cb31a7eef2baaf0",
"checkoutAction": "actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803",
+ "downloadArtifactAction": "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c",
+ "uploadArtifactAction": "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a",
"pythonVersion": "3.12"
}
diff --git a/.github/workflows/discover-agentic-rag.lock.yml b/.github/workflows/discover-agentic-rag.lock.yml
index f02d610..3dff8ee 100644
--- a/.github/workflows/discover-agentic-rag.lock.yml
+++ b/.github/workflows/discover-agentic-rag.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"582844232891b47171e31af990b11dfa91e37122049ec8cbb9b8ea3fba727c06","body_hash":"5ee7459c4e2c416a97f48adea9183ceb8ea02f5931434071379f56f44bdfddec","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"d109bf635ad05d7167f595bc20291e91b17af6b51d172d328e6db05c47164c4f","body_hash":"13d6da97cd8c2d7aa89f9cc8671e8303f2afb80e8d68ad1ff1e3f797fdacf735","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
# ___ _ _
# / _ \ | | (_)
@@ -28,6 +28,7 @@
# Imports:
# - ../agents/agentic-rag-quest-master.agent.md
# - shared/discovery-policy.md
+# - shared/report-output.md
#
# Secrets used:
# - GH_AW_GITHUB_MCP_SERVER_TOKEN
@@ -53,6 +54,26 @@
name: "Discover Agentic RAG"
on:
+ workflow_call:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ secrets:
+ GH_AW_GITHUB_MCP_SERVER_TOKEN:
+ required: false
+ GH_AW_GITHUB_TOKEN:
+ required: false
workflow_dispatch:
inputs:
aw_context:
@@ -60,21 +81,33 @@ on:
description: "Agent caller context (used internally by Agentic Workflows)."
required: false
type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
permissions: {}
concurrency:
- group: "gh-aw-${{ github.workflow }}"
+ cancel-in-progress: false
+ group: weekly-refresh-${{ inputs.week_key }}-agentic-rag
run-name: "Discover Agentic RAG"
jobs:
activation:
+ needs: pre_activation
+ if: needs.pre_activation.outputs.activated == 'true'
runs-on: ubuntu-slim
permissions:
actions: read
contents: read
outputs:
+ artifact_prefix: ${{ steps.artifact-prefix.outputs.prefix }}
comment_id: ""
comment_repo: ""
engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
@@ -84,6 +117,10 @@ jobs:
setup-span-id: ${{ steps.setup.outputs.span-id }}
setup-trace-id: ${{ steps.setup.outputs.trace-id }}
stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ target_checkout_ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
+ target_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}
+ target_repo: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ target_repo_name: ${{ steps.resolve-host-repo.outputs.target_repo_name }}
steps:
- name: Setup Scripts
id: setup
@@ -91,12 +128,34 @@ jobs:
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
+ trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }}
env:
GH_AW_SETUP_WORKFLOW_NAME: "Discover Agentic RAG"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-agentic-rag.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Resolve host repo for activation checkout
+ id: resolve-host-repo
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ JOB_WORKFLOW_REPOSITORY: ${{ job.workflow_repository }}
+ JOB_WORKFLOW_SHA: ${{ job.workflow_sha }}
+ JOB_WORKFLOW_REF: ${{ job.workflow_ref }}
+ JOB_WORKFLOW_FILE_PATH: ${{ job.workflow_file_path }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/resolve_host_repo.cjs');
+ await main();
+ - name: Compute artifact prefix
+ id: artifact-prefix
+ env:
+ INPUTS_JSON: ${{ toJSON(inputs) }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/compute_artifact_prefix.sh"
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -116,6 +175,7 @@ jobs:
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
+ GH_AW_INFO_TARGET_REPO: ${{ steps.resolve-host-repo.outputs.target_repo }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
@@ -123,10 +183,19 @@ jobs:
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
await main(core, context);
+ - name: Print cross-repo setup guidance
+ if: failure() && steps.resolve-host-repo.outputs.target_repo != github.repository
+ run: |
+ echo "::error::COPILOT_GITHUB_TOKEN must be configured in the CALLER repository's secrets."
+ echo "::error::For cross-repo workflow_call, secrets must be set in the repository that triggers the workflow."
+ echo "::error::See: https://github.github.com/gh-aw/patterns/central-repo-ops/#cross-repo-setup"
- name: Checkout .github and .agents folders
+ if: steps.resolve-host-repo.outputs.target_repo == github.repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
+ repository: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
sparse-checkout: |
.github
.agents
@@ -179,27 +248,26 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
# poutine:ignore untrusted_checkout_exec
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF'
+ cat << 'GH_AW_PROMPT_398e46681d5ec93d_EOF'
- GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF
+ GH_AW_PROMPT_398e46681d5ec93d_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF'
+ cat << 'GH_AW_PROMPT_398e46681d5ec93d_EOF'
- Tools: create_issue
- GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md"
- cat << 'GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF'
+ Tools: missing_tool, missing_data, noop, emit_weekly_report
- GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF
+ GH_AW_PROMPT_398e46681d5ec93d_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF'
+ cat << 'GH_AW_PROMPT_398e46681d5ec93d_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -228,21 +296,99 @@ jobs:
{{/if}}
- GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF
+ GH_AW_PROMPT_398e46681d5ec93d_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF'
+ cat << 'GH_AW_PROMPT_398e46681d5ec93d_EOF'
+ Apply this staged weekly reporting contract for quest
+ `agentic-rag`:
+
+ - Quest identity is `3`,
+ `LangChain.js Agentic RAG`, and
+ `quest/3-agentic-rag`.
+ - The reporting week is `__GH_AW_INPUTS_WEEK_KEY__` and the prior report locator is
+ `weekly-refresh:__GH_AW_INPUTS_PRIOR_WEEK_KEY__:agentic-rag`.
+ - Compare current allowed-source evidence, current Buildathon content, and any
+ prior finding keys available from the exact prior locator. Never broaden the
+ search to unrelated issues or repositories.
+ - Call `emit_weekly_report` exactly once, including blocked runs. The `result`
+ argument and `report_json.result` must match.
+ - Do not write a report file yourself. The deterministic safe-output job validates
+ the JSON and creates the run-scoped artifact.
+
+ The `report_json` value must encode exactly this version 1 shape:
+
+ ```json
+ {
+ "schemaVersion": 1,
+ "weekKey": "__GH_AW_INPUTS_WEEK_KEY__",
+ "priorWeekKey": "__GH_AW_INPUTS_PRIOR_WEEK_KEY__",
+ "questSlug": "agentic-rag",
+ "result": "material-change | no-material-change | blocked",
+ "executiveSummary": "short summary",
+ "currentQuestStatus": "current status",
+ "learnerImpact": "high | medium | low | none",
+ "officialSourceDeltas": [
+ {
+ "fact": "source fact",
+ "lifecycle": "GA | Public preview | Private preview | Not stated by allowed sources",
+ "evidenceUrl": "allowlisted URL",
+ "evidenceLocation": "heading or section",
+ "observedWording": "short factual wording",
+ "fingerprint": "source fingerprint"
+ }
+ ],
+ "contentDrift": [
+ {
+ "path": "owned Buildathon path",
+ "section": "section reference",
+ "observation": "repository observation"
+ }
+ ],
+ "proposedChanges": [
+ {
+ "repository": "Azure-Samples/JavaScript-AI-Buildathon",
+ "path": "owned Buildathon path",
+ "description": "bounded proposed change"
+ }
+ ],
+ "assetImpact": "asset impact or empty string",
+ "codeTourImpact": [
+ {
+ "repository": "exact external report-only repository",
+ "path": "exact observed CodeTour path",
+ "mode": "report-only",
+ "handoff": "manual-maintainer",
+ "observation": "bounded observation"
+ }
+ ],
+ "validationPlan": ["validation step"],
+ "risks": ["risk or uncertainty"],
+ "humanDecisions": ["decision required from Julia"],
+ "sourceFingerprints": ["source fingerprint"],
+ "priorFindingKeys": ["finding:<64 lowercase hex characters>"],
+ "blockedReason": null
+ }
+ ```
+
+ For `material-change`, include at least one proposed Buildathon change and use
+ `high`, `medium`, or `low` learner impact. For `no-material-change`, leave
+ `proposedChanges` empty. For `blocked`, set a concise `blockedReason`, leave
+ `proposedChanges` empty, and use `none` learner impact.
+
+
{{#runtime-import .github/workflows/shared/discovery-policy.md}}
{{#runtime-import .github/agents/agentic-rag-quest-master.agent.md}}
{{#runtime-import .github/workflows/discover-agentic-rag.md}}
- GH_AW_PROMPT_a3b6b9ea9b2518e0_EOF
+ GH_AW_PROMPT_398e46681d5ec93d_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_ENGINE_ID: "copilot"
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -261,7 +407,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -281,7 +430,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: process.env.GH_AW_INPUTS_PRIOR_WEEK_KEY,
+ GH_AW_INPUTS_WEEK_KEY: process.env.GH_AW_INPUTS_WEEK_KEY,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED
}
});
- name: Validate prompt placeholders
@@ -298,7 +450,7 @@ jobs:
if: success()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: activation
+ name: ${{ steps.artifact-prefix.outputs.prefix }}activation
include-hidden-files: true
path: |
/tmp/gh-aw/aw_info.json
@@ -328,6 +480,7 @@ jobs:
GH_AW_WORKFLOW_ID_SANITIZED: discoveragenticrag
outputs:
agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ artifact_prefix: ${{ needs.activation.outputs.artifact_prefix }}
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
@@ -356,6 +509,7 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Set runtime paths
id: set-runtime-paths
run: |
@@ -431,7 +585,7 @@ jobs:
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: activation
+ name: ${{ needs.activation.outputs.artifact_prefix }}activation
path: /tmp/gh-aw
- name: Restore agent config folders from base branch
if: steps.checkout-pr.outcome == 'success'
@@ -455,54 +609,117 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_ad7c8308fa89812f_EOF'
- {"create_issue":{"labels":["discover-agentic-rag"],"max":1,"title_prefix":"[discover-agentic-rag]"}}
- GH_AW_SAFE_OUTPUTS_CONFIG_ad7c8308fa89812f_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_feeffc626fdcdca3_EOF'
+ {"create_report_incomplete_issue":{},"emit-weekly-report":{"description":"Materialize one validated staged weekly quest report artifact","inputs":{"report_json":{"default":null,"description":"A JSON string matching the weekly quest report contract","required":true,"type":"string"},"result":{"default":null,"description":"The exact discovery result","options":["material-change","no-material-change","blocked"],"required":true,"type":"choice"}},"output":"The structured weekly quest report artifact was prepared"},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_feeffc626fdcdca3_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
- "description_suffixes": {
- "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[discover-agentic-rag]\". Labels [\"discover-agentic-rag\"] will be automatically added."
- },
+ "description_suffixes": {},
"repo_params": {},
- "dynamic_tools": []
+ "dynamic_tools": [
+ {
+ "description": "Materialize one validated staged weekly quest report artifact",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "report_json": {
+ "description": "A JSON string matching the weekly quest report contract",
+ "type": "string"
+ },
+ "result": {
+ "description": "The exact discovery result",
+ "enum": [
+ "material-change",
+ "no-material-change",
+ "blocked"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "report_json",
+ "result"
+ ],
+ "type": "object"
+ },
+ "name": "emit_weekly_report"
+ }
+ ]
}
GH_AW_VALIDATION_JSON: |
{
- "create_issue": {
- "defaultMax": 1,
+ "missing_data": {
+ "defaultMax": 20,
"fields": {
- "body": {
- "required": true,
+ "alternatives": {
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 256
},
- "fields": {
- "type": "array"
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
},
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
},
- "parent": {
- "issueOrPRNumber": true
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
},
- "repo": {
+ "reason": {
+ "required": true,
"type": "string",
+ "sanitize": true,
"maxLength": 256
},
- "temporary_id": {
- "type": "string"
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
},
- "title": {
+ "reason": {
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 128
+ "maxLength": 1024
}
}
}
@@ -589,7 +806,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_ae2c34e6877fb55c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_452486d449d867e5_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"safeoutputs": {
@@ -614,7 +831,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_ae2c34e6877fb55c_EOF
+ GH_AW_MCP_CONFIG_452486d449d867e5_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -870,7 +1087,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/sandbox/agent/logs/
@@ -893,6 +1110,7 @@ jobs:
needs:
- activation
- agent
+ - emit_weekly_report
- safe_outputs
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
@@ -900,9 +1118,14 @@ jobs:
runs-on: ubuntu-slim
permissions: {}
concurrency:
- group: "gh-aw-conclusion-discover-agentic-rag"
+ group: "gh-aw-conclusion-discover-agentic-rag-${{ inputs.week_key }}"
cancel-in-progress: false
queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
steps:
- name: Setup Scripts
id: setup
@@ -918,12 +1141,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -932,6 +1156,54 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Discover Agentic RAG"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-agentic-rag.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Agentic RAG"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-agentic-rag.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Agentic RAG"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-agentic-rag.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
- name: Handle agent failure
id: handle_agent_failure
if: always()
@@ -970,6 +1242,86 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
await main();
+ emit_weekly_report:
+ needs:
+ - agent
+ - agent
+ if: >
+ (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'emit_weekly_report') &&
+ (always())
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - name: Download agent output artifact
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.agent.outputs.artifact_prefix }}agent
+ path: ${{ runner.temp }}/gh-aw/safe-jobs/
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Materialize structured quest report
+ run: |
+ node scripts/weekly-refresh-report.mjs materialize \
+ --agent-output "$GH_AW_AGENT_OUTPUT" \
+ --output "/tmp/weekly-refresh/${EXPECTED_QUEST_SLUG}.json" \
+ --week "$EXPECTED_WEEK_KEY" \
+ --prior-week "$EXPECTED_PRIOR_WEEK_KEY" \
+ --quest "$EXPECTED_QUEST_SLUG"
+ env:
+ EXPECTED_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ EXPECTED_QUEST_SLUG: agentic-rag
+ EXPECTED_WEEK_KEY: ${{ inputs.week_key }}
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Upload structured quest report
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ with:
+ if-no-files-found: error
+ name: weekly-refresh-${{ inputs.week_key }}-agentic-rag
+ path: /tmp/weekly-refresh/agentic-rag.json
+ retention-days: 30.0
+
+ pre_activation:
+ runs-on: ubuntu-slim
+ outputs:
+ activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }}
+ matched_command: ''
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Discover Agentic RAG"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-agentic-rag.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Check team membership for workflow
+ id: check_membership
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_REQUIRED_ROLES: "admin,maintainer,write"
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
+ await main();
+
safe_outputs:
needs:
- activation
@@ -994,8 +1346,6 @@ jobs:
code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
@@ -1013,12 +1363,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -1045,7 +1396,8 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,docs.langchain.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"discover-agentic-rag\"],\"max\":1,\"title_prefix\":\"[discover-agentic-rag]\"}}"
+ GH_AW_SAFE_OUTPUT_JOBS: "{\"emit_weekly_report\":\"\"}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_STAGED: "true"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/discover-agentic-rag.md b/.github/workflows/discover-agentic-rag.md
index 266ba99..4f63f5b 100644
--- a/.github/workflows/discover-agentic-rag.md
+++ b/.github/workflows/discover-agentic-rag.md
@@ -2,10 +2,33 @@
name: Discover Agentic RAG
description: Manually audits the Agentic RAG quest and report-only CodeTours using approved evidence
on:
+ workflow_call:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
workflow_dispatch:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
permissions:
contents: read
copilot-requests: write
+concurrency:
+ group: weekly-refresh-${{ inputs.week_key }}-agentic-rag
+ cancel-in-progress: false
+ job-discriminator: ${{ inputs.week_key }}
strict: true
engine:
id: copilot
@@ -15,6 +38,12 @@ network:
- docs.langchain.com
imports:
- shared/discovery-policy.md
+ - uses: shared/report-output.md
+ with:
+ quest-id: 3
+ quest-slug: agentic-rag
+ quest-title: LangChain.js Agentic RAG
+ quest-label: quest/3-agentic-rag
- ../agents/agentic-rag-quest-master.agent.md
safe-outputs:
activation-comments: false
@@ -28,9 +57,9 @@ safe-outputs:
timeout-minutes: 20
---
-# Phase 1 Agentic RAG Discovery
+# Phase 2A Agentic RAG Reporting
-Run a read-only manual trial for quest slug `agentic-rag`.
+Run a read-only staged report for quest slug `agentic-rag`.
## Execution
@@ -43,15 +72,19 @@ Run a read-only manual trial for quest slug `agentic-rag`.
5. Invoke `external-codetour-auditor` once for the six report-only CodeTours.
6. Reconcile all reports. Reject unsupported Agentic RAG classifications and
make uncertainty explicit.
-7. Return one concise GitHub-flavored Markdown report. Do not create or update
- any persistent resource.
+7. Compare against any exact prior finding keys supplied for
+ `${{ inputs.prior_week_key }}`.
+8. Call `emit_weekly_report` exactly once with the structured report.
+9. Return one concise GitHub-flavored Markdown summary. Do not create or update
+ repository content or GitHub issues, comments, branches, or pull requests.
## Final report contract
Start nested headings at `###` and include:
- `Result: material-change | no-material-change | blocked`
-- Trial key `phase1-${{ github.run_id }}` and quest identity
+- Week key `${{ inputs.week_key }}`, prior week `${{ inputs.prior_week_key }}`,
+ and quest identity
- Executive summary and current quest status
- Official-source facts and deltas with lifecycle, URL, location, observed
wording, and fingerprint
diff --git a/.github/workflows/discover-context-engineering.lock.yml b/.github/workflows/discover-context-engineering.lock.yml
index 6643f33..ca61f86 100644
--- a/.github/workflows/discover-context-engineering.lock.yml
+++ b/.github/workflows/discover-context-engineering.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"49e8926bde3532391b4f603f09f68e31db69e9379b7aecbadb2e465e6feb3c81","body_hash":"fd15df223f45e6cdf1e8a4dff08cbbd68131cac1f4d358085d19c7191df69db6","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"05b1ef12178775faab2f5571cb01e1a38e248e51bf2d4c3349a7b3eae712ccbc","body_hash":"b851cd0d53032fed7b6ca9c75ef5fdb047d2756d885b2ce996251f9d1d280657","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
# ___ _ _
# / _ \ | | (_)
@@ -28,6 +28,7 @@
# Imports:
# - ../agents/context-engineering-quest-master.agent.md
# - shared/discovery-policy.md
+# - shared/report-output.md
#
# Secrets used:
# - GH_AW_GITHUB_MCP_SERVER_TOKEN
@@ -53,6 +54,26 @@
name: "Discover Context Engineering"
on:
+ workflow_call:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ secrets:
+ GH_AW_GITHUB_MCP_SERVER_TOKEN:
+ required: false
+ GH_AW_GITHUB_TOKEN:
+ required: false
workflow_dispatch:
inputs:
aw_context:
@@ -60,21 +81,33 @@ on:
description: "Agent caller context (used internally by Agentic Workflows)."
required: false
type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
permissions: {}
concurrency:
- group: "gh-aw-${{ github.workflow }}"
+ cancel-in-progress: false
+ group: weekly-refresh-${{ inputs.week_key }}-context-engineering
run-name: "Discover Context Engineering"
jobs:
activation:
+ needs: pre_activation
+ if: needs.pre_activation.outputs.activated == 'true'
runs-on: ubuntu-slim
permissions:
actions: read
contents: read
outputs:
+ artifact_prefix: ${{ steps.artifact-prefix.outputs.prefix }}
comment_id: ""
comment_repo: ""
engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
@@ -84,6 +117,10 @@ jobs:
setup-span-id: ${{ steps.setup.outputs.span-id }}
setup-trace-id: ${{ steps.setup.outputs.trace-id }}
stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ target_checkout_ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
+ target_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}
+ target_repo: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ target_repo_name: ${{ steps.resolve-host-repo.outputs.target_repo_name }}
steps:
- name: Setup Scripts
id: setup
@@ -91,12 +128,34 @@ jobs:
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
+ trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }}
env:
GH_AW_SETUP_WORKFLOW_NAME: "Discover Context Engineering"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-context-engineering.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Resolve host repo for activation checkout
+ id: resolve-host-repo
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ JOB_WORKFLOW_REPOSITORY: ${{ job.workflow_repository }}
+ JOB_WORKFLOW_SHA: ${{ job.workflow_sha }}
+ JOB_WORKFLOW_REF: ${{ job.workflow_ref }}
+ JOB_WORKFLOW_FILE_PATH: ${{ job.workflow_file_path }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/resolve_host_repo.cjs');
+ await main();
+ - name: Compute artifact prefix
+ id: artifact-prefix
+ env:
+ INPUTS_JSON: ${{ toJSON(inputs) }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/compute_artifact_prefix.sh"
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -116,6 +175,7 @@ jobs:
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
+ GH_AW_INFO_TARGET_REPO: ${{ steps.resolve-host-repo.outputs.target_repo }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
@@ -123,10 +183,19 @@ jobs:
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
await main(core, context);
+ - name: Print cross-repo setup guidance
+ if: failure() && steps.resolve-host-repo.outputs.target_repo != github.repository
+ run: |
+ echo "::error::COPILOT_GITHUB_TOKEN must be configured in the CALLER repository's secrets."
+ echo "::error::For cross-repo workflow_call, secrets must be set in the repository that triggers the workflow."
+ echo "::error::See: https://github.github.com/gh-aw/patterns/central-repo-ops/#cross-repo-setup"
- name: Checkout .github and .agents folders
+ if: steps.resolve-host-repo.outputs.target_repo == github.repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
+ repository: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
sparse-checkout: |
.github
.agents
@@ -179,27 +248,26 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
# poutine:ignore untrusted_checkout_exec
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_acfb4f332e62d21b_EOF'
+ cat << 'GH_AW_PROMPT_2c381e60a0e5db24_EOF'
- GH_AW_PROMPT_acfb4f332e62d21b_EOF
+ GH_AW_PROMPT_2c381e60a0e5db24_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_acfb4f332e62d21b_EOF'
+ cat << 'GH_AW_PROMPT_2c381e60a0e5db24_EOF'
- Tools: create_issue
- GH_AW_PROMPT_acfb4f332e62d21b_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md"
- cat << 'GH_AW_PROMPT_acfb4f332e62d21b_EOF'
+ Tools: missing_tool, missing_data, noop, emit_weekly_report
- GH_AW_PROMPT_acfb4f332e62d21b_EOF
+ GH_AW_PROMPT_2c381e60a0e5db24_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_acfb4f332e62d21b_EOF'
+ cat << 'GH_AW_PROMPT_2c381e60a0e5db24_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -228,21 +296,99 @@ jobs:
{{/if}}
- GH_AW_PROMPT_acfb4f332e62d21b_EOF
+ GH_AW_PROMPT_2c381e60a0e5db24_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_acfb4f332e62d21b_EOF'
+ cat << 'GH_AW_PROMPT_2c381e60a0e5db24_EOF'
+ Apply this staged weekly reporting contract for quest
+ `context-engineering`:
+
+ - Quest identity is `5`,
+ `Context Engineering in Agents`, and
+ `quest/5-context-engineering`.
+ - The reporting week is `__GH_AW_INPUTS_WEEK_KEY__` and the prior report locator is
+ `weekly-refresh:__GH_AW_INPUTS_PRIOR_WEEK_KEY__:context-engineering`.
+ - Compare current allowed-source evidence, current Buildathon content, and any
+ prior finding keys available from the exact prior locator. Never broaden the
+ search to unrelated issues or repositories.
+ - Call `emit_weekly_report` exactly once, including blocked runs. The `result`
+ argument and `report_json.result` must match.
+ - Do not write a report file yourself. The deterministic safe-output job validates
+ the JSON and creates the run-scoped artifact.
+
+ The `report_json` value must encode exactly this version 1 shape:
+
+ ```json
+ {
+ "schemaVersion": 1,
+ "weekKey": "__GH_AW_INPUTS_WEEK_KEY__",
+ "priorWeekKey": "__GH_AW_INPUTS_PRIOR_WEEK_KEY__",
+ "questSlug": "context-engineering",
+ "result": "material-change | no-material-change | blocked",
+ "executiveSummary": "short summary",
+ "currentQuestStatus": "current status",
+ "learnerImpact": "high | medium | low | none",
+ "officialSourceDeltas": [
+ {
+ "fact": "source fact",
+ "lifecycle": "GA | Public preview | Private preview | Not stated by allowed sources",
+ "evidenceUrl": "allowlisted URL",
+ "evidenceLocation": "heading or section",
+ "observedWording": "short factual wording",
+ "fingerprint": "source fingerprint"
+ }
+ ],
+ "contentDrift": [
+ {
+ "path": "owned Buildathon path",
+ "section": "section reference",
+ "observation": "repository observation"
+ }
+ ],
+ "proposedChanges": [
+ {
+ "repository": "Azure-Samples/JavaScript-AI-Buildathon",
+ "path": "owned Buildathon path",
+ "description": "bounded proposed change"
+ }
+ ],
+ "assetImpact": "asset impact or empty string",
+ "codeTourImpact": [
+ {
+ "repository": "exact external report-only repository",
+ "path": "exact observed CodeTour path",
+ "mode": "report-only",
+ "handoff": "manual-maintainer",
+ "observation": "bounded observation"
+ }
+ ],
+ "validationPlan": ["validation step"],
+ "risks": ["risk or uncertainty"],
+ "humanDecisions": ["decision required from Julia"],
+ "sourceFingerprints": ["source fingerprint"],
+ "priorFindingKeys": ["finding:<64 lowercase hex characters>"],
+ "blockedReason": null
+ }
+ ```
+
+ For `material-change`, include at least one proposed Buildathon change and use
+ `high`, `medium`, or `low` learner impact. For `no-material-change`, leave
+ `proposedChanges` empty. For `blocked`, set a concise `blockedReason`, leave
+ `proposedChanges` empty, and use `none` learner impact.
+
+
{{#runtime-import .github/workflows/shared/discovery-policy.md}}
{{#runtime-import .github/agents/context-engineering-quest-master.agent.md}}
{{#runtime-import .github/workflows/discover-context-engineering.md}}
- GH_AW_PROMPT_acfb4f332e62d21b_EOF
+ GH_AW_PROMPT_2c381e60a0e5db24_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_ENGINE_ID: "copilot"
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -261,7 +407,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -281,7 +430,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: process.env.GH_AW_INPUTS_PRIOR_WEEK_KEY,
+ GH_AW_INPUTS_WEEK_KEY: process.env.GH_AW_INPUTS_WEEK_KEY,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED
}
});
- name: Validate prompt placeholders
@@ -298,7 +450,7 @@ jobs:
if: success()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: activation
+ name: ${{ steps.artifact-prefix.outputs.prefix }}activation
include-hidden-files: true
path: |
/tmp/gh-aw/aw_info.json
@@ -328,6 +480,7 @@ jobs:
GH_AW_WORKFLOW_ID_SANITIZED: discovercontextengineering
outputs:
agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ artifact_prefix: ${{ needs.activation.outputs.artifact_prefix }}
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
@@ -356,6 +509,7 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Set runtime paths
id: set-runtime-paths
run: |
@@ -431,7 +585,7 @@ jobs:
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: activation
+ name: ${{ needs.activation.outputs.artifact_prefix }}activation
path: /tmp/gh-aw
- name: Restore agent config folders from base branch
if: steps.checkout-pr.outcome == 'success'
@@ -455,54 +609,117 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_64043bc31bf5d344_EOF'
- {"create_issue":{"labels":["discover-context-engineering"],"max":1,"title_prefix":"[discover-context-engineering]"}}
- GH_AW_SAFE_OUTPUTS_CONFIG_64043bc31bf5d344_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_f358afc67f339d4a_EOF'
+ {"create_report_incomplete_issue":{},"emit-weekly-report":{"description":"Materialize one validated staged weekly quest report artifact","inputs":{"report_json":{"default":null,"description":"A JSON string matching the weekly quest report contract","required":true,"type":"string"},"result":{"default":null,"description":"The exact discovery result","options":["material-change","no-material-change","blocked"],"required":true,"type":"choice"}},"output":"The structured weekly quest report artifact was prepared"},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_f358afc67f339d4a_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
- "description_suffixes": {
- "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[discover-context-engineering]\". Labels [\"discover-context-engineering\"] will be automatically added."
- },
+ "description_suffixes": {},
"repo_params": {},
- "dynamic_tools": []
+ "dynamic_tools": [
+ {
+ "description": "Materialize one validated staged weekly quest report artifact",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "report_json": {
+ "description": "A JSON string matching the weekly quest report contract",
+ "type": "string"
+ },
+ "result": {
+ "description": "The exact discovery result",
+ "enum": [
+ "material-change",
+ "no-material-change",
+ "blocked"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "report_json",
+ "result"
+ ],
+ "type": "object"
+ },
+ "name": "emit_weekly_report"
+ }
+ ]
}
GH_AW_VALIDATION_JSON: |
{
- "create_issue": {
- "defaultMax": 1,
+ "missing_data": {
+ "defaultMax": 20,
"fields": {
- "body": {
- "required": true,
+ "alternatives": {
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 256
},
- "fields": {
- "type": "array"
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
},
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
},
- "parent": {
- "issueOrPRNumber": true
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
},
- "repo": {
+ "reason": {
+ "required": true,
"type": "string",
+ "sanitize": true,
"maxLength": 256
},
- "temporary_id": {
- "type": "string"
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
},
- "title": {
+ "reason": {
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 128
+ "maxLength": 1024
}
}
}
@@ -589,7 +806,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_d614d33ae7fd2f23_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_63f2df9a30913f38_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"safeoutputs": {
@@ -614,7 +831,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_d614d33ae7fd2f23_EOF
+ GH_AW_MCP_CONFIG_63f2df9a30913f38_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -870,7 +1087,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/sandbox/agent/logs/
@@ -893,6 +1110,7 @@ jobs:
needs:
- activation
- agent
+ - emit_weekly_report
- safe_outputs
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
@@ -900,9 +1118,14 @@ jobs:
runs-on: ubuntu-slim
permissions: {}
concurrency:
- group: "gh-aw-conclusion-discover-context-engineering"
+ group: "gh-aw-conclusion-discover-context-engineering-${{ inputs.week_key }}"
cancel-in-progress: false
queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
steps:
- name: Setup Scripts
id: setup
@@ -918,12 +1141,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -932,6 +1156,54 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Discover Context Engineering"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-context-engineering.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Context Engineering"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-context-engineering.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Context Engineering"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-context-engineering.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
- name: Handle agent failure
id: handle_agent_failure
if: always()
@@ -970,6 +1242,86 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
await main();
+ emit_weekly_report:
+ needs:
+ - agent
+ - agent
+ if: >
+ (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'emit_weekly_report') &&
+ (always())
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - name: Download agent output artifact
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.agent.outputs.artifact_prefix }}agent
+ path: ${{ runner.temp }}/gh-aw/safe-jobs/
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Materialize structured quest report
+ run: |
+ node scripts/weekly-refresh-report.mjs materialize \
+ --agent-output "$GH_AW_AGENT_OUTPUT" \
+ --output "/tmp/weekly-refresh/${EXPECTED_QUEST_SLUG}.json" \
+ --week "$EXPECTED_WEEK_KEY" \
+ --prior-week "$EXPECTED_PRIOR_WEEK_KEY" \
+ --quest "$EXPECTED_QUEST_SLUG"
+ env:
+ EXPECTED_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ EXPECTED_QUEST_SLUG: context-engineering
+ EXPECTED_WEEK_KEY: ${{ inputs.week_key }}
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Upload structured quest report
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ with:
+ if-no-files-found: error
+ name: weekly-refresh-${{ inputs.week_key }}-context-engineering
+ path: /tmp/weekly-refresh/context-engineering.json
+ retention-days: 30.0
+
+ pre_activation:
+ runs-on: ubuntu-slim
+ outputs:
+ activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }}
+ matched_command: ''
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Discover Context Engineering"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-context-engineering.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Check team membership for workflow
+ id: check_membership
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_REQUIRED_ROLES: "admin,maintainer,write"
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
+ await main();
+
safe_outputs:
needs:
- activation
@@ -994,8 +1346,6 @@ jobs:
code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
@@ -1013,12 +1363,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -1045,7 +1396,8 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,docs.langchain.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,modelcontextprotocol.io,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"discover-context-engineering\"],\"max\":1,\"title_prefix\":\"[discover-context-engineering]\"}}"
+ GH_AW_SAFE_OUTPUT_JOBS: "{\"emit_weekly_report\":\"\"}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_STAGED: "true"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/discover-context-engineering.md b/.github/workflows/discover-context-engineering.md
index 75ec0ec..92ce498 100644
--- a/.github/workflows/discover-context-engineering.md
+++ b/.github/workflows/discover-context-engineering.md
@@ -2,10 +2,33 @@
name: Discover Context Engineering
description: Manually audits the Context Engineering quest and report-only CodeTours using approved evidence
on:
+ workflow_call:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
workflow_dispatch:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
permissions:
contents: read
copilot-requests: write
+concurrency:
+ group: weekly-refresh-${{ inputs.week_key }}-context-engineering
+ cancel-in-progress: false
+ job-discriminator: ${{ inputs.week_key }}
strict: true
engine:
id: copilot
@@ -16,6 +39,12 @@ network:
- modelcontextprotocol.io
imports:
- shared/discovery-policy.md
+ - uses: shared/report-output.md
+ with:
+ quest-id: 5
+ quest-slug: context-engineering
+ quest-title: Context Engineering in Agents
+ quest-label: quest/5-context-engineering
- ../agents/context-engineering-quest-master.agent.md
safe-outputs:
activation-comments: false
@@ -29,9 +58,9 @@ safe-outputs:
timeout-minutes: 20
---
-# Phase 1 Context Engineering Discovery
+# Phase 2A Context Engineering Reporting
-Run a read-only manual trial for quest slug `context-engineering`.
+Run a read-only staged report for quest slug `context-engineering`.
## Execution
@@ -44,15 +73,19 @@ Run a read-only manual trial for quest slug `context-engineering`.
5. Invoke `external-codetour-auditor` once for the six report-only CodeTours.
6. Reconcile all reports. Reject unsupported claims and make uncertainty
explicit.
-7. Return one concise GitHub-flavored Markdown report. Do not create or update
- any persistent resource.
+7. Compare against any exact prior finding keys supplied for
+ `${{ inputs.prior_week_key }}`.
+8. Call `emit_weekly_report` exactly once with the structured report.
+9. Return one concise GitHub-flavored Markdown summary. Do not create or update
+ repository content or GitHub issues, comments, branches, or pull requests.
## Final report contract
Start nested headings at `###` and include:
- `Result: material-change | no-material-change | blocked`
-- Trial key `phase1-${{ github.run_id }}` and quest identity
+- Week key `${{ inputs.week_key }}`, prior week `${{ inputs.prior_week_key }}`,
+ and quest identity
- Executive summary and current quest status
- Official-source facts and deltas with lifecycle, URL, location, observed
wording, and fingerprint
diff --git a/.github/workflows/discover-foundry-local.lock.yml b/.github/workflows/discover-foundry-local.lock.yml
index 18a38f0..e5da413 100644
--- a/.github/workflows/discover-foundry-local.lock.yml
+++ b/.github/workflows/discover-foundry-local.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"ffd28d981373a6a3e08279a639a6a3f0d680b7561ad2ef060ee52c3fe4c4d19f","body_hash":"d5b8d31dfbb2a1824f25a64bfb4185f85c4fde16b39a8fab676f9fea654c87aa","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"c15789717e0016e7b90433c3d4b068fa9e1cc8e688358bbbc8ae31a65c2c9691","body_hash":"83f53d44d855013681ddd0ded4e1003eb2e7ed2c08df6651e06e4c2d3c497d21","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
# ___ _ _
# / _ \ | | (_)
@@ -28,6 +28,7 @@
# Imports:
# - ../agents/foundry-local-quest-master.agent.md
# - shared/discovery-policy.md
+# - shared/report-output.md
#
# Secrets used:
# - GH_AW_GITHUB_MCP_SERVER_TOKEN
@@ -53,6 +54,26 @@
name: "Discover Foundry Local"
on:
+ workflow_call:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ secrets:
+ GH_AW_GITHUB_MCP_SERVER_TOKEN:
+ required: false
+ GH_AW_GITHUB_TOKEN:
+ required: false
workflow_dispatch:
inputs:
aw_context:
@@ -60,21 +81,33 @@ on:
description: "Agent caller context (used internally by Agentic Workflows)."
required: false
type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
permissions: {}
concurrency:
- group: "gh-aw-${{ github.workflow }}"
+ cancel-in-progress: false
+ group: weekly-refresh-${{ inputs.week_key }}-foundry-local
run-name: "Discover Foundry Local"
jobs:
activation:
+ needs: pre_activation
+ if: needs.pre_activation.outputs.activated == 'true'
runs-on: ubuntu-slim
permissions:
actions: read
contents: read
outputs:
+ artifact_prefix: ${{ steps.artifact-prefix.outputs.prefix }}
comment_id: ""
comment_repo: ""
engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
@@ -84,6 +117,10 @@ jobs:
setup-span-id: ${{ steps.setup.outputs.span-id }}
setup-trace-id: ${{ steps.setup.outputs.trace-id }}
stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ target_checkout_ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
+ target_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}
+ target_repo: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ target_repo_name: ${{ steps.resolve-host-repo.outputs.target_repo_name }}
steps:
- name: Setup Scripts
id: setup
@@ -91,12 +128,34 @@ jobs:
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
+ trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }}
env:
GH_AW_SETUP_WORKFLOW_NAME: "Discover Foundry Local"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-foundry-local.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Resolve host repo for activation checkout
+ id: resolve-host-repo
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ JOB_WORKFLOW_REPOSITORY: ${{ job.workflow_repository }}
+ JOB_WORKFLOW_SHA: ${{ job.workflow_sha }}
+ JOB_WORKFLOW_REF: ${{ job.workflow_ref }}
+ JOB_WORKFLOW_FILE_PATH: ${{ job.workflow_file_path }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/resolve_host_repo.cjs');
+ await main();
+ - name: Compute artifact prefix
+ id: artifact-prefix
+ env:
+ INPUTS_JSON: ${{ toJSON(inputs) }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/compute_artifact_prefix.sh"
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -116,6 +175,7 @@ jobs:
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
+ GH_AW_INFO_TARGET_REPO: ${{ steps.resolve-host-repo.outputs.target_repo }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
@@ -123,10 +183,19 @@ jobs:
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
await main(core, context);
+ - name: Print cross-repo setup guidance
+ if: failure() && steps.resolve-host-repo.outputs.target_repo != github.repository
+ run: |
+ echo "::error::COPILOT_GITHUB_TOKEN must be configured in the CALLER repository's secrets."
+ echo "::error::For cross-repo workflow_call, secrets must be set in the repository that triggers the workflow."
+ echo "::error::See: https://github.github.com/gh-aw/patterns/central-repo-ops/#cross-repo-setup"
- name: Checkout .github and .agents folders
+ if: steps.resolve-host-repo.outputs.target_repo == github.repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
+ repository: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
sparse-checkout: |
.github
.agents
@@ -179,27 +248,26 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
# poutine:ignore untrusted_checkout_exec
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_2f12f4a6d80d4765_EOF'
+ cat << 'GH_AW_PROMPT_bd2bdf51cc699cb9_EOF'
- GH_AW_PROMPT_2f12f4a6d80d4765_EOF
+ GH_AW_PROMPT_bd2bdf51cc699cb9_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_2f12f4a6d80d4765_EOF'
+ cat << 'GH_AW_PROMPT_bd2bdf51cc699cb9_EOF'
- Tools: create_issue
- GH_AW_PROMPT_2f12f4a6d80d4765_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md"
- cat << 'GH_AW_PROMPT_2f12f4a6d80d4765_EOF'
+ Tools: missing_tool, missing_data, noop, emit_weekly_report
- GH_AW_PROMPT_2f12f4a6d80d4765_EOF
+ GH_AW_PROMPT_bd2bdf51cc699cb9_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_2f12f4a6d80d4765_EOF'
+ cat << 'GH_AW_PROMPT_bd2bdf51cc699cb9_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -228,21 +296,99 @@ jobs:
{{/if}}
- GH_AW_PROMPT_2f12f4a6d80d4765_EOF
+ GH_AW_PROMPT_bd2bdf51cc699cb9_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_2f12f4a6d80d4765_EOF'
+ cat << 'GH_AW_PROMPT_bd2bdf51cc699cb9_EOF'
+ Apply this staged weekly reporting contract for quest
+ `foundry-local`:
+
+ - Quest identity is `1`,
+ `Foundry Local`, and
+ `quest/1-foundry-local`.
+ - The reporting week is `__GH_AW_INPUTS_WEEK_KEY__` and the prior report locator is
+ `weekly-refresh:__GH_AW_INPUTS_PRIOR_WEEK_KEY__:foundry-local`.
+ - Compare current allowed-source evidence, current Buildathon content, and any
+ prior finding keys available from the exact prior locator. Never broaden the
+ search to unrelated issues or repositories.
+ - Call `emit_weekly_report` exactly once, including blocked runs. The `result`
+ argument and `report_json.result` must match.
+ - Do not write a report file yourself. The deterministic safe-output job validates
+ the JSON and creates the run-scoped artifact.
+
+ The `report_json` value must encode exactly this version 1 shape:
+
+ ```json
+ {
+ "schemaVersion": 1,
+ "weekKey": "__GH_AW_INPUTS_WEEK_KEY__",
+ "priorWeekKey": "__GH_AW_INPUTS_PRIOR_WEEK_KEY__",
+ "questSlug": "foundry-local",
+ "result": "material-change | no-material-change | blocked",
+ "executiveSummary": "short summary",
+ "currentQuestStatus": "current status",
+ "learnerImpact": "high | medium | low | none",
+ "officialSourceDeltas": [
+ {
+ "fact": "source fact",
+ "lifecycle": "GA | Public preview | Private preview | Not stated by allowed sources",
+ "evidenceUrl": "allowlisted URL",
+ "evidenceLocation": "heading or section",
+ "observedWording": "short factual wording",
+ "fingerprint": "source fingerprint"
+ }
+ ],
+ "contentDrift": [
+ {
+ "path": "owned Buildathon path",
+ "section": "section reference",
+ "observation": "repository observation"
+ }
+ ],
+ "proposedChanges": [
+ {
+ "repository": "Azure-Samples/JavaScript-AI-Buildathon",
+ "path": "owned Buildathon path",
+ "description": "bounded proposed change"
+ }
+ ],
+ "assetImpact": "asset impact or empty string",
+ "codeTourImpact": [
+ {
+ "repository": "exact external report-only repository",
+ "path": "exact observed CodeTour path",
+ "mode": "report-only",
+ "handoff": "manual-maintainer",
+ "observation": "bounded observation"
+ }
+ ],
+ "validationPlan": ["validation step"],
+ "risks": ["risk or uncertainty"],
+ "humanDecisions": ["decision required from Julia"],
+ "sourceFingerprints": ["source fingerprint"],
+ "priorFindingKeys": ["finding:<64 lowercase hex characters>"],
+ "blockedReason": null
+ }
+ ```
+
+ For `material-change`, include at least one proposed Buildathon change and use
+ `high`, `medium`, or `low` learner impact. For `no-material-change`, leave
+ `proposedChanges` empty. For `blocked`, set a concise `blockedReason`, leave
+ `proposedChanges` empty, and use `none` learner impact.
+
+
{{#runtime-import .github/workflows/shared/discovery-policy.md}}
{{#runtime-import .github/agents/foundry-local-quest-master.agent.md}}
{{#runtime-import .github/workflows/discover-foundry-local.md}}
- GH_AW_PROMPT_2f12f4a6d80d4765_EOF
+ GH_AW_PROMPT_bd2bdf51cc699cb9_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_ENGINE_ID: "copilot"
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -261,7 +407,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -281,7 +430,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: process.env.GH_AW_INPUTS_PRIOR_WEEK_KEY,
+ GH_AW_INPUTS_WEEK_KEY: process.env.GH_AW_INPUTS_WEEK_KEY,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED
}
});
- name: Validate prompt placeholders
@@ -298,7 +450,7 @@ jobs:
if: success()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: activation
+ name: ${{ steps.artifact-prefix.outputs.prefix }}activation
include-hidden-files: true
path: |
/tmp/gh-aw/aw_info.json
@@ -328,6 +480,7 @@ jobs:
GH_AW_WORKFLOW_ID_SANITIZED: discoverfoundrylocal
outputs:
agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ artifact_prefix: ${{ needs.activation.outputs.artifact_prefix }}
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
@@ -356,6 +509,7 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Set runtime paths
id: set-runtime-paths
run: |
@@ -431,7 +585,7 @@ jobs:
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: activation
+ name: ${{ needs.activation.outputs.artifact_prefix }}activation
path: /tmp/gh-aw
- name: Restore agent config folders from base branch
if: steps.checkout-pr.outcome == 'success'
@@ -455,54 +609,117 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_60082d2bd72576f5_EOF'
- {"create_issue":{"labels":["discover-foundry-local"],"max":1,"title_prefix":"[discover-foundry-local]"}}
- GH_AW_SAFE_OUTPUTS_CONFIG_60082d2bd72576f5_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_4483f6c7d573195b_EOF'
+ {"create_report_incomplete_issue":{},"emit-weekly-report":{"description":"Materialize one validated staged weekly quest report artifact","inputs":{"report_json":{"default":null,"description":"A JSON string matching the weekly quest report contract","required":true,"type":"string"},"result":{"default":null,"description":"The exact discovery result","options":["material-change","no-material-change","blocked"],"required":true,"type":"choice"}},"output":"The structured weekly quest report artifact was prepared"},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_4483f6c7d573195b_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
- "description_suffixes": {
- "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[discover-foundry-local]\". Labels [\"discover-foundry-local\"] will be automatically added."
- },
+ "description_suffixes": {},
"repo_params": {},
- "dynamic_tools": []
+ "dynamic_tools": [
+ {
+ "description": "Materialize one validated staged weekly quest report artifact",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "report_json": {
+ "description": "A JSON string matching the weekly quest report contract",
+ "type": "string"
+ },
+ "result": {
+ "description": "The exact discovery result",
+ "enum": [
+ "material-change",
+ "no-material-change",
+ "blocked"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "report_json",
+ "result"
+ ],
+ "type": "object"
+ },
+ "name": "emit_weekly_report"
+ }
+ ]
}
GH_AW_VALIDATION_JSON: |
{
- "create_issue": {
- "defaultMax": 1,
+ "missing_data": {
+ "defaultMax": 20,
"fields": {
- "body": {
- "required": true,
+ "alternatives": {
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 256
},
- "fields": {
- "type": "array"
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
},
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
},
- "parent": {
- "issueOrPRNumber": true
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
},
- "repo": {
+ "reason": {
+ "required": true,
"type": "string",
+ "sanitize": true,
"maxLength": 256
},
- "temporary_id": {
- "type": "string"
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
},
- "title": {
+ "reason": {
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 128
+ "maxLength": 1024
}
}
}
@@ -589,7 +806,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_c8b7aebc66316797_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_f5b081f3a914d617_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"safeoutputs": {
@@ -614,7 +831,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_c8b7aebc66316797_EOF
+ GH_AW_MCP_CONFIG_f5b081f3a914d617_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -870,7 +1087,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/sandbox/agent/logs/
@@ -893,6 +1110,7 @@ jobs:
needs:
- activation
- agent
+ - emit_weekly_report
- safe_outputs
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
@@ -900,9 +1118,14 @@ jobs:
runs-on: ubuntu-slim
permissions: {}
concurrency:
- group: "gh-aw-conclusion-discover-foundry-local"
+ group: "gh-aw-conclusion-discover-foundry-local-${{ inputs.week_key }}"
cancel-in-progress: false
queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
steps:
- name: Setup Scripts
id: setup
@@ -918,12 +1141,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -932,6 +1156,54 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Discover Foundry Local"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-foundry-local.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Foundry Local"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-foundry-local.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Foundry Local"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-foundry-local.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
- name: Handle agent failure
id: handle_agent_failure
if: always()
@@ -970,6 +1242,86 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
await main();
+ emit_weekly_report:
+ needs:
+ - agent
+ - agent
+ if: >
+ (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'emit_weekly_report') &&
+ (always())
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - name: Download agent output artifact
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.agent.outputs.artifact_prefix }}agent
+ path: ${{ runner.temp }}/gh-aw/safe-jobs/
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Materialize structured quest report
+ run: |
+ node scripts/weekly-refresh-report.mjs materialize \
+ --agent-output "$GH_AW_AGENT_OUTPUT" \
+ --output "/tmp/weekly-refresh/${EXPECTED_QUEST_SLUG}.json" \
+ --week "$EXPECTED_WEEK_KEY" \
+ --prior-week "$EXPECTED_PRIOR_WEEK_KEY" \
+ --quest "$EXPECTED_QUEST_SLUG"
+ env:
+ EXPECTED_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ EXPECTED_QUEST_SLUG: foundry-local
+ EXPECTED_WEEK_KEY: ${{ inputs.week_key }}
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Upload structured quest report
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ with:
+ if-no-files-found: error
+ name: weekly-refresh-${{ inputs.week_key }}-foundry-local
+ path: /tmp/weekly-refresh/foundry-local.json
+ retention-days: 30.0
+
+ pre_activation:
+ runs-on: ubuntu-slim
+ outputs:
+ activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }}
+ matched_command: ''
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Discover Foundry Local"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-foundry-local.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Check team membership for workflow
+ id: check_membership
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_REQUIRED_ROLES: "admin,maintainer,write"
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
+ await main();
+
safe_outputs:
needs:
- activation
@@ -994,8 +1346,6 @@ jobs:
code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
@@ -1013,12 +1363,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -1045,7 +1396,8 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.foundrylocal.ai,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"discover-foundry-local\"],\"max\":1,\"title_prefix\":\"[discover-foundry-local]\"}}"
+ GH_AW_SAFE_OUTPUT_JOBS: "{\"emit_weekly_report\":\"\"}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_STAGED: "true"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/discover-foundry-local.md b/.github/workflows/discover-foundry-local.md
index 528b8e6..0dc6c9a 100644
--- a/.github/workflows/discover-foundry-local.md
+++ b/.github/workflows/discover-foundry-local.md
@@ -2,10 +2,33 @@
name: Discover Foundry Local
description: Manually audits the Foundry Local quest using read-only approved evidence
on:
+ workflow_call:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
workflow_dispatch:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
permissions:
contents: read
copilot-requests: write
+concurrency:
+ group: weekly-refresh-${{ inputs.week_key }}-foundry-local
+ cancel-in-progress: false
+ job-discriminator: ${{ inputs.week_key }}
strict: true
engine:
id: copilot
@@ -15,6 +38,12 @@ network:
- www.foundrylocal.ai
imports:
- shared/discovery-policy.md
+ - uses: shared/report-output.md
+ with:
+ quest-id: 1
+ quest-slug: foundry-local
+ quest-title: Foundry Local
+ quest-label: quest/1-foundry-local
- ../agents/foundry-local-quest-master.agent.md
safe-outputs:
activation-comments: false
@@ -28,9 +57,9 @@ safe-outputs:
timeout-minutes: 20
---
-# Phase 1 Foundry Local Discovery
+# Phase 2A Foundry Local Reporting
-Run a read-only manual trial for quest slug `foundry-local`.
+Run a read-only staged report for quest slug `foundry-local`.
## Execution
@@ -42,15 +71,19 @@ Run a read-only manual trial for quest slug `foundry-local`.
file and section references.
5. Reconcile both reports. Reject unsupported claims and make uncertainty
explicit.
-6. Return one concise GitHub-flavored Markdown report. Do not create or update
- any persistent resource.
+6. Compare against any exact prior finding keys supplied for
+ `${{ inputs.prior_week_key }}`.
+7. Call `emit_weekly_report` exactly once with the structured report.
+8. Return one concise GitHub-flavored Markdown summary. Do not create or update
+ repository content or GitHub issues, comments, branches, or pull requests.
## Final report contract
Start nested headings at `###` and include:
- `Result: material-change | no-material-change | blocked`
-- Trial key `phase1-${{ github.run_id }}` and quest identity
+- Week key `${{ inputs.week_key }}`, prior week `${{ inputs.prior_week_key }}`,
+ and quest identity
- Executive summary and current quest status
- Official-source facts and deltas with lifecycle, URL, location, observed
wording, and fingerprint
diff --git a/.github/workflows/discover-foundry-toolkit.lock.yml b/.github/workflows/discover-foundry-toolkit.lock.yml
index 8d2c424..56e3616 100644
--- a/.github/workflows/discover-foundry-toolkit.lock.yml
+++ b/.github/workflows/discover-foundry-toolkit.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"d40bf6b72bb59d3ea2ec5e77a70bfd087c2b186aa28464e7aeda540f528ddd2a","body_hash":"b0b0a8da1c790cf7d2927b55d1de770cff90a219da0feea563648b70b84f2f41","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"6c0b732fbf281a8abfe5690fceaa11600f1fd5ff3b7564865ddfe12679750563","body_hash":"acae829c960cd47d3dde1b3ab67b1b05f7bf070683d8809bb80913c4d53d3687","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
# ___ _ _
# / _ \ | | (_)
@@ -28,6 +28,7 @@
# Imports:
# - ../agents/foundry-toolkit-quest-master.agent.md
# - shared/discovery-policy.md
+# - shared/report-output.md
#
# Secrets used:
# - GH_AW_GITHUB_MCP_SERVER_TOKEN
@@ -53,6 +54,26 @@
name: "Discover Foundry Toolkit"
on:
+ workflow_call:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ secrets:
+ GH_AW_GITHUB_MCP_SERVER_TOKEN:
+ required: false
+ GH_AW_GITHUB_TOKEN:
+ required: false
workflow_dispatch:
inputs:
aw_context:
@@ -60,21 +81,33 @@ on:
description: "Agent caller context (used internally by Agentic Workflows)."
required: false
type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
permissions: {}
concurrency:
- group: "gh-aw-${{ github.workflow }}"
+ cancel-in-progress: false
+ group: weekly-refresh-${{ inputs.week_key }}-foundry-toolkit
run-name: "Discover Foundry Toolkit"
jobs:
activation:
+ needs: pre_activation
+ if: needs.pre_activation.outputs.activated == 'true'
runs-on: ubuntu-slim
permissions:
actions: read
contents: read
outputs:
+ artifact_prefix: ${{ steps.artifact-prefix.outputs.prefix }}
comment_id: ""
comment_repo: ""
engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
@@ -84,6 +117,10 @@ jobs:
setup-span-id: ${{ steps.setup.outputs.span-id }}
setup-trace-id: ${{ steps.setup.outputs.trace-id }}
stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ target_checkout_ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
+ target_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}
+ target_repo: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ target_repo_name: ${{ steps.resolve-host-repo.outputs.target_repo_name }}
steps:
- name: Setup Scripts
id: setup
@@ -91,12 +128,34 @@ jobs:
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
+ trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }}
env:
GH_AW_SETUP_WORKFLOW_NAME: "Discover Foundry Toolkit"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-foundry-toolkit.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Resolve host repo for activation checkout
+ id: resolve-host-repo
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ JOB_WORKFLOW_REPOSITORY: ${{ job.workflow_repository }}
+ JOB_WORKFLOW_SHA: ${{ job.workflow_sha }}
+ JOB_WORKFLOW_REF: ${{ job.workflow_ref }}
+ JOB_WORKFLOW_FILE_PATH: ${{ job.workflow_file_path }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/resolve_host_repo.cjs');
+ await main();
+ - name: Compute artifact prefix
+ id: artifact-prefix
+ env:
+ INPUTS_JSON: ${{ toJSON(inputs) }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/compute_artifact_prefix.sh"
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -116,6 +175,7 @@ jobs:
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
+ GH_AW_INFO_TARGET_REPO: ${{ steps.resolve-host-repo.outputs.target_repo }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
@@ -123,10 +183,19 @@ jobs:
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
await main(core, context);
+ - name: Print cross-repo setup guidance
+ if: failure() && steps.resolve-host-repo.outputs.target_repo != github.repository
+ run: |
+ echo "::error::COPILOT_GITHUB_TOKEN must be configured in the CALLER repository's secrets."
+ echo "::error::For cross-repo workflow_call, secrets must be set in the repository that triggers the workflow."
+ echo "::error::See: https://github.github.com/gh-aw/patterns/central-repo-ops/#cross-repo-setup"
- name: Checkout .github and .agents folders
+ if: steps.resolve-host-repo.outputs.target_repo == github.repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
+ repository: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
sparse-checkout: |
.github
.agents
@@ -179,27 +248,26 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
# poutine:ignore untrusted_checkout_exec
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF'
+ cat << 'GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF'
- GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF
+ GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF'
+ cat << 'GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF'
- Tools: create_issue
- GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md"
- cat << 'GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF'
+ Tools: missing_tool, missing_data, noop, emit_weekly_report
- GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF
+ GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF'
+ cat << 'GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -228,21 +296,99 @@ jobs:
{{/if}}
- GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF
+ GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF'
+ cat << 'GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF'
+ Apply this staged weekly reporting contract for quest
+ `foundry-toolkit`:
+
+ - Quest identity is `4`,
+ `Microsoft Foundry Toolkit`, and
+ `quest/4-foundry-toolkit`.
+ - The reporting week is `__GH_AW_INPUTS_WEEK_KEY__` and the prior report locator is
+ `weekly-refresh:__GH_AW_INPUTS_PRIOR_WEEK_KEY__:foundry-toolkit`.
+ - Compare current allowed-source evidence, current Buildathon content, and any
+ prior finding keys available from the exact prior locator. Never broaden the
+ search to unrelated issues or repositories.
+ - Call `emit_weekly_report` exactly once, including blocked runs. The `result`
+ argument and `report_json.result` must match.
+ - Do not write a report file yourself. The deterministic safe-output job validates
+ the JSON and creates the run-scoped artifact.
+
+ The `report_json` value must encode exactly this version 1 shape:
+
+ ```json
+ {
+ "schemaVersion": 1,
+ "weekKey": "__GH_AW_INPUTS_WEEK_KEY__",
+ "priorWeekKey": "__GH_AW_INPUTS_PRIOR_WEEK_KEY__",
+ "questSlug": "foundry-toolkit",
+ "result": "material-change | no-material-change | blocked",
+ "executiveSummary": "short summary",
+ "currentQuestStatus": "current status",
+ "learnerImpact": "high | medium | low | none",
+ "officialSourceDeltas": [
+ {
+ "fact": "source fact",
+ "lifecycle": "GA | Public preview | Private preview | Not stated by allowed sources",
+ "evidenceUrl": "allowlisted URL",
+ "evidenceLocation": "heading or section",
+ "observedWording": "short factual wording",
+ "fingerprint": "source fingerprint"
+ }
+ ],
+ "contentDrift": [
+ {
+ "path": "owned Buildathon path",
+ "section": "section reference",
+ "observation": "repository observation"
+ }
+ ],
+ "proposedChanges": [
+ {
+ "repository": "Azure-Samples/JavaScript-AI-Buildathon",
+ "path": "owned Buildathon path",
+ "description": "bounded proposed change"
+ }
+ ],
+ "assetImpact": "asset impact or empty string",
+ "codeTourImpact": [
+ {
+ "repository": "exact external report-only repository",
+ "path": "exact observed CodeTour path",
+ "mode": "report-only",
+ "handoff": "manual-maintainer",
+ "observation": "bounded observation"
+ }
+ ],
+ "validationPlan": ["validation step"],
+ "risks": ["risk or uncertainty"],
+ "humanDecisions": ["decision required from Julia"],
+ "sourceFingerprints": ["source fingerprint"],
+ "priorFindingKeys": ["finding:<64 lowercase hex characters>"],
+ "blockedReason": null
+ }
+ ```
+
+ For `material-change`, include at least one proposed Buildathon change and use
+ `high`, `medium`, or `low` learner impact. For `no-material-change`, leave
+ `proposedChanges` empty. For `blocked`, set a concise `blockedReason`, leave
+ `proposedChanges` empty, and use `none` learner impact.
+
+
{{#runtime-import .github/workflows/shared/discovery-policy.md}}
{{#runtime-import .github/agents/foundry-toolkit-quest-master.agent.md}}
{{#runtime-import .github/workflows/discover-foundry-toolkit.md}}
- GH_AW_PROMPT_9fbb2bb06a9f0ab9_EOF
+ GH_AW_PROMPT_2fc5f82ad8c6f5f7_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_ENGINE_ID: "copilot"
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -261,7 +407,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -281,7 +430,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: process.env.GH_AW_INPUTS_PRIOR_WEEK_KEY,
+ GH_AW_INPUTS_WEEK_KEY: process.env.GH_AW_INPUTS_WEEK_KEY,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED
}
});
- name: Validate prompt placeholders
@@ -298,7 +450,7 @@ jobs:
if: success()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: activation
+ name: ${{ steps.artifact-prefix.outputs.prefix }}activation
include-hidden-files: true
path: |
/tmp/gh-aw/aw_info.json
@@ -328,6 +480,7 @@ jobs:
GH_AW_WORKFLOW_ID_SANITIZED: discoverfoundrytoolkit
outputs:
agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ artifact_prefix: ${{ needs.activation.outputs.artifact_prefix }}
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
@@ -356,6 +509,7 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Set runtime paths
id: set-runtime-paths
run: |
@@ -431,7 +585,7 @@ jobs:
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: activation
+ name: ${{ needs.activation.outputs.artifact_prefix }}activation
path: /tmp/gh-aw
- name: Restore agent config folders from base branch
if: steps.checkout-pr.outcome == 'success'
@@ -455,54 +609,117 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_ff9e3236ff02e08a_EOF'
- {"create_issue":{"labels":["discover-foundry-toolkit"],"max":1,"title_prefix":"[discover-foundry-toolkit]"}}
- GH_AW_SAFE_OUTPUTS_CONFIG_ff9e3236ff02e08a_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a240839364e73802_EOF'
+ {"create_report_incomplete_issue":{},"emit-weekly-report":{"description":"Materialize one validated staged weekly quest report artifact","inputs":{"report_json":{"default":null,"description":"A JSON string matching the weekly quest report contract","required":true,"type":"string"},"result":{"default":null,"description":"The exact discovery result","options":["material-change","no-material-change","blocked"],"required":true,"type":"choice"}},"output":"The structured weekly quest report artifact was prepared"},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_a240839364e73802_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
- "description_suffixes": {
- "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[discover-foundry-toolkit]\". Labels [\"discover-foundry-toolkit\"] will be automatically added."
- },
+ "description_suffixes": {},
"repo_params": {},
- "dynamic_tools": []
+ "dynamic_tools": [
+ {
+ "description": "Materialize one validated staged weekly quest report artifact",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "report_json": {
+ "description": "A JSON string matching the weekly quest report contract",
+ "type": "string"
+ },
+ "result": {
+ "description": "The exact discovery result",
+ "enum": [
+ "material-change",
+ "no-material-change",
+ "blocked"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "report_json",
+ "result"
+ ],
+ "type": "object"
+ },
+ "name": "emit_weekly_report"
+ }
+ ]
}
GH_AW_VALIDATION_JSON: |
{
- "create_issue": {
- "defaultMax": 1,
+ "missing_data": {
+ "defaultMax": 20,
"fields": {
- "body": {
- "required": true,
+ "alternatives": {
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 256
},
- "fields": {
- "type": "array"
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
},
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
},
- "parent": {
- "issueOrPRNumber": true
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
},
- "repo": {
+ "reason": {
+ "required": true,
"type": "string",
+ "sanitize": true,
"maxLength": 256
},
- "temporary_id": {
- "type": "string"
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
},
- "title": {
+ "reason": {
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 128
+ "maxLength": 1024
}
}
}
@@ -589,7 +806,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_a867f96cd03f5afe_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_836d949c0b7b6e0c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"safeoutputs": {
@@ -614,7 +831,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_a867f96cd03f5afe_EOF
+ GH_AW_MCP_CONFIG_836d949c0b7b6e0c_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -870,7 +1087,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/sandbox/agent/logs/
@@ -893,6 +1110,7 @@ jobs:
needs:
- activation
- agent
+ - emit_weekly_report
- safe_outputs
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
@@ -900,9 +1118,14 @@ jobs:
runs-on: ubuntu-slim
permissions: {}
concurrency:
- group: "gh-aw-conclusion-discover-foundry-toolkit"
+ group: "gh-aw-conclusion-discover-foundry-toolkit-${{ inputs.week_key }}"
cancel-in-progress: false
queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
steps:
- name: Setup Scripts
id: setup
@@ -918,12 +1141,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -932,6 +1156,54 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Discover Foundry Toolkit"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-foundry-toolkit.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Foundry Toolkit"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-foundry-toolkit.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Foundry Toolkit"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-foundry-toolkit.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
- name: Handle agent failure
id: handle_agent_failure
if: always()
@@ -970,6 +1242,86 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
await main();
+ emit_weekly_report:
+ needs:
+ - agent
+ - agent
+ if: >
+ (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'emit_weekly_report') &&
+ (always())
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - name: Download agent output artifact
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.agent.outputs.artifact_prefix }}agent
+ path: ${{ runner.temp }}/gh-aw/safe-jobs/
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Materialize structured quest report
+ run: |
+ node scripts/weekly-refresh-report.mjs materialize \
+ --agent-output "$GH_AW_AGENT_OUTPUT" \
+ --output "/tmp/weekly-refresh/${EXPECTED_QUEST_SLUG}.json" \
+ --week "$EXPECTED_WEEK_KEY" \
+ --prior-week "$EXPECTED_PRIOR_WEEK_KEY" \
+ --quest "$EXPECTED_QUEST_SLUG"
+ env:
+ EXPECTED_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ EXPECTED_QUEST_SLUG: foundry-toolkit
+ EXPECTED_WEEK_KEY: ${{ inputs.week_key }}
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Upload structured quest report
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ with:
+ if-no-files-found: error
+ name: weekly-refresh-${{ inputs.week_key }}-foundry-toolkit
+ path: /tmp/weekly-refresh/foundry-toolkit.json
+ retention-days: 30.0
+
+ pre_activation:
+ runs-on: ubuntu-slim
+ outputs:
+ activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }}
+ matched_command: ''
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Discover Foundry Toolkit"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-foundry-toolkit.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Check team membership for workflow
+ id: check_membership
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_REQUIRED_ROLES: "admin,maintainer,write"
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
+ await main();
+
safe_outputs:
needs:
- activation
@@ -994,8 +1346,6 @@ jobs:
code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
@@ -1013,12 +1363,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -1045,7 +1396,8 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,code.visualstudio.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,marketplace.visualstudio.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"discover-foundry-toolkit\"],\"max\":1,\"title_prefix\":\"[discover-foundry-toolkit]\"}}"
+ GH_AW_SAFE_OUTPUT_JOBS: "{\"emit_weekly_report\":\"\"}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_STAGED: "true"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/discover-foundry-toolkit.md b/.github/workflows/discover-foundry-toolkit.md
index 1b54baf..3e06886 100644
--- a/.github/workflows/discover-foundry-toolkit.md
+++ b/.github/workflows/discover-foundry-toolkit.md
@@ -2,10 +2,33 @@
name: Discover Foundry Toolkit
description: Manually audits the Microsoft Foundry Toolkit quest using read-only approved evidence
on:
+ workflow_call:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
workflow_dispatch:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
permissions:
contents: read
copilot-requests: write
+concurrency:
+ group: weekly-refresh-${{ inputs.week_key }}-foundry-toolkit
+ cancel-in-progress: false
+ job-discriminator: ${{ inputs.week_key }}
strict: true
engine:
id: copilot
@@ -16,6 +39,12 @@ network:
- marketplace.visualstudio.com
imports:
- shared/discovery-policy.md
+ - uses: shared/report-output.md
+ with:
+ quest-id: 4
+ quest-slug: foundry-toolkit
+ quest-title: Microsoft Foundry Toolkit
+ quest-label: quest/4-foundry-toolkit
- ../agents/foundry-toolkit-quest-master.agent.md
safe-outputs:
activation-comments: false
@@ -29,9 +58,9 @@ safe-outputs:
timeout-minutes: 20
---
-# Phase 1 Foundry Toolkit Discovery
+# Phase 2A Foundry Toolkit Reporting
-Run a read-only manual trial for quest slug `foundry-toolkit`.
+Run a read-only staged report for quest slug `foundry-toolkit`.
## Execution
@@ -43,15 +72,19 @@ Run a read-only manual trial for quest slug `foundry-toolkit`.
with exact file and section references.
5. Reconcile both reports. Reject unsupported claims and make uncertainty
explicit.
-6. Return one concise GitHub-flavored Markdown report. Do not create or update
- any persistent resource.
+6. Compare against any exact prior finding keys supplied for
+ `${{ inputs.prior_week_key }}`.
+7. Call `emit_weekly_report` exactly once with the structured report.
+8. Return one concise GitHub-flavored Markdown summary. Do not create or update
+ repository content or GitHub issues, comments, branches, or pull requests.
## Final report contract
Start nested headings at `###` and include:
- `Result: material-change | no-material-change | blocked`
-- Trial key `phase1-${{ github.run_id }}` and quest identity
+- Week key `${{ inputs.week_key }}`, prior week `${{ inputs.prior_week_key }}`,
+ and quest identity
- Executive summary and current quest status
- Official-source facts and deltas with lifecycle, URL, location, observed
wording, and fingerprint
diff --git a/.github/workflows/discover-microsoft-foundry.lock.yml b/.github/workflows/discover-microsoft-foundry.lock.yml
index 488edcf..e1b22a5 100644
--- a/.github/workflows/discover-microsoft-foundry.lock.yml
+++ b/.github/workflows/discover-microsoft-foundry.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"9949cf859a994fad158037899da1fc71ddb04750a1a8cbc0a818659949834098","body_hash":"3198de2221e180925b129810b950355cffc9bf96294a6e71913c23bbe7d0bdff","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"908293d6c58ff680f62c316c5e46a2ece61960dfbab18446ef00ab9a791606a5","body_hash":"789d17f437d800703e837a41bb5d69ce26157e2eab55f7151de1c009ebfe7e9d","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
# ___ _ _
# / _ \ | | (_)
@@ -28,6 +28,7 @@
# Imports:
# - ../agents/microsoft-foundry-quest-master.agent.md
# - shared/discovery-policy.md
+# - shared/report-output.md
#
# Secrets used:
# - GH_AW_GITHUB_MCP_SERVER_TOKEN
@@ -53,6 +54,26 @@
name: "Discover Microsoft Foundry"
on:
+ workflow_call:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ secrets:
+ GH_AW_GITHUB_MCP_SERVER_TOKEN:
+ required: false
+ GH_AW_GITHUB_TOKEN:
+ required: false
workflow_dispatch:
inputs:
aw_context:
@@ -60,21 +81,33 @@ on:
description: "Agent caller context (used internally by Agentic Workflows)."
required: false
type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
permissions: {}
concurrency:
- group: "gh-aw-${{ github.workflow }}"
+ cancel-in-progress: false
+ group: weekly-refresh-${{ inputs.week_key }}-microsoft-foundry
run-name: "Discover Microsoft Foundry"
jobs:
activation:
+ needs: pre_activation
+ if: needs.pre_activation.outputs.activated == 'true'
runs-on: ubuntu-slim
permissions:
actions: read
contents: read
outputs:
+ artifact_prefix: ${{ steps.artifact-prefix.outputs.prefix }}
comment_id: ""
comment_repo: ""
engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
@@ -84,6 +117,10 @@ jobs:
setup-span-id: ${{ steps.setup.outputs.span-id }}
setup-trace-id: ${{ steps.setup.outputs.trace-id }}
stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ target_checkout_ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
+ target_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}
+ target_repo: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ target_repo_name: ${{ steps.resolve-host-repo.outputs.target_repo_name }}
steps:
- name: Setup Scripts
id: setup
@@ -91,12 +128,34 @@ jobs:
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
+ trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }}
env:
GH_AW_SETUP_WORKFLOW_NAME: "Discover Microsoft Foundry"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-microsoft-foundry.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Resolve host repo for activation checkout
+ id: resolve-host-repo
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ JOB_WORKFLOW_REPOSITORY: ${{ job.workflow_repository }}
+ JOB_WORKFLOW_SHA: ${{ job.workflow_sha }}
+ JOB_WORKFLOW_REF: ${{ job.workflow_ref }}
+ JOB_WORKFLOW_FILE_PATH: ${{ job.workflow_file_path }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/resolve_host_repo.cjs');
+ await main();
+ - name: Compute artifact prefix
+ id: artifact-prefix
+ env:
+ INPUTS_JSON: ${{ toJSON(inputs) }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/compute_artifact_prefix.sh"
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -116,6 +175,7 @@ jobs:
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
+ GH_AW_INFO_TARGET_REPO: ${{ steps.resolve-host-repo.outputs.target_repo }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
@@ -123,10 +183,19 @@ jobs:
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
await main(core, context);
+ - name: Print cross-repo setup guidance
+ if: failure() && steps.resolve-host-repo.outputs.target_repo != github.repository
+ run: |
+ echo "::error::COPILOT_GITHUB_TOKEN must be configured in the CALLER repository's secrets."
+ echo "::error::For cross-repo workflow_call, secrets must be set in the repository that triggers the workflow."
+ echo "::error::See: https://github.github.com/gh-aw/patterns/central-repo-ops/#cross-repo-setup"
- name: Checkout .github and .agents folders
+ if: steps.resolve-host-repo.outputs.target_repo == github.repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
+ repository: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
sparse-checkout: |
.github
.agents
@@ -179,27 +248,26 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
# poutine:ignore untrusted_checkout_exec
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_5732786544e69446_EOF'
+ cat << 'GH_AW_PROMPT_bc6e89830fd3236a_EOF'
- GH_AW_PROMPT_5732786544e69446_EOF
+ GH_AW_PROMPT_bc6e89830fd3236a_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_5732786544e69446_EOF'
+ cat << 'GH_AW_PROMPT_bc6e89830fd3236a_EOF'
- Tools: create_issue
- GH_AW_PROMPT_5732786544e69446_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md"
- cat << 'GH_AW_PROMPT_5732786544e69446_EOF'
+ Tools: missing_tool, missing_data, noop, emit_weekly_report
- GH_AW_PROMPT_5732786544e69446_EOF
+ GH_AW_PROMPT_bc6e89830fd3236a_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_5732786544e69446_EOF'
+ cat << 'GH_AW_PROMPT_bc6e89830fd3236a_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -228,21 +296,99 @@ jobs:
{{/if}}
- GH_AW_PROMPT_5732786544e69446_EOF
+ GH_AW_PROMPT_bc6e89830fd3236a_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_5732786544e69446_EOF'
+ cat << 'GH_AW_PROMPT_bc6e89830fd3236a_EOF'
+ Apply this staged weekly reporting contract for quest
+ `microsoft-foundry`:
+
+ - Quest identity is `2`,
+ `Microsoft Foundry`, and
+ `quest/2-microsoft-foundry`.
+ - The reporting week is `__GH_AW_INPUTS_WEEK_KEY__` and the prior report locator is
+ `weekly-refresh:__GH_AW_INPUTS_PRIOR_WEEK_KEY__:microsoft-foundry`.
+ - Compare current allowed-source evidence, current Buildathon content, and any
+ prior finding keys available from the exact prior locator. Never broaden the
+ search to unrelated issues or repositories.
+ - Call `emit_weekly_report` exactly once, including blocked runs. The `result`
+ argument and `report_json.result` must match.
+ - Do not write a report file yourself. The deterministic safe-output job validates
+ the JSON and creates the run-scoped artifact.
+
+ The `report_json` value must encode exactly this version 1 shape:
+
+ ```json
+ {
+ "schemaVersion": 1,
+ "weekKey": "__GH_AW_INPUTS_WEEK_KEY__",
+ "priorWeekKey": "__GH_AW_INPUTS_PRIOR_WEEK_KEY__",
+ "questSlug": "microsoft-foundry",
+ "result": "material-change | no-material-change | blocked",
+ "executiveSummary": "short summary",
+ "currentQuestStatus": "current status",
+ "learnerImpact": "high | medium | low | none",
+ "officialSourceDeltas": [
+ {
+ "fact": "source fact",
+ "lifecycle": "GA | Public preview | Private preview | Not stated by allowed sources",
+ "evidenceUrl": "allowlisted URL",
+ "evidenceLocation": "heading or section",
+ "observedWording": "short factual wording",
+ "fingerprint": "source fingerprint"
+ }
+ ],
+ "contentDrift": [
+ {
+ "path": "owned Buildathon path",
+ "section": "section reference",
+ "observation": "repository observation"
+ }
+ ],
+ "proposedChanges": [
+ {
+ "repository": "Azure-Samples/JavaScript-AI-Buildathon",
+ "path": "owned Buildathon path",
+ "description": "bounded proposed change"
+ }
+ ],
+ "assetImpact": "asset impact or empty string",
+ "codeTourImpact": [
+ {
+ "repository": "exact external report-only repository",
+ "path": "exact observed CodeTour path",
+ "mode": "report-only",
+ "handoff": "manual-maintainer",
+ "observation": "bounded observation"
+ }
+ ],
+ "validationPlan": ["validation step"],
+ "risks": ["risk or uncertainty"],
+ "humanDecisions": ["decision required from Julia"],
+ "sourceFingerprints": ["source fingerprint"],
+ "priorFindingKeys": ["finding:<64 lowercase hex characters>"],
+ "blockedReason": null
+ }
+ ```
+
+ For `material-change`, include at least one proposed Buildathon change and use
+ `high`, `medium`, or `low` learner impact. For `no-material-change`, leave
+ `proposedChanges` empty. For `blocked`, set a concise `blockedReason`, leave
+ `proposedChanges` empty, and use `none` learner impact.
+
+
{{#runtime-import .github/workflows/shared/discovery-policy.md}}
{{#runtime-import .github/agents/microsoft-foundry-quest-master.agent.md}}
{{#runtime-import .github/workflows/discover-microsoft-foundry.md}}
- GH_AW_PROMPT_5732786544e69446_EOF
+ GH_AW_PROMPT_bc6e89830fd3236a_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_ENGINE_ID: "copilot"
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -261,7 +407,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@@ -281,7 +430,10 @@ jobs:
GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: process.env.GH_AW_INPUTS_PRIOR_WEEK_KEY,
+ GH_AW_INPUTS_WEEK_KEY: process.env.GH_AW_INPUTS_WEEK_KEY,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED
}
});
- name: Validate prompt placeholders
@@ -298,7 +450,7 @@ jobs:
if: success()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: activation
+ name: ${{ steps.artifact-prefix.outputs.prefix }}activation
include-hidden-files: true
path: |
/tmp/gh-aw/aw_info.json
@@ -328,6 +480,7 @@ jobs:
GH_AW_WORKFLOW_ID_SANITIZED: discovermicrosoftfoundry
outputs:
agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ artifact_prefix: ${{ needs.activation.outputs.artifact_prefix }}
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
@@ -356,6 +509,7 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Set runtime paths
id: set-runtime-paths
run: |
@@ -431,7 +585,7 @@ jobs:
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: activation
+ name: ${{ needs.activation.outputs.artifact_prefix }}activation
path: /tmp/gh-aw
- name: Restore agent config folders from base branch
if: steps.checkout-pr.outcome == 'success'
@@ -455,54 +609,117 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_dfce5e0f0e76aafb_EOF'
- {"create_issue":{"labels":["discover-microsoft-foundry"],"max":1,"title_prefix":"[discover-microsoft-foundry]"}}
- GH_AW_SAFE_OUTPUTS_CONFIG_dfce5e0f0e76aafb_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_d711b77a2c3aa8e8_EOF'
+ {"create_report_incomplete_issue":{},"emit-weekly-report":{"description":"Materialize one validated staged weekly quest report artifact","inputs":{"report_json":{"default":null,"description":"A JSON string matching the weekly quest report contract","required":true,"type":"string"},"result":{"default":null,"description":"The exact discovery result","options":["material-change","no-material-change","blocked"],"required":true,"type":"choice"}},"output":"The structured weekly quest report artifact was prepared"},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_d711b77a2c3aa8e8_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
- "description_suffixes": {
- "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[discover-microsoft-foundry]\". Labels [\"discover-microsoft-foundry\"] will be automatically added."
- },
+ "description_suffixes": {},
"repo_params": {},
- "dynamic_tools": []
+ "dynamic_tools": [
+ {
+ "description": "Materialize one validated staged weekly quest report artifact",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "report_json": {
+ "description": "A JSON string matching the weekly quest report contract",
+ "type": "string"
+ },
+ "result": {
+ "description": "The exact discovery result",
+ "enum": [
+ "material-change",
+ "no-material-change",
+ "blocked"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "report_json",
+ "result"
+ ],
+ "type": "object"
+ },
+ "name": "emit_weekly_report"
+ }
+ ]
}
GH_AW_VALIDATION_JSON: |
{
- "create_issue": {
- "defaultMax": 1,
+ "missing_data": {
+ "defaultMax": 20,
"fields": {
- "body": {
- "required": true,
+ "alternatives": {
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 256
},
- "fields": {
- "type": "array"
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
},
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
},
- "parent": {
- "issueOrPRNumber": true
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
},
- "repo": {
+ "reason": {
+ "required": true,
"type": "string",
+ "sanitize": true,
"maxLength": 256
},
- "temporary_id": {
- "type": "string"
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
},
- "title": {
+ "reason": {
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 128
+ "maxLength": 1024
}
}
}
@@ -589,7 +806,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_df5ea76d7fc4c8d3_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_33f7c5d06774b0ca_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"safeoutputs": {
@@ -614,7 +831,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_df5ea76d7fc4c8d3_EOF
+ GH_AW_MCP_CONFIG_33f7c5d06774b0ca_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -870,7 +1087,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/sandbox/agent/logs/
@@ -893,6 +1110,7 @@ jobs:
needs:
- activation
- agent
+ - emit_weekly_report
- safe_outputs
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
@@ -900,9 +1118,14 @@ jobs:
runs-on: ubuntu-slim
permissions: {}
concurrency:
- group: "gh-aw-conclusion-discover-microsoft-foundry"
+ group: "gh-aw-conclusion-discover-microsoft-foundry-${{ inputs.week_key }}"
cancel-in-progress: false
queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
steps:
- name: Setup Scripts
id: setup
@@ -918,12 +1141,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -932,6 +1156,54 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Discover Microsoft Foundry"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-microsoft-foundry.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Microsoft Foundry"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-microsoft-foundry.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Discover Microsoft Foundry"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/discover-microsoft-foundry.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
- name: Handle agent failure
id: handle_agent_failure
if: always()
@@ -970,6 +1242,86 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
await main();
+ emit_weekly_report:
+ needs:
+ - agent
+ - agent
+ if: >
+ (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'emit_weekly_report') &&
+ (always())
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - name: Download agent output artifact
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.agent.outputs.artifact_prefix }}agent
+ path: ${{ runner.temp }}/gh-aw/safe-jobs/
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Materialize structured quest report
+ run: |
+ node scripts/weekly-refresh-report.mjs materialize \
+ --agent-output "$GH_AW_AGENT_OUTPUT" \
+ --output "/tmp/weekly-refresh/${EXPECTED_QUEST_SLUG}.json" \
+ --week "$EXPECTED_WEEK_KEY" \
+ --prior-week "$EXPECTED_PRIOR_WEEK_KEY" \
+ --quest "$EXPECTED_QUEST_SLUG"
+ env:
+ EXPECTED_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ EXPECTED_QUEST_SLUG: microsoft-foundry
+ EXPECTED_WEEK_KEY: ${{ inputs.week_key }}
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ - name: Upload structured quest report
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json
+ with:
+ if-no-files-found: error
+ name: weekly-refresh-${{ inputs.week_key }}-microsoft-foundry
+ path: /tmp/weekly-refresh/microsoft-foundry.json
+ retention-days: 30.0
+
+ pre_activation:
+ runs-on: ubuntu-slim
+ outputs:
+ activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }}
+ matched_command: ''
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Discover Microsoft Foundry"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/discover-microsoft-foundry.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Check team membership for workflow
+ id: check_membership
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_REQUIRED_ROLES: "admin,maintainer,write"
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
+ await main();
+
safe_outputs:
needs:
- activation
@@ -994,8 +1346,6 @@ jobs:
code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
@@ -1013,12 +1363,13 @@ jobs:
GH_AW_INFO_VERSION: "1.0.55"
GH_AW_INFO_AWF_VERSION: "v0.25.58"
GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
path: /tmp/gh-aw/
- name: Setup agent output environment variable
id: setup-agent-output-env
@@ -1045,7 +1396,8 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,learn.microsoft.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"discover-microsoft-foundry\"],\"max\":1,\"title_prefix\":\"[discover-microsoft-foundry]\"}}"
+ GH_AW_SAFE_OUTPUT_JOBS: "{\"emit_weekly_report\":\"\"}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_STAGED: "true"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/discover-microsoft-foundry.md b/.github/workflows/discover-microsoft-foundry.md
index 0c3c34d..aa380c9 100644
--- a/.github/workflows/discover-microsoft-foundry.md
+++ b/.github/workflows/discover-microsoft-foundry.md
@@ -2,10 +2,33 @@
name: Discover Microsoft Foundry
description: Manually audits the Microsoft Foundry quest using read-only approved evidence
on:
+ workflow_call:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
workflow_dispatch:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
permissions:
contents: read
copilot-requests: write
+concurrency:
+ group: weekly-refresh-${{ inputs.week_key }}-microsoft-foundry
+ cancel-in-progress: false
+ job-discriminator: ${{ inputs.week_key }}
strict: true
engine:
id: copilot
@@ -15,6 +38,12 @@ network:
- learn.microsoft.com
imports:
- shared/discovery-policy.md
+ - uses: shared/report-output.md
+ with:
+ quest-id: 2
+ quest-slug: microsoft-foundry
+ quest-title: Microsoft Foundry
+ quest-label: quest/2-microsoft-foundry
- ../agents/microsoft-foundry-quest-master.agent.md
safe-outputs:
activation-comments: false
@@ -28,9 +57,9 @@ safe-outputs:
timeout-minutes: 20
---
-# Phase 1 Microsoft Foundry Discovery
+# Phase 2A Microsoft Foundry Reporting
-Run a read-only manual trial for quest slug `microsoft-foundry`.
+Run a read-only staged report for quest slug `microsoft-foundry`.
## Execution
@@ -42,15 +71,19 @@ Run a read-only manual trial for quest slug `microsoft-foundry`.
file and section references.
5. Reconcile both reports. Reject unsupported claims and make uncertainty
explicit.
-6. Return one concise GitHub-flavored Markdown report. Do not create or update
- any persistent resource.
+6. Compare against any exact prior finding keys supplied for
+ `${{ inputs.prior_week_key }}`.
+7. Call `emit_weekly_report` exactly once with the structured report.
+8. Return one concise GitHub-flavored Markdown summary. Do not create or update
+ repository content or GitHub issues, comments, branches, or pull requests.
## Final report contract
Start nested headings at `###` and include:
- `Result: material-change | no-material-change | blocked`
-- Trial key `phase1-${{ github.run_id }}` and quest identity
+- Week key `${{ inputs.week_key }}`, prior week `${{ inputs.prior_week_key }}`,
+ and quest identity
- Executive summary and current quest status
- Official-source facts and deltas with lifecycle, URL, location, observed
wording, and fingerprint
diff --git a/.github/workflows/shared/discovery-policy.md b/.github/workflows/shared/discovery-policy.md
index afe1bd6..201d2f6 100644
--- a/.github/workflows/shared/discovery-policy.md
+++ b/.github/workflows/shared/discovery-policy.md
@@ -35,10 +35,11 @@ Apply these Phase 1 discovery rules:
independently listed in the matching quest entry.
- Use GitHub transport domains only for repositories listed in the matching
quest entry. Cite the human-facing GitHub URL, not a transport URL.
-- Return the report in the final agent response. No safe-output handler is
- configured; automatic comments, missing-data issues, missing-tool issues,
- incomplete-run issues, and failure issues are disabled. The workflow-level
- staged guard must remain enabled.
+- Return a concise summary in the final agent response and use only the imported
+ `emit_weekly_report` safe-output job for the structured run artifact.
+ Automatic comments, missing-data issues, missing-tool issues, incomplete-run
+ issues, and failure issues are disabled. The workflow-level staged guard must
+ remain enabled.
- When no material drift is found, return `Result: no-material-change` with the
inspected evidence and any coverage gaps.
- When a boundary, citation, lifecycle, or source conflict cannot be resolved,
diff --git a/.github/workflows/shared/report-output.md b/.github/workflows/shared/report-output.md
new file mode 100644
index 0000000..905d505
--- /dev/null
+++ b/.github/workflows/shared/report-output.md
@@ -0,0 +1,137 @@
+---
+import-schema:
+ quest-id:
+ type: number
+ required: true
+ quest-slug:
+ type: string
+ required: true
+ quest-title:
+ type: string
+ required: true
+ quest-label:
+ type: string
+ required: true
+safe-outputs:
+ jobs:
+ emit-weekly-report:
+ description: Materialize one validated staged weekly quest report artifact
+ runs-on: ubuntu-latest
+ needs: agent
+ if: always()
+ output: The structured weekly quest report artifact was prepared
+ permissions:
+ contents: read
+ inputs:
+ result:
+ description: The exact discovery result
+ required: true
+ type: choice
+ options:
+ - material-change
+ - no-material-change
+ - blocked
+ report_json:
+ description: A JSON string matching the weekly quest report contract
+ required: true
+ type: string
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ - name: Materialize structured quest report
+ env:
+ EXPECTED_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ EXPECTED_QUEST_SLUG: ${{ github.aw.import-inputs.quest-slug }}
+ EXPECTED_WEEK_KEY: ${{ inputs.week_key }}
+ run: |
+ node scripts/weekly-refresh-report.mjs materialize \
+ --agent-output "$GH_AW_AGENT_OUTPUT" \
+ --output "/tmp/weekly-refresh/${EXPECTED_QUEST_SLUG}.json" \
+ --week "$EXPECTED_WEEK_KEY" \
+ --prior-week "$EXPECTED_PRIOR_WEEK_KEY" \
+ --quest "$EXPECTED_QUEST_SLUG"
+ - name: Upload structured quest report
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: weekly-refresh-${{ inputs.week_key }}-${{ github.aw.import-inputs.quest-slug }}
+ path: /tmp/weekly-refresh/${{ github.aw.import-inputs.quest-slug }}.json
+ if-no-files-found: error
+ retention-days: 30
+---
+
+Apply this staged weekly reporting contract for quest
+`${{ github.aw.import-inputs.quest-slug }}`:
+
+- Quest identity is `${{ github.aw.import-inputs.quest-id }}`,
+ `${{ github.aw.import-inputs.quest-title }}`, and
+ `${{ github.aw.import-inputs.quest-label }}`.
+- The reporting week is `${{ inputs.week_key }}` and the prior report locator is
+ `weekly-refresh:${{ inputs.prior_week_key }}:${{ github.aw.import-inputs.quest-slug }}`.
+- Compare current allowed-source evidence, current Buildathon content, and any
+ prior finding keys available from the exact prior locator. Never broaden the
+ search to unrelated issues or repositories.
+- Call `emit_weekly_report` exactly once, including blocked runs. The `result`
+ argument and `report_json.result` must match.
+- Do not write a report file yourself. The deterministic safe-output job validates
+ the JSON and creates the run-scoped artifact.
+
+The `report_json` value must encode exactly this version 1 shape:
+
+```json
+{
+ "schemaVersion": 1,
+ "weekKey": "${{ inputs.week_key }}",
+ "priorWeekKey": "${{ inputs.prior_week_key }}",
+ "questSlug": "${{ github.aw.import-inputs.quest-slug }}",
+ "result": "material-change | no-material-change | blocked",
+ "executiveSummary": "short summary",
+ "currentQuestStatus": "current status",
+ "learnerImpact": "high | medium | low | none",
+ "officialSourceDeltas": [
+ {
+ "fact": "source fact",
+ "lifecycle": "GA | Public preview | Private preview | Not stated by allowed sources",
+ "evidenceUrl": "allowlisted URL",
+ "evidenceLocation": "heading or section",
+ "observedWording": "short factual wording",
+ "fingerprint": "source fingerprint"
+ }
+ ],
+ "contentDrift": [
+ {
+ "path": "owned Buildathon path",
+ "section": "section reference",
+ "observation": "repository observation"
+ }
+ ],
+ "proposedChanges": [
+ {
+ "repository": "Azure-Samples/JavaScript-AI-Buildathon",
+ "path": "owned Buildathon path",
+ "description": "bounded proposed change"
+ }
+ ],
+ "assetImpact": "asset impact or empty string",
+ "codeTourImpact": [
+ {
+ "repository": "exact external report-only repository",
+ "path": "exact observed CodeTour path",
+ "mode": "report-only",
+ "handoff": "manual-maintainer",
+ "observation": "bounded observation"
+ }
+ ],
+ "validationPlan": ["validation step"],
+ "risks": ["risk or uncertainty"],
+ "humanDecisions": ["decision required from Julia"],
+ "sourceFingerprints": ["source fingerprint"],
+ "priorFindingKeys": ["finding:<64 lowercase hex characters>"],
+ "blockedReason": null
+}
+```
+
+For `material-change`, include at least one proposed Buildathon change and use
+`high`, `medium`, or `low` learner impact. For `no-material-change`, leave
+`proposedChanges` empty. For `blocked`, set a concise `blockedReason`, leave
+`proposedChanges` empty, and use `none` learner impact.
diff --git a/.github/workflows/validate-agentic-workflows.yml b/.github/workflows/validate-agentic-workflows.yml
index 0a11044..f6ccc65 100644
--- a/.github/workflows/validate-agentic-workflows.yml
+++ b/.github/workflows/validate-agentic-workflows.yml
@@ -12,7 +12,7 @@ on:
- ".github/mcp.json"
- ".github/skills/**"
- ".github/workflows/**"
- - "scripts/validate-refresh-config.mjs"
+ - "scripts/**"
workflow_dispatch:
permissions:
diff --git a/.github/workflows/validate-content.yml b/.github/workflows/validate-content.yml
index 6cd7bf9..b1eb077 100644
--- a/.github/workflows/validate-content.yml
+++ b/.github/workflows/validate-content.yml
@@ -11,8 +11,7 @@ on:
- "docs/**"
- "mkdocs.yml"
- "requirements.txt"
- - "scripts/validate-content-links.mjs"
- - "scripts/validate-refresh-config.mjs"
+ - "scripts/**"
- ".github/workflows/validate-content.yml"
workflow_dispatch:
@@ -35,6 +34,9 @@ jobs:
- name: Validate refresh configuration
run: node scripts/validate-refresh-config.mjs
+ - name: Test weekly refresh reporting
+ run: node scripts/weekly-refresh-report.mjs self-test
+
- name: Validate local content links
run: node scripts/validate-content-links.mjs
diff --git a/.github/workflows/weekly-refresh-digest.lock.yml b/.github/workflows/weekly-refresh-digest.lock.yml
new file mode 100644
index 0000000..d7644d1
--- /dev/null
+++ b/.github/workflows/weekly-refresh-digest.lock.yml
@@ -0,0 +1,1174 @@
+# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5caa1f9e7525766cb092f962fd6af5f2a54eeabfb70299bb2aaa86b41d4dfcd3","body_hash":"bf8dbc7572167bc9b6249e36fe6df0f6e0cd4afab7d820cc4fecdf1fdce33e7c","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
+# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.77.5). DO NOT EDIT.
+#
+# To update this file, edit the corresponding .md file and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Deterministically assembles staged quest reports into parent and child issue previews
+#
+# Secrets used:
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.58
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.58
+# - ghcr.io/github/gh-aw-mcpg:v0.3.22
+# - ghcr.io/github/github-mcp-server:v1.1.0
+# - node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+
+name: "Build Weekly Refresh Digest Preview"
+on:
+ workflow_call:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ outputs:
+ created_issue_number:
+ description: Number of the first created issue
+ value: ${{ jobs.safe_outputs.outputs.created_issue_number }}
+ created_issue_url:
+ description: URL of the first created issue
+ value: ${{ jobs.safe_outputs.outputs.created_issue_url }}
+ secrets:
+ GH_AW_GITHUB_MCP_SERVER_TOKEN:
+ required: false
+ GH_AW_GITHUB_TOKEN:
+ required: false
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "Build Weekly Refresh Digest Preview"
+
+jobs:
+ activation:
+ needs: pre_activation
+ if: needs.pre_activation.outputs.activated == 'true'
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ artifact_prefix: ${{ steps.artifact-prefix.outputs.prefix }}
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ target_checkout_ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
+ target_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}
+ target_repo: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ target_repo_name: ${{ steps.resolve-host-repo.outputs.target_repo_name }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/weekly-refresh-digest.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Resolve host repo for activation checkout
+ id: resolve-host-repo
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ JOB_WORKFLOW_REPOSITORY: ${{ job.workflow_repository }}
+ JOB_WORKFLOW_SHA: ${{ job.workflow_sha }}
+ JOB_WORKFLOW_REF: ${{ job.workflow_ref }}
+ JOB_WORKFLOW_FILE_PATH: ${{ job.workflow_file_path }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/resolve_host_repo.cjs');
+ await main();
+ - name: Compute artifact prefix
+ id: artifact-prefix
+ env:
+ INPUTS_JSON: ${{ toJSON(inputs) }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/compute_artifact_prefix.sh"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AGENT_VERSION: "1.0.55"
+ GH_AW_INFO_CLI_VERSION: "v0.77.5"
+ GH_AW_INFO_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "true"
+ GH_AW_INFO_ALLOWED_DOMAINS: '[]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_COMPILED_STRICT: "true"
+ GH_AW_INFO_TARGET_REPO: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Print cross-repo setup guidance
+ if: failure() && steps.resolve-host-repo.outputs.target_repo != github.repository
+ run: |
+ echo "::error::COPILOT_GITHUB_TOKEN must be configured in the CALLER repository's secrets."
+ echo "::error::For cross-repo workflow_call, secrets must be set in the repository that triggers the workflow."
+ echo "::error::See: https://github.github.com/gh-aw/patterns/central-repo-ops/#cross-repo-setup"
+ - name: Checkout .github and .agents folders
+ if: steps.resolve-host-repo.outputs.target_repo == github.repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ repository: ${{ steps.resolve-host-repo.outputs.target_repo }}
+ ref: ${{ steps.resolve-host-repo.outputs.target_checkout_ref }}
+ sparse-checkout: |
+ .github
+ .agents
+ .antigravity
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "weekly-refresh-digest.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.77.5"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_08c8bbacd97d5fa0_EOF'
+
+ GH_AW_PROMPT_08c8bbacd97d5fa0_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
+ cat << 'GH_AW_PROMPT_08c8bbacd97d5fa0_EOF'
+
+ Tools: create_issue
+ GH_AW_PROMPT_08c8bbacd97d5fa0_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md"
+ cat << 'GH_AW_PROMPT_08c8bbacd97d5fa0_EOF'
+
+ GH_AW_PROMPT_08c8bbacd97d5fa0_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_08c8bbacd97d5fa0_EOF'
+
+ The following GitHub context information is available for this workflow:
+ {{#if github.actor}}
+ - **actor**: __GH_AW_GITHUB_ACTOR__
+ {{/if}}
+ {{#if github.repository}}
+ - **repository**: __GH_AW_GITHUB_REPOSITORY__
+ {{/if}}
+ {{#if github.workspace}}
+ - **workspace**: __GH_AW_GITHUB_WORKSPACE__
+ {{/if}}
+ {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
+ - **issue-number**: #__GH_AW_EXPR_802A9F6A__
+ {{/if}}
+ {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
+ - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
+ {{/if}}
+ {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
+ - **pull-request-number**: #__GH_AW_EXPR_463A214A__
+ {{/if}}
+ {{#if github.event.comment.id || github.aw.context.comment_id}}
+ - **comment-id**: __GH_AW_EXPR_FF1D34CE__
+ {{/if}}
+ {{#if github.run_id}}
+ - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
+ {{/if}}
+
+
+ GH_AW_PROMPT_08c8bbacd97d5fa0_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_08c8bbacd97d5fa0_EOF'
+
+ {{#runtime-import .github/workflows/weekly-refresh-digest.md}}
+ GH_AW_PROMPT_08c8bbacd97d5fa0_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
+ GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
+ GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
+ GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
+ GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
+ GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
+ GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_INPUTS_WEEK_KEY: process.env.GH_AW_INPUTS_WEEK_KEY,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: ${{ steps.artifact-prefix.outputs.prefix }}activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/model_multipliers.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/aw-prompts/prompt-template.txt
+ /tmp/gh-aw/aw-prompts/prompt-import-tree.json
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ /tmp/gh-aw/.github/agents
+ /tmp/gh-aw/.github/skills
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ copilot-requests: write
+ concurrency:
+ group: "gh-aw-copilot-weekly-refresh-digest"
+ queue: max
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: weeklyrefreshdigest
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ artifact_prefix: ${{ needs.activation.outputs.artifact_prefix }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/weekly-refresh-digest.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - name: Create gh-aw temp directory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
+ - name: Configure gh CLI for GitHub Enterprise
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
+ env:
+ GH_TOKEN: ${{ github.token }}
+ - continue-on-error: true
+ name: Download staged quest reports
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
+ with:
+ merge-multiple: true
+ path: /tmp/gh-aw/agent/weekly-refresh-reports
+ pattern: weekly-refresh-${{ inputs.week_key }}-*
+ - env:
+ GH_AW_INPUTS_PRIOR_WEEK_KEY: ${{ inputs.prior_week_key }}
+ GH_AW_INPUTS_WEEK_KEY: ${{ inputs.week_key }}
+ name: Build deterministic digest preview
+ run: |
+ node scripts/weekly-refresh-report.mjs digest \
+ --reports-dir /tmp/gh-aw/agent/weekly-refresh-reports \
+ --output-dir /tmp/gh-aw/agent/weekly-refresh-preview \
+ --week "$GH_AW_INPUTS_WEEK_KEY" \
+ --prior-week "$GH_AW_INPUTS_PRIOR_WEEK_KEY"
+ - name: Upload staged digest preview
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
+ with:
+ if-no-files-found: error
+ name: weekly-refresh-${{ inputs.week_key }}-preview
+ path: /tmp/gh-aw/agent/weekly-refresh-preview
+ retention-days: 30
+
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Checkout PR branch
+ id: checkout-pr
+ if: |
+ github.event.pull_request || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.55
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.58
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.activation.outputs.artifact_prefix }}activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Restore inline sub-agents from activation artifact
+ env:
+ GH_AW_SUB_AGENT_DIR: ".github/agents"
+ GH_AW_SUB_AGENT_EXT: ".agent.md"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
+ - name: Restore inline skills from activation artifact
+ env:
+ GH_AW_SKILL_DIR: ".github/skills"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.58 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58 ghcr.io/github/gh-aw-firewall/squid:0.25.58 ghcr.io/github/gh-aw-mcpg:v0.3.22 ghcr.io/github/github-mcp-server:v1.1.0 node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+ - name: Generate Safe Outputs Config
+ run: |
+ mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
+ mkdir -p /tmp/gh-aw/safeoutputs
+ mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_5ae00f67103541aa_EOF'
+ {"create_issue":{"labels":["weekly-refresh-digest"],"max":1,"title_prefix":"[weekly-refresh-digest]"}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_5ae00f67103541aa_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[weekly-refresh-digest]\". Labels [\"weekly-refresh-digest\"] will be automatically added."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_issue": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "fields": {
+ "type": "array"
+ },
+ "labels": {
+ "type": "array",
+ "itemType": "string",
+ "itemSanitize": true,
+ "itemMaxLength": 128
+ },
+ "parent": {
+ "issueOrPRNumber": true
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "temporary_id": {
+ "type": "string"
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ run: |
+ set -eo pipefail
+ mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ case "${DOCKER_HOST:-}" in
+ unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
+ /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
+ * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
+ esac
+ DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.22'
+
+ mkdir -p /home/runner/.copilot
+ GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
+ cat << GH_AW_MCP_CONFIG_9da31ebd0612ba53_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v1.1.0",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_9da31ebd0612ba53_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ # --allow-tool github
+ # --allow-tool safeoutputs
+ # --allow-tool shell(cat /tmp/gh-aw/agent/weekly-refresh-preview/*)
+ # --allow-tool shell(cat)
+ # --allow-tool shell(date)
+ # --allow-tool shell(echo)
+ # --allow-tool shell(find /tmp/gh-aw/agent/weekly-refresh-preview)
+ # --allow-tool shell(grep)
+ # --allow-tool shell(head)
+ # --allow-tool shell(jq * /tmp/gh-aw/agent/weekly-refresh-preview/*)
+ # --allow-tool shell(ls)
+ # --allow-tool shell(printf)
+ # --allow-tool shell(pwd)
+ # --allow-tool shell(safeoutputs:*)
+ # --allow-tool shell(sort)
+ # --allow-tool shell(tail)
+ # --allow-tool shell(uniq)
+ # --allow-tool shell(wc * /tmp/gh-aw/agent/weekly-refresh-preview/*)
+ # --allow-tool shell(wc)
+ # --allow-tool shell(yq)
+ # --allow-tool write
+ timeout-minutes: 10
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.58/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","raw.githubusercontent.com","registry.npmjs.org","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.58"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ GH_AW_MODEL_MULTIPLIERS_PATH="/tmp/gh-aw/model_multipliers.json" node "${RUNNER_TEMP}/gh-aw/actions/merge_awf_model_multipliers.cjs"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ GH_AW_TOOL_CACHE_MOUNT=""
+ GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"
+ if [ -d "$GH_AW_TOOL_CACHE" ]; then
+ if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
+ GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
+ fi
+ elif [ -d "/home/runner/work/_tool" ]; then
+ GH_AW_TOOL_CACHE_MOUNT="/home/runner/work/_tool:/home/runner/work/_tool:ro"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"; export PATH="$(find "$GH_AW_TOOL_CACHE" /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat /tmp/gh-aw/agent/weekly-refresh-preview/*)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find /tmp/gh-aw/agent/weekly-refresh-preview)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq * /tmp/gh-aw/agent/weekly-refresh-preview/*)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc * /tmp/gh-aw/agent/weekly-refresh-preview/*)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ github.token }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_STAGED: true
+ GH_AW_VERSION: v0.77.5
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ RUNNER_TEMP: ${{ runner.temp }}
+ S2STOKENS: true
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect agent errors
+ if: always()
+ id: detect-agent-errors
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - name: Stop MCP Gateway
+ if: always()
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs');
+ await main();
+ env:
+ GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
+ SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ await main();
+ - name: Print firewall logs
+ if: always()
+ continue-on-error: true
+ env:
+ AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
+ run: |
+ # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - safe_outputs
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions: {}
+ concurrency:
+ group: "gh-aw-conclusion-weekly-refresh-digest"
+ cancel-in-progress: false
+ queue: max
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/weekly-refresh-digest.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/weekly-refresh-digest.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "weekly-refresh-digest"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
+ GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"activationComments\":\"false\"}"
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "false"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "10"
+ GH_AW_MAX_EFFECTIVE_TOKENS: "25000000"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
+ await main();
+
+ pre_activation:
+ runs-on: ubuntu-slim
+ outputs:
+ activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }}
+ matched_command: ''
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/weekly-refresh-digest.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Check team membership for workflow
+ id: check_membership
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_REQUIRED_ROLES: "admin,maintainer,write"
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
+ await main();
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ if: (!cancelled()) && needs.agent.result != 'skipped'
+ runs-on: ubuntu-slim
+ permissions: {}
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/weekly-refresh-digest"
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.55"
+ GH_AW_SAFE_OUTPUTS_STAGED: "true"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"activationComments\":\"false\"}"
+ GH_AW_WORKFLOW_ID: "weekly-refresh-digest"
+ GH_AW_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/weekly-refresh-digest.md"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
+ created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@3ea13c02d765410340d533515cb31a7eef2baaf0 # v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Build Weekly Refresh Digest Preview"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/weekly-refresh-digest.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.55"
+ GH_AW_INFO_AWF_VERSION: "v0.25.58"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }}
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: ${{ needs.activation.outputs.artifact_prefix }}agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Configure GH_HOST for enterprise compatibility
+ id: ghes-host-config
+ shell: bash
+ run: |
+ # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
+ # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
+ GH_HOST="${GITHUB_SERVER_URL#https://}"
+ GH_HOST="${GH_HOST#http://}"
+ echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"weekly-refresh-digest\"],\"max\":1,\"title_prefix\":\"[weekly-refresh-digest]\"}}"
+ GH_AW_SAFE_OUTPUTS_STAGED: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
+ await main();
+
diff --git a/.github/workflows/weekly-refresh-digest.md b/.github/workflows/weekly-refresh-digest.md
new file mode 100644
index 0000000..7023037
--- /dev/null
+++ b/.github/workflows/weekly-refresh-digest.md
@@ -0,0 +1,74 @@
+---
+name: Build Weekly Refresh Digest Preview
+description: Deterministically assembles staged quest reports into parent and child issue previews
+on:
+ workflow_call:
+ inputs:
+ week_key:
+ description: ISO week key for this reporting run
+ required: true
+ type: string
+ prior_week_key:
+ description: ISO week key used to locate prior findings
+ required: true
+ type: string
+permissions:
+ actions: read
+ contents: read
+ copilot-requests: write
+strict: true
+network: {}
+tools:
+ bash:
+ - "cat /tmp/gh-aw/agent/weekly-refresh-preview/*"
+ - "find /tmp/gh-aw/agent/weekly-refresh-preview *"
+ - "jq * /tmp/gh-aw/agent/weekly-refresh-preview/*"
+ - "wc * /tmp/gh-aw/agent/weekly-refresh-preview/*"
+steps:
+ - name: Download staged quest reports
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ pattern: weekly-refresh-${{ inputs.week_key }}-*
+ path: /tmp/gh-aw/agent/weekly-refresh-reports
+ merge-multiple: true
+ - name: Build deterministic digest preview
+ run: |
+ node scripts/weekly-refresh-report.mjs digest \
+ --reports-dir /tmp/gh-aw/agent/weekly-refresh-reports \
+ --output-dir /tmp/gh-aw/agent/weekly-refresh-preview \
+ --week "${{ inputs.week_key }}" \
+ --prior-week "${{ inputs.prior_week_key }}"
+ - name: Upload staged digest preview
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: weekly-refresh-${{ inputs.week_key }}-preview
+ path: /tmp/gh-aw/agent/weekly-refresh-preview
+ if-no-files-found: error
+ retention-days: 30
+safe-outputs:
+ activation-comments: false
+ missing-data: false
+ missing-tool: false
+ noop: false
+ report-failure-as-issue: false
+ report-incomplete: false
+ staged: true
+ threat-detection: false
+timeout-minutes: 10
+---
+
+# Phase 2A Weekly Refresh Digest Audit
+
+Audit the deterministic preview for `${{ inputs.week_key }}` without changing it.
+
+1. Read `/tmp/gh-aw/agent/weekly-refresh-preview/digest.json`.
+2. Confirm it contains exactly five quest results and that every result is
+ `material-change`, `no-material-change`, or `blocked`.
+3. Confirm `parent-issue.md` covers all five quests.
+4. Confirm a child preview exists only for each material-change result.
+5. Report any missing, malformed, or inconsistent preview as blocked.
+
+Return a concise GitHub-flavored Markdown audit starting nested headings at
+`###`. Do not create or update issues, comments, branches, pull requests,
+workflows, or repository content.
diff --git a/.github/workflows/weekly-refresh-orchestrator.yml b/.github/workflows/weekly-refresh-orchestrator.yml
new file mode 100644
index 0000000..7fe6dba
--- /dev/null
+++ b/.github/workflows/weekly-refresh-orchestrator.yml
@@ -0,0 +1,86 @@
+name: Weekly Refresh Preview
+
+# Phase 2B may add `0 8 * * 1` only after default-branch trials and issue-output
+# spikes pass. Phase 2A remains manual-only.
+on:
+ workflow_dispatch:
+ inputs:
+ week_key:
+ description: Optional ISO week key; defaults to the current UTC ISO week
+ required: false
+ type: string
+
+permissions:
+ actions: read
+ contents: read
+ copilot-requests: write
+
+jobs:
+ context:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ week_key: ${{ steps.context.outputs.week_key }}
+ prior_week_key: ${{ steps.context.outputs.prior_week_key }}
+ parent_key: ${{ steps.context.outputs.parent_key }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
+ - name: Resolve deterministic reporting context
+ id: context
+ env:
+ REQUESTED_WEEK_KEY: ${{ inputs.week_key }}
+ run: |
+ node scripts/weekly-refresh-report.mjs context \
+ --week "$REQUESTED_WEEK_KEY" \
+ --github-output "$GITHUB_OUTPUT"
+
+ foundry-local:
+ needs: context
+ uses: ./.github/workflows/discover-foundry-local.lock.yml
+ with:
+ week_key: ${{ needs.context.outputs.week_key }}
+ prior_week_key: ${{ needs.context.outputs.prior_week_key }}
+
+ microsoft-foundry:
+ needs: context
+ uses: ./.github/workflows/discover-microsoft-foundry.lock.yml
+ with:
+ week_key: ${{ needs.context.outputs.week_key }}
+ prior_week_key: ${{ needs.context.outputs.prior_week_key }}
+
+ agentic-rag:
+ needs: context
+ uses: ./.github/workflows/discover-agentic-rag.lock.yml
+ with:
+ week_key: ${{ needs.context.outputs.week_key }}
+ prior_week_key: ${{ needs.context.outputs.prior_week_key }}
+
+ foundry-toolkit:
+ needs: context
+ uses: ./.github/workflows/discover-foundry-toolkit.lock.yml
+ with:
+ week_key: ${{ needs.context.outputs.week_key }}
+ prior_week_key: ${{ needs.context.outputs.prior_week_key }}
+
+ context-engineering:
+ needs: context
+ uses: ./.github/workflows/discover-context-engineering.lock.yml
+ with:
+ week_key: ${{ needs.context.outputs.week_key }}
+ prior_week_key: ${{ needs.context.outputs.prior_week_key }}
+
+ digest:
+ if: ${{ always() && needs.context.result == 'success' }}
+ needs:
+ - context
+ - foundry-local
+ - microsoft-foundry
+ - agentic-rag
+ - foundry-toolkit
+ - context-engineering
+ uses: ./.github/workflows/weekly-refresh-digest.lock.yml
+ with:
+ week_key: ${{ needs.context.outputs.week_key }}
+ prior_week_key: ${{ needs.context.outputs.prior_week_key }}
diff --git a/scripts/validate-refresh-config.mjs b/scripts/validate-refresh-config.mjs
index e14c33e..d9ad169 100644
--- a/scripts/validate-refresh-config.mjs
+++ b/scripts/validate-refresh-config.mjs
@@ -4,6 +4,11 @@ import { fileURLToPath } from "node:url";
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const errors = [];
+const resultValues = [
+ "material-change",
+ "no-material-change",
+ "blocked",
+];
function check(condition, message) {
if (!condition) {
@@ -802,10 +807,10 @@ function validateDiscoveryPolicy(policyText) {
"shared discovery policy must leave output suppression to each main workflow",
);
check(
- policyText.includes("No safe-output handler") &&
+ policyText.includes("`emit_weekly_report`") &&
policyText.includes("Result: no-material-change") &&
policyText.includes("Result: blocked"),
- "discovery policy must define explicit no-change and blocked outcomes",
+ "discovery policy must define the staged report output and explicit outcomes",
);
}
@@ -833,9 +838,10 @@ function validateDiscoveryWorkflow(
];
check(
- /^\s{2}workflow_dispatch:\s*$/m.test(frontmatter) &&
+ /^\s{2}workflow_call:\s*$/m.test(frontmatter) &&
+ /^\s{2}workflow_dispatch:\s*$/m.test(frontmatter) &&
!/^\s{2}schedule:\s*$/m.test(frontmatter),
- `${workflowPath} must be manual-only in Phase 1`,
+ `${workflowPath} must support manual and reusable Phase 2A runs without a schedule`,
);
check(
/^strict:\s*true$/m.test(frontmatter),
@@ -853,8 +859,13 @@ function validateDiscoveryWorkflow(
);
check(
frontmatter.includes("shared/discovery-policy.md") &&
+ frontmatter.includes("uses: shared/report-output.md") &&
+ frontmatter.includes(`quest-id: ${quest.id}`) &&
+ frontmatter.includes(`quest-slug: ${quest.slug}`) &&
+ frontmatter.includes(`quest-title: ${quest.title}`) &&
+ frontmatter.includes(`quest-label: quest/${quest.id}-${quest.slug}`) &&
frontmatter.includes(`../agents/${quest.agent}.agent.md`),
- `${workflowPath} must import the shared policy and exactly scoped Quest Master`,
+ `${workflowPath} must import the reporting policy and exactly scoped Quest Master`,
);
check(
sameJson(allowedDomains, specification.domains),
@@ -870,13 +881,14 @@ function validateDiscoveryWorkflow(
!/^\s{2}(?:contents|issues|pull-requests|actions):\s*write$/m.test(
frontmatter,
),
- `${workflowPath} must disable automatic writes without enabling mutation permissions or outputs`,
+ `${workflowPath} must disable automatic writes and retain staged output processing`,
);
check(
compiledText.includes('GH_AW_FAILURE_REPORT_AS_ISSUE: "false"') &&
compiledText.includes('GH_AW_SAFE_OUTPUTS_STAGED: "true"') &&
- compiledText.includes("Tools: create_issue"),
- `${workflowPath} compiled fallback must remain disabled and staged`,
+ compiledText.includes("emit_weekly_report") &&
+ compiledText.includes("weekly-refresh-report.mjs materialize"),
+ `${workflowPath} compiled reporting output must remain disabled for writes and staged`,
);
check(
!/^\s{6}(?:contents|issues|pull-requests|actions|checks):\s*write$/m.test(
@@ -890,8 +902,11 @@ function validateDiscoveryWorkflow(
);
check(
workflowText.includes(`quest slug \`${quest.slug}\``) &&
+ workflowText.includes("${{ inputs.week_key }}") &&
+ workflowText.includes("${{ inputs.prior_week_key }}") &&
+ workflowText.includes("`emit_weekly_report`") &&
workflowText.includes("Do not create or update"),
- `${workflowPath} must declare its fixed quest and read-only outcome`,
+ `${workflowPath} must declare its fixed quest, reporting context, and read-only outcome`,
);
check(
new RegExp(`^name:\\s*${quest.agent}$`, "m").test(agentFrontmatter) &&
@@ -930,6 +945,169 @@ function validateDiscoveryWorkflow(
}
}
+function validateReportOutput(reportOutputText, actionsLock) {
+ const path = ".github/workflows/shared/report-output.md";
+ const frontmatter = readFrontmatter(reportOutputText, path);
+ const checkoutPin =
+ actionsLock.entries["actions/checkout@v6.0.2"]?.sha;
+ const uploadPin =
+ actionsLock.entries["actions/upload-artifact@v7.0.1"]?.sha;
+
+ check(
+ ["quest-id", "quest-slug", "quest-title", "quest-label"].every((field) =>
+ new RegExp(`^ ${field}:\\s*$`, "m").test(frontmatter),
+ ),
+ `${path} must define the exact parameterized quest identity`,
+ );
+ check(
+ /^\s{4}emit-weekly-report:\s*$/m.test(frontmatter) &&
+ frontmatter.includes("needs: agent") &&
+ frontmatter.includes("if: always()") &&
+ frontmatter.includes("type: choice") &&
+ frontmatter.includes("report_json:"),
+ `${path} must define one structured report materializer job`,
+ );
+ check(
+ frontmatter.includes(
+ `actions/checkout@${checkoutPin}`,
+ ) &&
+ frontmatter.includes(
+ `actions/upload-artifact@${uploadPin}`,
+ ) &&
+ frontmatter.includes("weekly-refresh-report.mjs materialize") &&
+ frontmatter.includes('"$GH_AW_AGENT_OUTPUT"'),
+ `${path} must materialize and upload reports with pinned actions`,
+ );
+ check(
+ !/^\s+(?:contents|issues|pull-requests|actions|checks|statuses):\s*write$/m.test(
+ frontmatter,
+ ),
+ `${path} must not grant a repository write permission`,
+ );
+ check(
+ reportOutputText.includes("schemaVersion") &&
+ reportOutputText.includes("officialSourceDeltas") &&
+ reportOutputText.includes("priorFindingKeys") &&
+ reportOutputText.includes("blockedReason") &&
+ reportOutputText.includes("Call `emit_weekly_report` exactly once"),
+ `${path} must expose the complete versioned report contract`,
+ );
+ validatePinnedActions(reportOutputText, path);
+}
+
+function validateDigestWorkflow(digestText, compiledText, tooling) {
+ const path = ".github/workflows/weekly-refresh-digest.md";
+ const frontmatter = readFrontmatter(digestText, path);
+
+ check(
+ /^\s{2}workflow_call:\s*$/m.test(frontmatter) &&
+ !/^\s{2}workflow_dispatch:\s*$/m.test(frontmatter) &&
+ !/^\s{2}schedule:\s*$/m.test(frontmatter),
+ `${path} must be reusable only; the orchestrator owns manual activation`,
+ );
+ check(
+ /^permissions:\s*\n actions:\s*read\s*\n contents:\s*read\s*\n copilot-requests:\s*write$/m.test(
+ frontmatter,
+ ),
+ `${path} must grant only artifact, repository-read, and inference permissions`,
+ );
+ check(
+ /^network:\s*\{\}$/m.test(frontmatter),
+ `${path} must deny agent network access`,
+ );
+ check(
+ frontmatter.includes(tooling.downloadArtifactAction) &&
+ frontmatter.includes(tooling.uploadArtifactAction) &&
+ frontmatter.includes("weekly-refresh-report.mjs digest") &&
+ frontmatter.includes("continue-on-error: true") &&
+ frontmatter.includes("merge-multiple: true"),
+ `${path} must tolerate missing quest artifacts and build a pinned deterministic preview`,
+ );
+ check(
+ /^safe-outputs:\s*\n activation-comments:\s*false\s*\n missing-data:\s*false\s*\n missing-tool:\s*false\s*\n noop:\s*false\s*\n report-failure-as-issue:\s*false\s*\n report-incomplete:\s*false\s*\n staged:\s*true\s*\n threat-detection:\s*false$/m.test(
+ frontmatter,
+ ),
+ `${path} must keep every framework output staged and suppressed`,
+ );
+ check(
+ compiledText.includes('GH_AW_SAFE_OUTPUTS_STAGED: "true"') &&
+ compiledText.includes("weekly-refresh-report.mjs digest") &&
+ !/^\s+(?:contents|issues|pull-requests|actions|checks|statuses):\s*write$/m.test(
+ compiledText,
+ ),
+ `${path} compiled workflow must remain staged and free of repository writes`,
+ );
+ check(
+ digestText.includes("exactly five quest results") &&
+ digestText.includes("child preview exists only") &&
+ digestText.includes("Do not create or update"),
+ `${path} must audit the deterministic five-quest preview without mutation`,
+ );
+ validatePinnedActions(digestText, path);
+}
+
+function validateOrchestrator(orchestratorText, quests, tooling) {
+ const path = ".github/workflows/weekly-refresh-orchestrator.yml";
+ const discoveryUses = [
+ ...orchestratorText.matchAll(
+ /uses:\s*\.\/\.github\/workflows\/(discover-[a-z-]+\.lock\.yml)/g,
+ ),
+ ].map((match) => match[1]);
+ const expectedUses = quests.quests
+ .map(({ slug }) => `discover-${slug}.lock.yml`)
+ .sort();
+
+ check(
+ /^\s{2}workflow_dispatch:\s*$/m.test(orchestratorText) &&
+ !/^\s{2}schedule:\s*$/m.test(orchestratorText),
+ `${path} must remain manual-only in Phase 2A`,
+ );
+ check(
+ orchestratorText.includes("0 8 * * 1") &&
+ orchestratorText.includes("Phase 2B"),
+ `${path} must document but not activate the intended Monday schedule`,
+ );
+ check(
+ /^permissions:\s*\n actions:\s*read\s*\n contents:\s*read\s*\n copilot-requests:\s*write$/m.test(
+ orchestratorText,
+ ) &&
+ !/^\s+(?:contents|issues|pull-requests|actions|checks|statuses):\s*write$/m.test(
+ orchestratorText,
+ ),
+ `${path} must grant only read and inference permissions`,
+ );
+ check(
+ sameJson(discoveryUses.sort(), expectedUses) &&
+ discoveryUses.length === quests.quests.length,
+ `${path} must fan out to each discovery workflow exactly once`,
+ );
+ check(
+ orchestratorText.includes("weekly-refresh-report.mjs context") &&
+ orchestratorText.includes("prior_week_key") &&
+ orchestratorText.includes("always() && needs.context.result == 'success'") &&
+ orchestratorText.includes(
+ "uses: ./.github/workflows/weekly-refresh-digest.lock.yml",
+ ),
+ `${path} must use one week algorithm and run the digest after every quest outcome`,
+ );
+ check(
+ orchestratorText.includes(tooling.checkoutAction),
+ `${path} must use the reviewed checkout action pin`,
+ );
+ validatePinnedActions(orchestratorText, path);
+}
+
+function validateReportContract(reportContractText) {
+ check(
+ reportContractText.includes("version 1 JSON artifact") &&
+ resultValues.every((result) => reportContractText.includes(result)) &&
+ reportContractText.includes("deterministic parent key") &&
+ reportContractText.includes("child issue Markdown preview only") &&
+ reportContractText.includes("No Phase 2A workflow creates or updates"),
+ "report-contract.md must define the Phase 2A schema, outcomes, identifiers, and no-write boundary",
+ );
+}
+
const capabilities = await readJson(".github/agentic-refresh/capabilities.json");
const quests = await readJson(".github/agentic-refresh/quests.json");
const labels = await readJson(".github/agentic-refresh/labels.json");
@@ -941,6 +1119,7 @@ const humanMergeRuleset = await readJson(
".github/agentic-refresh/rulesets/human-merge.json",
);
const tooling = await readJson(".github/agentic-refresh/tooling.json");
+const actionsLock = await readJson(".github/aw/actions-lock.json");
const codeowners = await readText(".github/CODEOWNERS");
const copilotSetup = await readText(
".github/workflows/copilot-setup-steps.yml",
@@ -952,6 +1131,21 @@ const workflowFiles = (await readdir(resolve(root, workflowDirectory)))
const discoveryPolicy = await readText(
".github/workflows/shared/discovery-policy.md",
);
+const reportOutput = await readText(
+ ".github/workflows/shared/report-output.md",
+);
+const reportContract = await readText(
+ ".github/agentic-refresh/report-contract.md",
+);
+const digestWorkflow = await readText(
+ ".github/workflows/weekly-refresh-digest.md",
+);
+const digestCompiledWorkflow = await readText(
+ ".github/workflows/weekly-refresh-digest.lock.yml",
+);
+const weeklyOrchestrator = await readText(
+ ".github/workflows/weekly-refresh-orchestrator.yml",
+);
const discoverySpecifications = new Map([
[
"foundry-local",
@@ -1014,6 +1208,10 @@ validateGitHubTokenPolicy(tokenPolicy);
validateRuleset(ruleset);
validateHumanMergeRuleset(humanMergeRuleset);
validateDiscoveryPolicy(discoveryPolicy);
+validateReportOutput(reportOutput, actionsLock);
+validateDigestWorkflow(digestWorkflow, digestCompiledWorkflow, tooling);
+validateOrchestrator(weeklyOrchestrator, quests, tooling);
+validateReportContract(reportContract);
for (const quest of quests.quests) {
const specification = discoverySpecifications.get(quest.slug);
@@ -1036,6 +1234,13 @@ check(
tooling.ghAwVersion === "v0.77.5",
"tooling.json must pin gh-aw v0.77.5",
);
+check(
+ tooling.downloadArtifactAction ===
+ `actions/download-artifact@${actionsLock.entries["actions/download-artifact@v8.0.1"]?.sha}` &&
+ tooling.uploadArtifactAction ===
+ `actions/upload-artifact@${actionsLock.entries["actions/upload-artifact@v7.0.1"]?.sha}`,
+ "tooling.json artifact action pins must match the gh-aw action lock",
+);
check(
copilotSetup.includes(tooling.ghAwSetupAction) &&
copilotSetup.includes(`version: ${tooling.ghAwVersion}`),
@@ -1061,6 +1266,6 @@ if (errors.length > 0) {
process.exitCode = 1;
} else {
console.log(
- `Validated ${quests.quests.length} quests, ${discoverySpecifications.size} read-only discovery workflows, and ${capabilities.repositories.length} repository capability records.`,
+ `Validated ${quests.quests.length} quests, ${discoverySpecifications.size} staged reporting workflows, the weekly digest/orchestrator, and ${capabilities.repositories.length} repository capability records.`,
);
}
diff --git a/scripts/weekly-refresh-report.mjs b/scripts/weekly-refresh-report.mjs
new file mode 100644
index 0000000..a2c6440
--- /dev/null
+++ b/scripts/weekly-refresh-report.mjs
@@ -0,0 +1,1069 @@
+import assert from "node:assert/strict";
+import { createHash } from "node:crypto";
+import {
+ mkdir,
+ mkdtemp,
+ readFile,
+ rm,
+ writeFile,
+} from "node:fs/promises";
+import { tmpdir } from "node:os";
+import { dirname, posix, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+
+const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+const resultValues = [
+ "material-change",
+ "no-material-change",
+ "blocked",
+];
+const lifecycleValues = [
+ "GA",
+ "Public preview",
+ "Private preview",
+ "Not stated by allowed sources",
+];
+const learnerImpactValues = ["high", "medium", "low", "none"];
+const coreKeys = [
+ "schemaVersion",
+ "weekKey",
+ "priorWeekKey",
+ "questSlug",
+ "result",
+ "executiveSummary",
+ "currentQuestStatus",
+ "learnerImpact",
+ "officialSourceDeltas",
+ "contentDrift",
+ "proposedChanges",
+ "assetImpact",
+ "codeTourImpact",
+ "validationPlan",
+ "risks",
+ "humanDecisions",
+ "sourceFingerprints",
+ "priorFindingKeys",
+ "blockedReason",
+];
+
+function parseArguments(argv) {
+ const [command, ...values] = argv;
+ const options = {};
+
+ for (let index = 0; index < values.length; index += 2) {
+ const flag = values[index];
+ const value = values[index + 1];
+ if (!flag?.startsWith("--") || value === undefined) {
+ throw new Error(`Invalid argument sequence near ${flag ?? ""}`);
+ }
+ options[flag.slice(2)] = value;
+ }
+
+ return { command, options };
+}
+
+function canonicalize(value) {
+ if (Array.isArray(value)) {
+ return value.map(canonicalize);
+ }
+ if (value !== null && typeof value === "object") {
+ return Object.fromEntries(
+ Object.entries(value)
+ .sort(([left], [right]) => left.localeCompare(right))
+ .map(([key, nestedValue]) => [key, canonicalize(nestedValue)]),
+ );
+ }
+ return value;
+}
+
+function stableJson(value) {
+ return JSON.stringify(canonicalize(value));
+}
+
+function sha256(value) {
+ return createHash("sha256").update(stableJson(value)).digest("hex");
+}
+
+function uniqueSorted(values) {
+ return [...new Set(values)].sort();
+}
+
+function isoWeekKey(date) {
+ const target = new Date(
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
+ );
+ const day = target.getUTCDay() || 7;
+ target.setUTCDate(target.getUTCDate() + 4 - day);
+ const isoYear = target.getUTCFullYear();
+ const yearStart = new Date(Date.UTC(isoYear, 0, 1));
+ const week = Math.ceil(
+ ((target.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7,
+ );
+ return `${isoYear}-W${String(week).padStart(2, "0")}`;
+}
+
+function mondayForWeekKey(weekKey) {
+ const match = /^([0-9]{4})-W([0-9]{2})$/.exec(weekKey);
+ if (!match) {
+ throw new Error(`Invalid ISO week key: ${weekKey}`);
+ }
+
+ const year = Number(match[1]);
+ const week = Number(match[2]);
+ const januaryFourth = new Date(Date.UTC(year, 0, 4));
+ const januaryFourthDay = januaryFourth.getUTCDay() || 7;
+ const monday = new Date(januaryFourth);
+ monday.setUTCDate(januaryFourth.getUTCDate() - januaryFourthDay + 1 + (week - 1) * 7);
+
+ if (isoWeekKey(monday) !== weekKey) {
+ throw new Error(`ISO week does not exist: ${weekKey}`);
+ }
+ return monday;
+}
+
+function weekContext(requestedWeek) {
+ const weekKey = requestedWeek || isoWeekKey(new Date());
+ const monday = mondayForWeekKey(weekKey);
+ const priorMonday = new Date(monday);
+ priorMonday.setUTCDate(priorMonday.getUTCDate() - 7);
+ return {
+ weekKey,
+ priorWeekKey: isoWeekKey(priorMonday),
+ parentKey: `weekly-refresh:${weekKey}`,
+ parentMarker: ``,
+ };
+}
+
+async function readQuests() {
+ const text = await readFile(
+ resolve(root, ".github/agentic-refresh/quests.json"),
+ "utf8",
+ );
+ return JSON.parse(text);
+}
+
+function questLabel(quest) {
+ return `quest/${quest.id}-${quest.slug}`;
+}
+
+function isOwnedPath(quest, repository, path) {
+ if (
+ typeof path !== "string" ||
+ path.length === 0 ||
+ path.startsWith("/") ||
+ path.includes("\\") ||
+ posix.normalize(path) !== path ||
+ path.split("/").some((segment) => segment === "." || segment === "..")
+ ) {
+ return false;
+ }
+ const patterns = quest.ownedPaths?.[repository] ?? [];
+ return patterns.some((pattern) => {
+ if (pattern.endsWith("/**")) {
+ const directory = pattern.slice(0, -3);
+ return path === directory || path.startsWith(`${directory}/`);
+ }
+ return path === pattern;
+ });
+}
+
+function isAllowedSource(quest, url) {
+ let candidate;
+ try {
+ candidate = new URL(url);
+ } catch {
+ return false;
+ }
+
+ return quest.officialSourcePrefixes.some((prefix) => {
+ const allowed = new URL(prefix);
+ const allowedPath = allowed.pathname.replace(/\/+$/, "");
+ return (
+ candidate.origin === allowed.origin &&
+ (candidate.pathname === allowedPath ||
+ candidate.pathname.startsWith(`${allowedPath}/`))
+ );
+ });
+}
+
+function externalPathAllowed(quest, repository, path) {
+ return quest.externalReports.some(
+ (report) =>
+ report.repository === repository && report.observedPaths.includes(path),
+ );
+}
+
+function requireString(errors, value, label, { allowEmpty = false, max = 8_000 } = {}) {
+ if (
+ typeof value !== "string" ||
+ (!allowEmpty && value.trim().length === 0) ||
+ value.length > max
+ ) {
+ errors.push(`${label} must be a ${allowEmpty ? "" : "non-empty "}string of at most ${max} characters`);
+ }
+}
+
+function requireStringArray(errors, value, label, { maxItems = 50 } = {}) {
+ if (!Array.isArray(value) || value.length > maxItems) {
+ errors.push(`${label} must be an array with at most ${maxItems} items`);
+ return;
+ }
+ value.forEach((item, index) =>
+ requireString(errors, item, `${label}[${index}]`, { max: 2_000 }),
+ );
+ if (new Set(value).size !== value.length) {
+ errors.push(`${label} must not contain duplicates`);
+ }
+}
+
+function validateCoreReport(report, quest, expected) {
+ const errors = [];
+ if (report === null || typeof report !== "object" || Array.isArray(report)) {
+ return ["report must be a JSON object"];
+ }
+
+ const unknownKeys = Object.keys(report).filter((key) => !coreKeys.includes(key));
+ const missingKeys = coreKeys.filter((key) => !(key in report));
+ if (unknownKeys.length > 0) {
+ errors.push(`report has unknown fields: ${unknownKeys.join(", ")}`);
+ }
+ if (missingKeys.length > 0) {
+ errors.push(`report is missing fields: ${missingKeys.join(", ")}`);
+ }
+
+ if (report.schemaVersion !== 1) {
+ errors.push("schemaVersion must be 1");
+ }
+ if (report.weekKey !== expected.weekKey) {
+ errors.push(`weekKey must be ${expected.weekKey}`);
+ }
+ if (report.priorWeekKey !== expected.priorWeekKey) {
+ errors.push(`priorWeekKey must be ${expected.priorWeekKey}`);
+ }
+ if (report.questSlug !== quest.slug) {
+ errors.push(`questSlug must be ${quest.slug}`);
+ }
+ if (!resultValues.includes(report.result)) {
+ errors.push(`result must be one of ${resultValues.join(", ")}`);
+ }
+ if (!learnerImpactValues.includes(report.learnerImpact)) {
+ errors.push(`learnerImpact must be one of ${learnerImpactValues.join(", ")}`);
+ }
+
+ requireString(errors, report.executiveSummary, "executiveSummary", { max: 2_000 });
+ requireString(errors, report.currentQuestStatus, "currentQuestStatus", { max: 4_000 });
+ requireString(errors, report.assetImpact, "assetImpact", {
+ allowEmpty: true,
+ max: 4_000,
+ });
+ requireStringArray(errors, report.validationPlan, "validationPlan");
+ requireStringArray(errors, report.risks, "risks");
+ requireStringArray(errors, report.humanDecisions, "humanDecisions");
+ requireStringArray(errors, report.sourceFingerprints, "sourceFingerprints");
+ requireStringArray(errors, report.priorFindingKeys, "priorFindingKeys");
+
+ for (const [index, key] of (report.priorFindingKeys ?? []).entries()) {
+ if (!/^finding:[0-9a-f]{64}$/.test(key)) {
+ errors.push(`priorFindingKeys[${index}] must be a deterministic finding key`);
+ }
+ }
+
+ if (!Array.isArray(report.officialSourceDeltas)) {
+ errors.push("officialSourceDeltas must be an array");
+ } else {
+ report.officialSourceDeltas.forEach((delta, index) => {
+ const prefix = `officialSourceDeltas[${index}]`;
+ if (delta === null || typeof delta !== "object" || Array.isArray(delta)) {
+ errors.push(`${prefix} must be an object`);
+ return;
+ }
+ for (const field of [
+ "fact",
+ "lifecycle",
+ "evidenceUrl",
+ "evidenceLocation",
+ "observedWording",
+ "fingerprint",
+ ]) {
+ requireString(errors, delta[field], `${prefix}.${field}`, { max: 4_000 });
+ }
+ if (!lifecycleValues.includes(delta.lifecycle)) {
+ errors.push(`${prefix}.lifecycle is not an allowed lifecycle value`);
+ }
+ if (!isAllowedSource(quest, delta.evidenceUrl ?? "")) {
+ errors.push(`${prefix}.evidenceUrl is outside the quest source allowlist`);
+ }
+ if (!report.sourceFingerprints?.includes(delta.fingerprint)) {
+ errors.push(`${prefix}.fingerprint must appear in sourceFingerprints`);
+ }
+ });
+ }
+
+ if (!Array.isArray(report.contentDrift)) {
+ errors.push("contentDrift must be an array");
+ } else {
+ report.contentDrift.forEach((drift, index) => {
+ const prefix = `contentDrift[${index}]`;
+ if (drift === null || typeof drift !== "object" || Array.isArray(drift)) {
+ errors.push(`${prefix} must be an object`);
+ return;
+ }
+ requireString(errors, drift.path, `${prefix}.path`);
+ requireString(errors, drift.section, `${prefix}.section`, { max: 1_000 });
+ requireString(errors, drift.observation, `${prefix}.observation`, { max: 4_000 });
+ if (
+ !isOwnedPath(
+ quest,
+ "Azure-Samples/JavaScript-AI-Buildathon",
+ drift.path ?? "",
+ )
+ ) {
+ errors.push(`${prefix}.path is outside the quest ownership boundary`);
+ }
+ });
+ }
+
+ if (!Array.isArray(report.proposedChanges)) {
+ errors.push("proposedChanges must be an array");
+ } else {
+ report.proposedChanges.forEach((change, index) => {
+ const prefix = `proposedChanges[${index}]`;
+ if (change === null || typeof change !== "object" || Array.isArray(change)) {
+ errors.push(`${prefix} must be an object`);
+ return;
+ }
+ requireString(errors, change.repository, `${prefix}.repository`);
+ requireString(errors, change.path, `${prefix}.path`);
+ requireString(errors, change.description, `${prefix}.description`, {
+ max: 4_000,
+ });
+ if (
+ change.repository !== "Azure-Samples/JavaScript-AI-Buildathon" ||
+ !isOwnedPath(quest, change.repository, change.path ?? "")
+ ) {
+ errors.push(`${prefix} must target a Buildathon path owned by this quest`);
+ }
+ });
+ }
+
+ if (!Array.isArray(report.codeTourImpact)) {
+ errors.push("codeTourImpact must be an array");
+ } else {
+ report.codeTourImpact.forEach((impact, index) => {
+ const prefix = `codeTourImpact[${index}]`;
+ if (impact === null || typeof impact !== "object" || Array.isArray(impact)) {
+ errors.push(`${prefix} must be an object`);
+ return;
+ }
+ for (const field of ["repository", "path", "mode", "handoff", "observation"]) {
+ requireString(errors, impact[field], `${prefix}.${field}`, { max: 4_000 });
+ }
+ if (
+ impact.mode !== "report-only" ||
+ impact.handoff !== "manual-maintainer" ||
+ !externalPathAllowed(quest, impact.repository ?? "", impact.path ?? "")
+ ) {
+ errors.push(`${prefix} must preserve an exact report-only CodeTour handoff`);
+ }
+ });
+ }
+
+ if (report.result === "material-change") {
+ if (report.learnerImpact === "none") {
+ errors.push("material-change must declare high, medium, or low learnerImpact");
+ }
+ if ((report.proposedChanges ?? []).length === 0) {
+ errors.push("material-change must include at least one proposed change");
+ }
+ if (
+ (report.officialSourceDeltas ?? []).length === 0 &&
+ (report.contentDrift ?? []).length === 0
+ ) {
+ errors.push("material-change must include official evidence or content drift");
+ }
+ if (report.blockedReason !== null) {
+ errors.push("material-change must set blockedReason to null");
+ }
+ } else if (report.result === "no-material-change") {
+ if ((report.proposedChanges ?? []).length !== 0) {
+ errors.push("no-material-change must not propose changes");
+ }
+ if (report.blockedReason !== null) {
+ errors.push("no-material-change must set blockedReason to null");
+ }
+ } else if (report.result === "blocked") {
+ requireString(errors, report.blockedReason, "blockedReason", { max: 2_000 });
+ if ((report.proposedChanges ?? []).length !== 0) {
+ errors.push("blocked reports must not propose changes");
+ }
+ }
+
+ if (
+ report.result !== "blocked" &&
+ (report.sourceFingerprints ?? []).length === 0
+ ) {
+ errors.push("completed reports must include at least one source fingerprint");
+ }
+
+ if (Buffer.byteLength(JSON.stringify(report), "utf8") > 262_144) {
+ errors.push("report exceeds the 256 KiB limit");
+ }
+ return errors;
+}
+
+function findingKeys(report) {
+ const findings = [
+ ...report.officialSourceDeltas.map((delta) => ({
+ type: "official-source",
+ evidenceUrl: delta.evidenceUrl,
+ evidenceLocation: delta.evidenceLocation,
+ observedWording: delta.observedWording,
+ })),
+ ...report.contentDrift.map((drift) => ({
+ type: "content-drift",
+ path: drift.path,
+ section: drift.section,
+ observation: drift.observation,
+ })),
+ ...report.codeTourImpact.map((impact) => ({
+ type: "codetour",
+ repository: impact.repository,
+ path: impact.path,
+ observation: impact.observation,
+ })),
+ ];
+ return uniqueSorted(
+ findings.map((finding) => `finding:${sha256({
+ questSlug: report.questSlug,
+ ...finding,
+ })}`),
+ );
+}
+
+function escapeTable(value) {
+ return String(value).replaceAll("|", "\\|").replaceAll("\n", " ");
+}
+
+function renderList(values) {
+ return values.length > 0 ? values.map((value) => `- ${value}`).join("\n") : "- None.";
+}
+
+function renderDeltaTable(deltas) {
+ if (deltas.length === 0) {
+ return "No official-source delta was recorded.";
+ }
+ return [
+ "| Fact | Lifecycle | Evidence | Location | Fingerprint |",
+ "|---|---|---|---|---|",
+ ...deltas.map(
+ (delta) =>
+ `| ${escapeTable(delta.fact)} | ${escapeTable(delta.lifecycle)} | ${escapeTable(delta.evidenceUrl)} | ${escapeTable(delta.evidenceLocation)} | ${escapeTable(delta.fingerprint)} |`,
+ ),
+ ].join("\n");
+}
+
+function buildChildIssuePreview(report, quest, deduplication) {
+ if (report.result !== "material-change") {
+ return null;
+ }
+
+ const manifest = {
+ version: 1,
+ weekKey: report.weekKey,
+ priorWeekKey: report.priorWeekKey,
+ questSlug: quest.slug,
+ result: report.result,
+ parentKey: deduplication.parentKey,
+ childKey: deduplication.childKey,
+ sourceFingerprints: report.sourceFingerprints,
+ findingKeys: deduplication.findingKeys,
+ priorFindingKeys: report.priorFindingKeys,
+ };
+ const body = [
+ deduplication.childMarker,
+ ``,
+ "",
+ "### Executive summary",
+ report.executiveSummary,
+ "",
+ "### Current quest status",
+ report.currentQuestStatus,
+ "",
+ "### Official-source delta",
+ renderDeltaTable(report.officialSourceDeltas),
+ "",
+ "### Learner impact",
+ report.learnerImpact,
+ "",
+ "### Current-content drift",
+ report.contentDrift.length > 0
+ ? report.contentDrift
+ .map(
+ (drift) =>
+ `- \`${drift.path}\` — **${drift.section}**: ${drift.observation}`,
+ )
+ .join("\n")
+ : "- None.",
+ "",
+ "### Proposed changes",
+ report.proposedChanges
+ .map(
+ (change) =>
+ `- \`${change.repository}:${change.path}\` — ${change.description}`,
+ )
+ .join("\n"),
+ "",
+ "### Asset impact",
+ report.assetImpact || "None.",
+ "",
+ "### CodeTour impact",
+ report.codeTourImpact.length > 0
+ ? report.codeTourImpact
+ .map(
+ (impact) =>
+ `- \`${impact.repository}:${impact.path}\` — ${impact.observation} (${impact.mode}, ${impact.handoff})`,
+ )
+ .join("\n")
+ : "- None.",
+ "",
+ "### Stack map",
+ `- \`refresh/${report.weekKey}/${quest.slug}/01-core\``,
+ `- \`refresh/${report.weekKey}/${quest.slug}/02-docs\` when documentation synchronization is required`,
+ "",
+ "### Validation plan",
+ renderList(report.validationPlan),
+ "",
+ "### Risks and uncertainty",
+ renderList(report.risks),
+ "",
+ "### Human decisions",
+ renderList(report.humanDecisions),
+ "",
+ "### Approval",
+ "Julia may approve this exact plan by commenting `/approve-refresh`.",
+ ].join("\n");
+
+ return {
+ title: `[Refresh plan][${report.weekKey}] ${quest.title}`,
+ labels: ["refresh/plan", "refresh/awaiting-approval", questLabel(quest)],
+ body,
+ };
+}
+
+function addDerivedFields(report, quest) {
+ const normalizedReport = {
+ ...report,
+ sourceFingerprints: uniqueSorted(report.sourceFingerprints),
+ priorFindingKeys: uniqueSorted(report.priorFindingKeys),
+ };
+ const currentFindingKeys = findingKeys(normalizedReport);
+ const deduplication = {
+ parentKey: `weekly-refresh:${normalizedReport.weekKey}`,
+ childKey: `weekly-refresh:${normalizedReport.weekKey}:${quest.slug}`,
+ parentMarker: ``,
+ childMarker: ``,
+ findingKeys: currentFindingKeys,
+ carriedFindingKeys: currentFindingKeys.filter((key) =>
+ normalizedReport.priorFindingKeys.includes(key),
+ ),
+ };
+
+ return {
+ ...normalizedReport,
+ deduplication,
+ childIssuePreview: buildChildIssuePreview(
+ normalizedReport,
+ quest,
+ deduplication,
+ ),
+ };
+}
+
+function blockedCoreReport(quest, expected, reason) {
+ return {
+ schemaVersion: 1,
+ weekKey: expected.weekKey,
+ priorWeekKey: expected.priorWeekKey,
+ questSlug: quest.slug,
+ result: "blocked",
+ executiveSummary: "The discovery run did not produce a valid structured report.",
+ currentQuestStatus: "The quest status could not be assessed safely.",
+ learnerImpact: "none",
+ officialSourceDeltas: [],
+ contentDrift: [],
+ proposedChanges: [],
+ assetImpact: "",
+ codeTourImpact: [],
+ validationPlan: [],
+ risks: ["The weekly digest has incomplete evidence for this quest."],
+ humanDecisions: ["Review the blocked reason before retrying discovery."],
+ sourceFingerprints: [],
+ priorFindingKeys: [],
+ blockedReason: reason.slice(0, 2_000),
+ };
+}
+
+async function writeJson(path, value) {
+ await mkdir(dirname(path), { recursive: true });
+ await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
+}
+
+async function materialize(options) {
+ const quests = await readQuests();
+ const quest = quests.quests.find(({ slug }) => slug === options.quest);
+ if (!quest) {
+ throw new Error(`Unknown quest slug: ${options.quest}`);
+ }
+ const expected = {
+ weekKey: options.week,
+ priorWeekKey: options["prior-week"],
+ };
+ const context = weekContext(expected.weekKey);
+ if (expected.priorWeekKey !== context.priorWeekKey) {
+ throw new Error(
+ `prior week must be ${context.priorWeekKey} for ${expected.weekKey}`,
+ );
+ }
+
+ let artifact;
+ let failure;
+ try {
+ const agentOutput = JSON.parse(await readFile(options["agent-output"], "utf8"));
+ const items = (agentOutput.items ?? []).filter(
+ ({ type }) => type === "emit_weekly_report",
+ );
+ if (items.length !== 1) {
+ throw new Error(`Expected one emit_weekly_report item, received ${items.length}`);
+ }
+ const item = items[0];
+ const report = JSON.parse(item.report_json);
+ if (item.result !== report.result) {
+ throw new Error("Safe-output result does not match report_json.result");
+ }
+ const errors = validateCoreReport(report, quest, expected);
+ if (errors.length > 0) {
+ throw new Error(errors.join("; "));
+ }
+ artifact = addDerivedFields(report, quest);
+ } catch (error) {
+ failure = error;
+ artifact = addDerivedFields(
+ blockedCoreReport(quest, expected, `Report materialization failed: ${error.message}`),
+ quest,
+ );
+ }
+
+ await writeJson(resolve(options.output), artifact);
+ if (failure) {
+ throw failure;
+ }
+}
+
+function validateArtifact(report, quest, expected) {
+ const core = Object.fromEntries(coreKeys.map((key) => [key, report[key]]));
+ const errors = validateCoreReport(core, quest, expected);
+ const derived = addDerivedFields(core, quest);
+ if (stableJson(report.deduplication) !== stableJson(derived.deduplication)) {
+ errors.push("deduplication fields do not match deterministic values");
+ }
+ if (stableJson(report.childIssuePreview) !== stableJson(derived.childIssuePreview)) {
+ errors.push("childIssuePreview does not match the deterministic preview");
+ }
+ return errors;
+}
+
+function buildParentIssuePreview(reports, weekKey, priorWeekKey) {
+ const parentMarker = ``;
+ const childKeys = reports
+ .filter(({ result }) => result === "material-change")
+ .map(({ deduplication }) => deduplication.childKey);
+ const manifest = {
+ version: 1,
+ weekKey,
+ priorWeekKey,
+ parentKey: `weekly-refresh:${weekKey}`,
+ childKeys,
+ outcomes: Object.fromEntries(
+ reports.map((report) => [report.questSlug, report.result]),
+ ),
+ };
+ const table = [
+ "| Quest | Result | Learner impact | Child preview |",
+ "|---|---|---|---|",
+ ...reports.map((report) => {
+ const child =
+ report.result === "material-change"
+ ? `\`children/${report.questSlug}.md\``
+ : "None";
+ return `| ${report.questSlug} | ${report.result} | ${report.learnerImpact} | ${child} |`;
+ }),
+ ].join("\n");
+ const blocked = reports.filter(({ result }) => result === "blocked");
+ const unchanged = reports.filter(({ result }) => result === "no-material-change");
+ const body = [
+ parentMarker,
+ ``,
+ "",
+ "### Weekly summary",
+ table,
+ "",
+ "### Affected quests",
+ renderList(
+ reports
+ .filter(({ result }) => result === "material-change")
+ .map(
+ (report) =>
+ `${report.questSlug}: ${report.executiveSummary} (${report.deduplication.childKey})`,
+ ),
+ ),
+ "",
+ "### No material change",
+ renderList(
+ unchanged.map(
+ (report) => `${report.questSlug}: ${report.executiveSummary}`,
+ ),
+ ),
+ "",
+ "### Blocked scopes",
+ renderList(
+ blocked.map(
+ (report) => `${report.questSlug}: ${report.blockedReason}`,
+ ),
+ ),
+ "",
+ "### Run mode",
+ "Phase 2A staged preview only. No issue, branch, pull request, comment, schedule, or content write is enabled.",
+ ].join("\n");
+
+ return {
+ title: `[Weekly refresh][${weekKey}] Quest content digest`,
+ labels: ["refresh/plan"],
+ body,
+ };
+}
+
+async function digest(options) {
+ const quests = await readQuests();
+ const expected = {
+ weekKey: options.week,
+ priorWeekKey: options["prior-week"],
+ };
+ const context = weekContext(expected.weekKey);
+ if (expected.priorWeekKey !== context.priorWeekKey) {
+ throw new Error(
+ `prior week must be ${context.priorWeekKey} for ${expected.weekKey}`,
+ );
+ }
+ const reports = [];
+ const inputErrors = [];
+
+ for (const quest of quests.quests) {
+ const reportPath = resolve(options["reports-dir"], `${quest.slug}.json`);
+ try {
+ const report = JSON.parse(await readFile(reportPath, "utf8"));
+ const errors = validateArtifact(report, quest, expected);
+ if (errors.length > 0) {
+ throw new Error(errors.join("; "));
+ }
+ reports.push(report);
+ } catch (error) {
+ inputErrors.push(`${quest.slug}: ${error.message}`);
+ reports.push(
+ addDerivedFields(
+ blockedCoreReport(
+ quest,
+ expected,
+ `Quest artifact unavailable or invalid: ${error.message}`,
+ ),
+ quest,
+ ),
+ );
+ }
+ }
+
+ const outputDirectory = resolve(options["output-dir"]);
+ const parentIssuePreview = buildParentIssuePreview(
+ reports,
+ expected.weekKey,
+ expected.priorWeekKey,
+ );
+ const digestValue = {
+ schemaVersion: 1,
+ weekKey: expected.weekKey,
+ priorWeekKey: expected.priorWeekKey,
+ parentKey: `weekly-refresh:${expected.weekKey}`,
+ results: reports.map(({ questSlug, result }) => ({ questSlug, result })),
+ inputErrors,
+ parentIssuePreview,
+ reports,
+ };
+
+ await writeJson(resolve(outputDirectory, "digest.json"), digestValue);
+ await mkdir(resolve(outputDirectory, "children"), { recursive: true });
+ await writeFile(
+ resolve(outputDirectory, "parent-issue.md"),
+ `# ${parentIssuePreview.title}\n\nLabels: ${parentIssuePreview.labels.join(", ")}\n\n${parentIssuePreview.body}\n`,
+ );
+ for (const report of reports) {
+ if (!report.childIssuePreview) {
+ continue;
+ }
+ await writeFile(
+ resolve(outputDirectory, "children", `${report.questSlug}.md`),
+ `# ${report.childIssuePreview.title}\n\nLabels: ${report.childIssuePreview.labels.join(", ")}\n\n${report.childIssuePreview.body}\n`,
+ );
+ }
+ return digestValue;
+}
+
+function sampleOwnedPath(quest) {
+ const pattern =
+ quest.ownedPaths["Azure-Samples/JavaScript-AI-Buildathon"][0];
+ return pattern.endsWith("/**")
+ ? `${pattern.slice(0, -3)}/README.md`
+ : pattern;
+}
+
+function sampleReport(quest, expected, result) {
+ const material = result === "material-change";
+ const blocked = result === "blocked";
+ const path = sampleOwnedPath(quest);
+ return {
+ schemaVersion: 1,
+ weekKey: expected.weekKey,
+ priorWeekKey: expected.priorWeekKey,
+ questSlug: quest.slug,
+ result,
+ executiveSummary: `${quest.title} ${result} self-test result.`,
+ currentQuestStatus: blocked ? "Status unavailable." : "Current guidance inspected.",
+ learnerImpact: material ? "medium" : "none",
+ officialSourceDeltas: blocked
+ ? []
+ : [
+ {
+ fact: "Self-test fact",
+ lifecycle: "Not stated by allowed sources",
+ evidenceUrl: quest.officialSourcePrefixes[0],
+ evidenceLocation: "Self-test",
+ observedWording: "Self-test wording",
+ fingerprint: `content:${quest.slug}`,
+ },
+ ],
+ contentDrift: material
+ ? [{ path, section: "Self-test", observation: "Self-test drift" }]
+ : [],
+ proposedChanges: material
+ ? [
+ {
+ repository: "Azure-Samples/JavaScript-AI-Buildathon",
+ path,
+ description: "Apply the self-test change.",
+ },
+ ]
+ : [],
+ assetImpact: "",
+ codeTourImpact: [],
+ validationPlan: material ? ["Run self-test validation."] : [],
+ risks: blocked ? ["Self-test block."] : [],
+ humanDecisions: [],
+ sourceFingerprints: blocked ? [] : [`content:${quest.slug}`],
+ priorFindingKeys: [],
+ blockedReason: blocked ? "Self-test blocked reason." : null,
+ };
+}
+
+async function selfTest() {
+ assert.equal(weekContext("2021-W01").priorWeekKey, "2020-W53");
+ assert.equal(weekContext("2025-W01").priorWeekKey, "2024-W52");
+ assert.throws(() => weekContext("2021-W53"), /does not exist/);
+
+ const quests = await readQuests();
+ const expected = { weekKey: "2026-W30", priorWeekKey: "2026-W29" };
+ const temporaryDirectory = await mkdtemp(
+ resolve(tmpdir(), "weekly-refresh-self-test-"),
+ );
+ try {
+ const materialCore = sampleReport(
+ quests.quests[0],
+ expected,
+ "material-change",
+ );
+ materialCore.sourceFingerprints = [
+ "content:z-last",
+ materialCore.sourceFingerprints[0],
+ ];
+ materialCore.priorFindingKeys = [
+ `finding:${"b".repeat(64)}`,
+ `finding:${"a".repeat(64)}`,
+ ];
+ const agentOutputPath = resolve(temporaryDirectory, "agent-output.json");
+ const materializedPath = resolve(
+ temporaryDirectory,
+ "materialized",
+ `${quests.quests[0].slug}.json`,
+ );
+ await writeJson(agentOutputPath, {
+ items: [
+ {
+ type: "emit_weekly_report",
+ result: materialCore.result,
+ report_json: JSON.stringify(materialCore),
+ },
+ ],
+ });
+ await materialize({
+ "agent-output": agentOutputPath,
+ output: materializedPath,
+ week: expected.weekKey,
+ "prior-week": expected.priorWeekKey,
+ quest: quests.quests[0].slug,
+ });
+ const materialized = JSON.parse(await readFile(materializedPath, "utf8"));
+ assert.equal(materialized.result, "material-change");
+ assert.ok(materialized.childIssuePreview);
+ assert.deepEqual(
+ validateArtifact(materialized, quests.quests[0], expected),
+ [],
+ );
+
+ const adjacentSource = structuredClone(materialCore);
+ adjacentSource.officialSourceDeltas[0].evidenceUrl =
+ `${quests.quests[0].officialSourcePrefixes[1]}-malicious`;
+ assert.ok(
+ validateCoreReport(adjacentSource, quests.quests[0], expected).some(
+ (error) => error.includes("outside the quest source allowlist"),
+ ),
+ );
+ const traversalPath = structuredClone(materialCore);
+ traversalPath.contentDrift[0].path =
+ "01-Local-AI-Development/../.github/workflows/example.yml";
+ assert.ok(
+ validateCoreReport(traversalPath, quests.quests[0], expected).some(
+ (error) => error.includes("outside the quest ownership boundary"),
+ ),
+ );
+ const missingOutputPath = resolve(
+ temporaryDirectory,
+ "missing-agent-output.json",
+ );
+ const blockedMaterializedPath = resolve(
+ temporaryDirectory,
+ "materialized",
+ "blocked.json",
+ );
+ await writeJson(missingOutputPath, { items: [] });
+ await assert.rejects(
+ materialize({
+ "agent-output": missingOutputPath,
+ output: blockedMaterializedPath,
+ week: expected.weekKey,
+ "prior-week": expected.priorWeekKey,
+ quest: quests.quests[0].slug,
+ }),
+ /Expected one emit_weekly_report item/,
+ );
+ assert.equal(
+ JSON.parse(await readFile(blockedMaterializedPath, "utf8")).result,
+ "blocked",
+ );
+
+ for (const [index, quest] of quests.quests.entries()) {
+ const result =
+ index === 0
+ ? "material-change"
+ : index === 1
+ ? "blocked"
+ : "no-material-change";
+ const core = sampleReport(quest, expected, result);
+ assert.deepEqual(validateCoreReport(core, quest, expected), []);
+ await writeJson(
+ resolve(temporaryDirectory, "reports", `${quest.slug}.json`),
+ addDerivedFields(core, quest),
+ );
+ }
+
+ const digestValue = await digest({
+ week: expected.weekKey,
+ "prior-week": expected.priorWeekKey,
+ "reports-dir": resolve(temporaryDirectory, "reports"),
+ "output-dir": resolve(temporaryDirectory, "preview"),
+ });
+ assert.equal(digestValue.results.length, 5);
+ assert.deepEqual(digestValue.inputErrors, []);
+ assert.equal(
+ digestValue.reports.filter(({ childIssuePreview }) => childIssuePreview)
+ .length,
+ 1,
+ );
+ } finally {
+ await rm(temporaryDirectory, { recursive: true, force: true });
+ }
+ console.log("Weekly refresh report self-test passed.");
+}
+
+async function main() {
+ const { command, options } = parseArguments(process.argv.slice(2));
+ if (command === "context") {
+ const context = weekContext(options.week);
+ if (options["github-output"]) {
+ await writeFile(
+ resolve(options["github-output"]),
+ `week_key=${context.weekKey}\nprior_week_key=${context.priorWeekKey}\nparent_key=${context.parentKey}\n`,
+ { flag: "a" },
+ );
+ }
+ console.log(JSON.stringify(context));
+ } else if (command === "materialize") {
+ for (const key of ["agent-output", "output", "week", "prior-week", "quest"]) {
+ if (!options[key]) {
+ throw new Error(`materialize requires --${key}`);
+ }
+ }
+ await materialize(options);
+ } else if (command === "digest") {
+ for (const key of [
+ "reports-dir",
+ "output-dir",
+ "week",
+ "prior-week",
+ ]) {
+ if (!options[key]) {
+ throw new Error(`digest requires --${key}`);
+ }
+ }
+ await digest(options);
+ } else if (command === "validate") {
+ for (const key of ["report", "week", "prior-week", "quest"]) {
+ if (!options[key]) {
+ throw new Error(`validate requires --${key}`);
+ }
+ }
+ const quests = await readQuests();
+ const quest = quests.quests.find(({ slug }) => slug === options.quest);
+ if (!quest) {
+ throw new Error(`Unknown quest slug: ${options.quest}`);
+ }
+ const report = JSON.parse(await readFile(resolve(options.report), "utf8"));
+ const errors = validateArtifact(report, quest, {
+ weekKey: options.week,
+ priorWeekKey: options["prior-week"],
+ });
+ if (errors.length > 0) {
+ throw new Error(errors.join("; "));
+ }
+ console.log(`Validated ${options.report}`);
+ } else if (command === "self-test") {
+ await selfTest();
+ } else {
+ throw new Error(
+ "Usage: weekly-refresh-report.mjs context|materialize|digest|validate|self-test",
+ );
+ }
+}
+
+if (process.argv[1] === fileURLToPath(import.meta.url)) {
+ main().catch((error) => {
+ console.error(error.message);
+ process.exitCode = 1;
+ });
+}