diff --git a/.github/scripts/pages-report/aic-usage.mjs b/.github/scripts/pages-report/aic-usage.mjs index ea4d556..1d38d2a 100644 --- a/.github/scripts/pages-report/aic-usage.mjs +++ b/.github/scripts/pages-report/aic-usage.mjs @@ -82,13 +82,13 @@ async function mapWithConcurrency(values, concurrency, mapper) { const aic = Number(run.aic); if (!Number.isFinite(runId) || !Number.isFinite(aic)) continue; const metadata = workflowByRun.get(`${repository}:${runId}`); - const mode = metadata?.run?.displayTitle?.match(/(?:^|\s[·|:-]\s)(preview|staged|review|live)$/i)?.[1]?.toLowerCase() || null; + const mode = metadata?.run?.displayTitle?.match(/(?:^|\s[·|:-]\s)(review|live)$/i)?.[1]?.toLowerCase() || null; runs.set(`${repository}:${runId}`, { repository, runId, workflowName: run.workflow_name || run.workflow || metadata?.workflow?.name || null, workflowPath: metadata?.workflow?.path || null, - mode: mode === "preview" ? "staged" : mode, + mode, conclusion: metadata?.run?.conclusion || null, createdAt: run.created_at || run.started_at || metadata?.run?.createdAt || null, aic, diff --git a/.github/scripts/pages-report/report.mjs b/.github/scripts/pages-report/report.mjs index e585293..010ab99 100644 --- a/.github/scripts/pages-report/report.mjs +++ b/.github/scripts/pages-report/report.mjs @@ -487,8 +487,8 @@ function repositoryWorkflowTabs(repositoryName, workflow, selectedView) { } function configuredModeFor(bundle) { - const mode = repositoryVariables.get(bundle.rolloutModeVariable) || "staged"; - return normalizeMode(mode) === "unknown" ? "staged" : normalizeMode(mode); + const mode = repositoryVariables.get(bundle.rolloutModeVariable) || "review"; + return normalizeMode(mode) === "unknown" ? "review" : normalizeMode(mode); } function repositoryVariablesFromEnvironment() { @@ -506,7 +506,7 @@ function repositoryVariablesFromEnvironment() { } function modeIndicator(mode) { - const icons = { staged: "eye", review: "beaker", live: "rocket" }; + const icons = { review: "beaker", live: "rocket" }; const label = `${mode[0].toUpperCase()}${mode.slice(1)}`; return `${octicon(icons[mode])}${label}`; } @@ -625,8 +625,7 @@ const repositoryVariables = new Map([ const issueByUrl = new Map(reportSources.flatMap((source) => source.issues.map((issue) => [issue.url, issue]))); const runCache = new Map(); function normalizeMode(mode) { - if (mode === "preview") return "staged"; - return ["staged", "review", "live"].includes(mode) ? mode : "unknown"; + return ["review", "live"].includes(mode) ? mode : "unknown"; } async function metadataFromRunUrl(runUrl) { @@ -638,7 +637,7 @@ async function metadataFromRunUrl(runUrl) { runCache.set(cacheKey, githubOptional(`/repos/${runOwner}/${runRepository}/actions/runs/${runId}`, null)); } const run = await runCache.get(cacheKey); - const mode = run?.display_title?.match(/(?:^|\s[·|:-]\s)(preview|staged|review|live)$/i)?.[1]?.toLowerCase(); + const mode = run?.display_title?.match(/(?:^|\s[·|:-]\s)(review|live)$/i)?.[1]?.toLowerCase(); const workflowPath = run?.path || ""; return { mode: normalizeMode(mode), @@ -689,7 +688,7 @@ const records = (await Promise.all(discoveredRecords.map(async (record) => { const scopedRecords = allowedRepositories.size === 0 ? records : records.filter((record) => allowedRepositories.has(record.repository.toLowerCase())); -const reportRecords = scopedRecords.filter((record) => ["staged", "review", "live"].includes(record.mode)); +const reportRecords = scopedRecords.filter((record) => ["review", "live"].includes(record.mode)); await mkdir(outputDirectory, { recursive: true }); await writeFile(path.join(outputDirectory, "inventory.json"), `${JSON.stringify(inventory, null, 2)}\n`); @@ -2093,7 +2092,6 @@ tbody tr:hover { background: var(--canvas-subtle); } .status-muted { background: var(--neutral-muted); } .mode-live { border-color: color-mix(in srgb, var(--success) 45%, var(--border)); background: var(--success-muted); color: var(--success); } .mode-review { border-color: color-mix(in srgb, var(--attention) 45%, var(--border)); background: var(--attention-muted); color: var(--attention); } -.mode-staged { background: var(--neutral-muted); } .mode-indicator { min-height: 22px; display: inline-flex; flex: none; align-items: center; gap: 5px; padding: 1px 7px; border: 1px solid var(--border); border-radius: 2em; font-size: .6875rem; font-weight: 600; text-transform: none; white-space: nowrap; } .mode-indicator .octicon { width: 13px; height: 13px; flex-basis: 13px; } .sidebar-nav .mode-indicator { margin-left: auto; } diff --git a/.github/skills/create-ops-package/SKILL.md b/.github/skills/create-ops-package/SKILL.md index 2288b24..c4fcdd8 100644 --- a/.github/skills/create-ops-package/SKILL.md +++ b/.github/skills/create-ops-package/SKILL.md @@ -32,9 +32,9 @@ Create `.github/workflows/.md` with: - `name` set to the exact package display name, with no `/` suffix, and a run name that includes target and safe-output mode - a schedule when the operation is periodic, plus `workflow_dispatch` -- the standard dispatch inputs: `target_repo`, `safe_output_repo`, `max_repos`, and `safe_output_mode` with `staged`, `review`, and `live` choices +- the standard dispatch inputs: `target_repo`, `safe_output_repo`, `max_repos`, and `safe_output_mode` with `review` and `live` choices, defaulting to `review` - `shared/control.md` imported with `role: orchestrator` -- package-scoped rollout variables named `CENTRAL_AGENTIC_OPS__MODE` and `CENTRAL_AGENTIC_OPS__ROLLOUT_PERCENT`, defaulting to `staged` and `100`; shared control routes review safe outputs to a manual `safe_output_repo` override or `github.repository` +- package-scoped variables named `CENTRAL_AGENTIC_OPS__ENABLED`, `CENTRAL_AGENTIC_OPS__MODE`, and `CENTRAL_AGENTIC_OPS__ROLLOUT_PERCENT`, defaulting to `true`, `review`, and `100`; shared control routes review safe outputs to a manual `safe_output_repo` override or `github.repository` - least-privilege permissions, explicit tools/network configuration, `strict: true`, and a bounded `max-ai-credits` - `safe-outputs.dispatch-workflow.workflows` listing every worker slug and a `max` consistent with `max_repos` and worker count - a prompt headed with the package display name and containing `Discovery`, `Workers`, and `Completion` sections @@ -58,7 +58,7 @@ The orchestrator's `Completion` section must: Create at least one `.github/workflows/-.md`. Every worker must include: - `name` set to the exact ` / ` hierarchy, where `` exactly matches the orchestrator's `name` -- `workflow_dispatch` with the full control-plane envelope: `target_repo`, `safe_output_repo`, `safe_output_mode`, `preview_only`, `correlation_id`, `central_repo`, `control_plane_run_url`, and `batch_label` +- `workflow_dispatch` with the full control-plane envelope: `target_repo`, `safe_output_repo`, `safe_output_mode`, `correlation_id`, `central_repo`, `control_plane_run_url`, and `batch_label` - required `target_repo` and `safe_output_repo` string inputs - `shared/control.md` imported with `role: worker` - a stable `tracker-id` equal to its filename stem @@ -112,7 +112,7 @@ Before finishing: 2. Confirm the orchestrator `name` is exactly `` and every worker `name` is exactly ` / `. 3. Confirm the orchestrator dispatch list exactly matches the new worker stems. 4. Confirm each worker accepts the complete standard envelope and imports `shared/control.md` as `worker`. -5. Confirm the orchestrator imports `shared/control.md` as `orchestrator`, uses package-scoped rollout variables, and defaults safely to staged mode. +5. Confirm the orchestrator imports `shared/control.md` as `orchestrator`, uses package-scoped mode and enablement variables, and defaults safely to review mode. 6. Confirm the orchestrator has a `Completion` section that preserves the exact standard report contract from `shared/control.md`; package-specific reporting must be additive. 7. Confirm worker concurrency is keyed by `github.workflow` and `inputs.target_repo` with stale runs cancelled. 8. Check permissions, tools, network hosts, safe-output limits, credits, timeouts, and dispatch maximums against actual need. diff --git a/.github/workflows/advisory-package-maintainer.md b/.github/workflows/advisory-package-maintainer.md index 41745e9..bd0a10e 100644 --- a/.github/workflows/advisory-package-maintainer.md +++ b/.github/workflows/advisory-package-maintainer.md @@ -10,10 +10,10 @@ on: workflow_dispatch: inputs: safe_output_mode: - default: staged + default: review type: choice options: - - staged + - review - live checkout: @@ -40,7 +40,7 @@ network: - github - www.gov.uk -run-name: "Advisory package alignment maintenance · ${{ inputs.safe_output_mode || 'live' }}" +run-name: "Advisory package alignment maintenance · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}" @@ -56,7 +56,6 @@ tools: web-fetch: safe-outputs: - staged: ${{ github.event_name == 'workflow_dispatch' && inputs.safe_output_mode != 'live' }} create-pull-request: title-prefix: "[advisory:implementation-status] " draft: true diff --git a/.github/workflows/advisory-uk-ai-operational-resilience.md b/.github/workflows/advisory-uk-ai-operational-resilience.md index bd8a580..a4151c1 100644 --- a/.github/workflows/advisory-uk-ai-operational-resilience.md +++ b/.github/workflows/advisory-uk-ai-operational-resilience.md @@ -15,9 +15,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -28,7 +25,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -38,13 +35,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_ADVISORY_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_ADVISORY_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -73,7 +75,7 @@ network: - github - www.gov.uk -run-name: "UK AI operational resilience advisory · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "UK AI operational resilience advisory · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -89,13 +91,12 @@ tools: web-fetch: safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[advisory:uk-ai-resilience] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} noop: timeout-minutes: 30 diff --git a/.github/workflows/advisory.md b/.github/workflows/advisory.md index d62a95d..1dab965 100644 --- a/.github/workflows/advisory.md +++ b/.github/workflows/advisory.md @@ -1,7 +1,7 @@ --- name: "Advisory" -run-name: "Advisory · ${{ inputs.target_repo || 'auto' }} · ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'staged' }}" +run-name: "Advisory · ${{ inputs.target_repo || 'auto' }} · ${{ inputs.safe_output_mode || 'mode' }}" max-ai-credits: 250 timeout-minutes: 15 @@ -37,20 +37,22 @@ on: default: 0 type: number safe_output_mode: - default: "staged" + default: "review" type: choice options: - - staged - review - live env: - CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'staged') == 'preview' && 'staged' || (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'staged') }} + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_ADVISORY_ENABLED || 'true' }} + CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'staged') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_ADVISORY_MODE || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: (vars.CENTRAL_AGENTIC_OPS_ADVISORY_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: diff --git a/.github/workflows/ambient-context-agents-md-curator.md b/.github/workflows/ambient-context-agents-md-curator.md index d49efb5..df31efe 100644 --- a/.github/workflows/ambient-context-agents-md-curator.md +++ b/.github/workflows/ambient-context-agents-md-curator.md @@ -18,9 +18,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -31,7 +28,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} current: true - repository: ${{ inputs.target_repo }} @@ -40,13 +37,18 @@ checkout: fetch-depth: 0 env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -68,7 +70,7 @@ network: - defaults - github -run-name: "Ambient context AGENTS.md · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "Ambient context AGENTS.md · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -100,13 +102,12 @@ tools: - "find" safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[ambient-context:agents-md] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} timeout-minutes: 25 diff --git a/.github/workflows/ambient-context-skills-curator.md b/.github/workflows/ambient-context-skills-curator.md index 091f5ca..29afe2d 100644 --- a/.github/workflows/ambient-context-skills-curator.md +++ b/.github/workflows/ambient-context-skills-curator.md @@ -18,9 +18,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -31,7 +28,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} current: true - repository: ${{ inputs.target_repo }} @@ -40,13 +37,18 @@ checkout: fetch-depth: 0 env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -67,7 +69,7 @@ network: - defaults - github -run-name: "Ambient context skills · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "Ambient context skills · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -95,13 +97,12 @@ tools: - "find" safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[ambient-context:skills] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} timeout-minutes: 20 diff --git a/.github/workflows/ambient-context.md b/.github/workflows/ambient-context.md index b182561..76611cb 100644 --- a/.github/workflows/ambient-context.md +++ b/.github/workflows/ambient-context.md @@ -37,20 +37,22 @@ on: default: 0 type: number safe_output_mode: - default: "staged" + default: "review" type: choice options: - - staged - review - live env: - CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE || 'staged') == 'preview' && 'staged' || (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE || 'staged') }} + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED || 'true' }} + CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE || 'staged') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: (vars.CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: diff --git a/.github/workflows/aw-failures-investigator.md b/.github/workflows/aw-failures-investigator.md index 1ef5eca..b25a8df 100644 --- a/.github/workflows/aw-failures-investigator.md +++ b/.github/workflows/aw-failures-investigator.md @@ -18,9 +18,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -31,7 +28,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -41,13 +38,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_INVESTIGATOR_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_INVESTIGATOR_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_INVESTIGATOR_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_INVESTIGATOR_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -68,7 +70,7 @@ network: - defaults - github -run-name: "AW failure investigation · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "AW failure investigation · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -89,12 +91,11 @@ graders: run: .github/graders/aw-failures-investigator-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 14d title-prefix: "[aw-failures] " max: 3 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} timeout-minutes: 30 diff --git a/.github/workflows/aw-failures.md b/.github/workflows/aw-failures.md index 9d0e83f..7cdeb78 100644 --- a/.github/workflows/aw-failures.md +++ b/.github/workflows/aw-failures.md @@ -37,20 +37,22 @@ on: default: 0 type: number safe_output_mode: - default: "staged" + default: "review" type: choice options: - - staged - review - live env: - CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE || 'staged') == 'preview' && 'staged' || (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE || 'staged') }} + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_ENABLED || 'true' }} + CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE || 'staged') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: (vars.CENTRAL_AGENTIC_OPS_AW_FAILURES_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: diff --git a/.github/workflows/aw-maintenance-upgrade.md b/.github/workflows/aw-maintenance-upgrade.md index b20f4fc..0c1611c 100644 --- a/.github/workflows/aw-maintenance-upgrade.md +++ b/.github/workflows/aw-maintenance-upgrade.md @@ -18,9 +18,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -31,7 +28,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -41,13 +38,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_UPGRADE_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_UPGRADE_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_UPGRADE_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_UPGRADE_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -76,7 +78,7 @@ network: - defaults - github -run-name: "AW Maintenance upgrade · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "AW Maintenance upgrade · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -85,12 +87,11 @@ concurrency: tracker-id: aw-maintenance-upgrade safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[aw-maintenance] " max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} timeout-minutes: 30 diff --git a/.github/workflows/aw-maintenance.md b/.github/workflows/aw-maintenance.md index bfe3f57..1fdf140 100644 --- a/.github/workflows/aw-maintenance.md +++ b/.github/workflows/aw-maintenance.md @@ -37,20 +37,22 @@ on: default: 0 type: number safe_output_mode: - default: "staged" + default: "review" type: choice options: - - staged - review - live env: - CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE || 'staged') == 'preview' && 'staged' || (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE || 'staged') }} + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_ENABLED || 'true' }} + CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE || 'staged') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: (vars.CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: diff --git a/.github/workflows/dependabot-release-train-updater.md b/.github/workflows/dependabot-release-train-updater.md index 5f2f338..23e4bd0 100644 --- a/.github/workflows/dependabot-release-train-updater.md +++ b/.github/workflows/dependabot-release-train-updater.md @@ -18,9 +18,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -39,7 +36,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -49,13 +46,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -108,7 +110,7 @@ network: - opentelemetry.io - "*.opentelemetry.io" -run-name: "Dependabot release train · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "Dependabot release train · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -128,10 +130,8 @@ graders: run: .github/graders/dependabot-release-train-updater-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-pull-request: - staged: ${{ inputs.safe_output_mode == 'review' }} - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} title-prefix: "[dependabot-agent] " draft: true max: 1 @@ -248,9 +248,8 @@ safe-outputs: - "**/tests/**" - "**/__tests__/**" push-to-pull-request-branch: - staged: ${{ inputs.safe_output_mode == 'review' }} target: "*" - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} required-title-prefix: "[dependabot-agent] " max: 1 if-no-changes: ignore @@ -362,9 +361,8 @@ safe-outputs: - "**/tests/**" - "**/__tests__/**" update-pull-request: - staged: ${{ inputs.safe_output_mode == 'review' }} target: "*" - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} required-title-prefix: "[dependabot-agent] " title: true body: true @@ -372,10 +370,10 @@ safe-outputs: update-branch: true max: 1 add-comment: - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} max: 3 create-issue: - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} title-prefix: "[dependabot-agent] " expires: 14d max: 2 @@ -418,7 +416,7 @@ Read repository evidence from `target/`. Make all PR changes in the repository c In `review` mode, do not try to make the control-plane repository look like the target repository. Treat review mode as artifact-backed review, not as a control-plane pull request. If the live outcome would be `create-pull-request`, `push-to-pull-request-branch`, or `update-pull-request`, prepare a bundle directory under `/tmp/gh-aw/agent/review-bundles/dependabot-release-train-updater//` with `summary.md`, `changed-files.txt`, `validation.txt`, and any patch or bundle files you can produce safely, then call `publish_review_bundle` with that directory and create an issue or comment in `SAFE_OUTPUT_REPO` linking the intended target repository and review guidance. Files outside `/tmp/gh-aw/agent/` are not persisted to the publisher job. -Treat `target_repo`, `safe_output_mode`, `safe_output_repo`, `preview_only`, `correlation_id`, `central_repo`, and `control_plane_run_url` as the live control-plane envelope. +Treat `target_repo`, `safe_output_mode`, `safe_output_repo`, `correlation_id`, `central_repo`, and `control_plane_run_url` as the control-plane envelope. ## Validate and refine the work item diff --git a/.github/workflows/dependabot.md b/.github/workflows/dependabot.md index 911cdd6..2485936 100644 --- a/.github/workflows/dependabot.md +++ b/.github/workflows/dependabot.md @@ -37,20 +37,22 @@ on: default: 0 type: number safe_output_mode: - default: "staged" + default: "review" type: choice options: - - staged - review - live env: - CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE || 'staged') == 'preview' && 'staged' || (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE || 'staged') }} + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED || 'true' }} + CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE || 'staged') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: (vars.CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: diff --git a/.github/workflows/docs-explanatory-diagrams.md b/.github/workflows/docs-explanatory-diagrams.md index 1247e6b..dcc15bc 100644 --- a/.github/workflows/docs-explanatory-diagrams.md +++ b/.github/workflows/docs-explanatory-diagrams.md @@ -71,7 +71,7 @@ steps: re.MULTILINE, ) diagram_signal_re = re.compile( - r"(?:-->|\+--|\|\s*$|\bv\s*$|\bstaged\s*\|\s*review\s*\|\s*live\b)", + r"(?:-->|\+--|\|\s*$|\bv\s*$|\breview\s*\|\s*live\b)", re.MULTILINE, ) picture_re = re.compile(r"", re.IGNORECASE) diff --git a/.github/workflows/enterprise-canary.yml b/.github/workflows/enterprise-canary.yml index a0bd0d1..b9c9ee2 100644 --- a/.github/workflows/enterprise-canary.yml +++ b/.github/workflows/enterprise-canary.yml @@ -12,7 +12,7 @@ on: description: Canary mode required: true type: choice - options: [staged, review, live] + options: [review, live] target_repo: description: Dedicated OWNER/REPO canary target required: true @@ -27,8 +27,8 @@ on: default: false type: boolean confirmation: - description: REVIEW OWNER/REPO or LIVE OWNER/REPO; empty for staged - required: false + description: REVIEW OWNER/REPO or LIVE OWNER/REPO + required: true type: string permissions: diff --git a/.github/workflows/enterprise-stress.yml b/.github/workflows/enterprise-stress.yml index 9dc61d2..410181c 100644 --- a/.github/workflows/enterprise-stress.yml +++ b/.github/workflows/enterprise-stress.yml @@ -1,4 +1,4 @@ -name: Enterprise staged stress +name: Enterprise review stress on: workflow_dispatch: @@ -12,14 +12,18 @@ on: description: Dedicated OWNER/REPO canary target required: true type: string + safe_output_repo: + description: Dedicated private review repository + required: true + type: string runs: - description: Concurrent staged runs + description: Concurrent review runs required: true default: "3" type: choice options: [2, 3, 5] confirmation: - description: STRESS OWNER/REPO RUNS + description: STRESS OWNER/REPO REVIEW OWNER/REPO RUNS required: true type: string @@ -38,13 +42,14 @@ jobs: environment: central-agentic-ops-${{ 'stress' }} steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 - - name: Run bounded staged stress canary + - name: Run bounded review stress canary env: BUNDLE: ${{ inputs.bundle }} CONFIRMATION: ${{ inputs.confirmation }} CONTROL_REF: ${{ github.ref_name }} GH_TOKEN: ${{ secrets.GH_AW_E2E_TOKEN || github.token }} RUNS: ${{ inputs.runs }} + SAFE_OUTPUT_REPO: ${{ inputs.safe_output_repo }} TARGET_REPO: ${{ inputs.target_repo }} run: | bash tests/e2e/run-stress.sh \ No newline at end of file diff --git a/.github/workflows/eu-cra-compliance-article-14-reporting-readiness.md b/.github/workflows/eu-cra-compliance-article-14-reporting-readiness.md index e352462..be6e365 100644 --- a/.github/workflows/eu-cra-compliance-article-14-reporting-readiness.md +++ b/.github/workflows/eu-cra-compliance-article-14-reporting-readiness.md @@ -15,9 +15,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -28,7 +25,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -38,13 +35,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -77,7 +79,7 @@ network: - single-market-economy.ec.europa.eu - enisa.europa.eu -run-name: "CRA Article 14 readiness · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "CRA Article 14 readiness · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -97,13 +99,12 @@ graders: run: .github/graders/eu-cra-compliance-article-14-reporting-readiness-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[eu-cra:article-14-readiness] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} noop: timeout-minutes: 30 diff --git a/.github/workflows/eu-cra-compliance-conformity-release-evidence.md b/.github/workflows/eu-cra-compliance-conformity-release-evidence.md index 54410b6..7deaec1 100644 --- a/.github/workflows/eu-cra-compliance-conformity-release-evidence.md +++ b/.github/workflows/eu-cra-compliance-conformity-release-evidence.md @@ -15,9 +15,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -28,7 +25,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -38,13 +35,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -77,7 +79,7 @@ network: - single-market-economy.ec.europa.eu - enisa.europa.eu -run-name: "CRA conformity and release evidence · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "CRA conformity and release evidence · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -97,13 +99,12 @@ graders: run: .github/graders/eu-cra-compliance-conformity-release-evidence-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[eu-cra:conformity-release] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} noop: timeout-minutes: 30 diff --git a/.github/workflows/eu-cra-compliance-package-maintainer.md b/.github/workflows/eu-cra-compliance-package-maintainer.md index 0191b07..747def3 100644 --- a/.github/workflows/eu-cra-compliance-package-maintainer.md +++ b/.github/workflows/eu-cra-compliance-package-maintainer.md @@ -10,10 +10,10 @@ on: workflow_dispatch: inputs: safe_output_mode: - default: staged + default: review type: choice options: - - staged + - review - live checkout: @@ -44,7 +44,7 @@ network: - single-market-economy.ec.europa.eu - enisa.europa.eu -run-name: "CRA package implementation-status maintenance · ${{ inputs.safe_output_mode || 'live' }}" +run-name: "CRA package implementation-status maintenance · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}" @@ -64,7 +64,6 @@ graders: run: .github/graders/eu-cra-compliance-package-maintainer-operational-value.sh safe-outputs: - staged: ${{ github.event_name == 'workflow_dispatch' && inputs.safe_output_mode != 'live' }} create-pull-request: title-prefix: "[eu-cra:implementation-status] " draft: true diff --git a/.github/workflows/eu-cra-compliance-scope-classifier.md b/.github/workflows/eu-cra-compliance-scope-classifier.md index 68b877a..3d83755 100644 --- a/.github/workflows/eu-cra-compliance-scope-classifier.md +++ b/.github/workflows/eu-cra-compliance-scope-classifier.md @@ -15,9 +15,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -28,7 +25,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -38,13 +35,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -75,7 +77,7 @@ network: - single-market-economy.ec.europa.eu - enisa.europa.eu -run-name: "CRA scope classification · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "CRA scope classification · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -95,13 +97,12 @@ graders: run: .github/graders/eu-cra-compliance-scope-classifier-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[eu-cra:scope] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} noop: timeout-minutes: 25 diff --git a/.github/workflows/eu-cra-compliance-security-requirements-auditor.md b/.github/workflows/eu-cra-compliance-security-requirements-auditor.md index c53a081..00aad52 100644 --- a/.github/workflows/eu-cra-compliance-security-requirements-auditor.md +++ b/.github/workflows/eu-cra-compliance-security-requirements-auditor.md @@ -15,9 +15,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -28,7 +25,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -38,13 +35,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -77,7 +79,7 @@ network: - single-market-economy.ec.europa.eu - enisa.europa.eu -run-name: "CRA security requirements audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "CRA security requirements audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -97,13 +99,12 @@ graders: run: .github/graders/eu-cra-compliance-security-requirements-auditor-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[eu-cra:security-requirements] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} noop: timeout-minutes: 30 diff --git a/.github/workflows/eu-cra-compliance-supply-chain-sbom-auditor.md b/.github/workflows/eu-cra-compliance-supply-chain-sbom-auditor.md index b7b46fe..cdf6bf9 100644 --- a/.github/workflows/eu-cra-compliance-supply-chain-sbom-auditor.md +++ b/.github/workflows/eu-cra-compliance-supply-chain-sbom-auditor.md @@ -15,9 +15,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -28,7 +25,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -38,13 +35,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -78,7 +80,7 @@ network: - single-market-economy.ec.europa.eu - enisa.europa.eu -run-name: "CRA supply chain and SBOM audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "CRA supply chain and SBOM audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -98,13 +100,12 @@ graders: run: .github/graders/eu-cra-compliance-supply-chain-sbom-auditor-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[eu-cra:supply-chain-sbom] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} noop: timeout-minutes: 30 diff --git a/.github/workflows/eu-cra-compliance-vulnerability-handling-auditor.md b/.github/workflows/eu-cra-compliance-vulnerability-handling-auditor.md index 0ab54c1..0d0dd5f 100644 --- a/.github/workflows/eu-cra-compliance-vulnerability-handling-auditor.md +++ b/.github/workflows/eu-cra-compliance-vulnerability-handling-auditor.md @@ -15,9 +15,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -28,7 +25,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -38,13 +35,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -77,7 +79,7 @@ network: - single-market-economy.ec.europa.eu - enisa.europa.eu -run-name: "CRA vulnerability handling audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "CRA vulnerability handling audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -97,13 +99,12 @@ graders: run: .github/graders/eu-cra-compliance-vulnerability-handling-auditor-operational-value.sh safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 30d title-prefix: "[eu-cra:vulnerability-handling] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} noop: timeout-minutes: 30 diff --git a/.github/workflows/eu-cra-compliance.md b/.github/workflows/eu-cra-compliance.md index 493185f..24143bb 100644 --- a/.github/workflows/eu-cra-compliance.md +++ b/.github/workflows/eu-cra-compliance.md @@ -1,7 +1,7 @@ --- name: "EU CRA Advisor" -run-name: "EU CRA Advisor · ${{ inputs.target_repo || 'auto' }} · ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'staged' }}" +run-name: "EU CRA Advisor · ${{ inputs.target_repo || 'auto' }} · ${{ inputs.safe_output_mode || 'mode' }}" max-ai-credits: 200 timeout-minutes: 15 @@ -37,20 +37,22 @@ on: default: 0 type: number safe_output_mode: - default: "staged" + default: "review" type: choice options: - - staged - review - live env: - CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'staged') == 'preview' && 'staged' || (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'staged') }} + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true' }} + CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'staged') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: (vars.CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: diff --git a/.github/workflows/optimization-ai-credit-auditor.md b/.github/workflows/optimization-ai-credit-auditor.md index c954b87..76e8157 100644 --- a/.github/workflows/optimization-ai-credit-auditor.md +++ b/.github/workflows/optimization-ai-credit-auditor.md @@ -18,9 +18,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -31,7 +28,7 @@ on: type: string checkout: - - repository: ${{ inputs.safe_output_repo }} + - repository: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 fetch: ["*"] @@ -41,13 +38,18 @@ checkout: path: target env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_AUDITOR_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_AUDITOR_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_AUDITOR_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_AUDITOR_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -69,7 +71,7 @@ network: - defaults - python -run-name: "Token audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "Token audit · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -96,13 +98,12 @@ tools: max-patch-size: 51200 safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 3d title-prefix: "[optimization:ai-credit-auditor] " max: 1 close-older-issues: true - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} upload-artifact: max-uploads: 5 retention-days: 14 @@ -110,7 +111,6 @@ safe-outputs: allowed-paths: - "/tmp/gh-aw/token-audit/charts/**" upload-asset: - staged: ${{ inputs.safe_output_mode == 'review' }} #target-repo: ${{ env.SAFE_OUTPUT_REPO }} Does not compile with this, this is a bug allowed-exts: [.png, .jpg, .jpeg, .svg] max: 5 @@ -211,7 +211,7 @@ Read target-repository evidence from `target/`. Treat the workspace root as the In `live`, the workspace root may be the target repository itself. In `review`, the workspace root is the control-plane repository. -Recreate safe outputs there without pretending the control-plane repo is the target repo: keep issues as issues, prefer `upload_artifact` for charts and other audit evidence, and use `upload_asset` only when you need a persistent URL outside staged mode. +Recreate safe outputs there without pretending the control-plane repo is the target repo: keep issues as issues, prefer `upload_artifact` for charts and other audit evidence, and use `upload_asset` only when you need a persistent URL. ## Mission diff --git a/.github/workflows/optimization-ai-credit-optimizer.md b/.github/workflows/optimization-ai-credit-optimizer.md index d582137..5b21a47 100644 --- a/.github/workflows/optimization-ai-credit-optimizer.md +++ b/.github/workflows/optimization-ai-credit-optimizer.md @@ -18,9 +18,6 @@ on: type: string safe_output_mode: type: string - preview_only: - default: "true" - type: string correlation_id: type: string central_repo: @@ -31,13 +28,18 @@ on: type: string env: + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED || 'true' }} CENTRAL_AGENTIC_OPS_WORKER_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_OPTIMIZER_ENABLED || 'true' }} - CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_OPTIMIZER_MAX_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'staged' }} + CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_OPTIMIZER_MAX_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ inputs.safe_output_mode == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: >- + (vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED || 'true') == 'true' && + (vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_OPTIMIZER_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: @@ -60,7 +62,7 @@ network: - defaults - github -run-name: "AI Credit Optimizer · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || (inputs.preview_only == 'true' && 'staged' || 'live') }}" +run-name: "AI Credit Optimizer · ${{ inputs.target_repo }} · ${{ inputs.safe_output_mode || 'review' }}" concurrency: group: "${{ github.workflow }}-${{ inputs.target_repo }}" @@ -86,13 +88,12 @@ tools: max-patch-size: 51200 safe-outputs: - staged: ${{ inputs.preview_only == 'true' }} create-issue: expires: 7d title-prefix: "[optimization:ai-credit-optimizer] " close-older-issues: true max: 1 - target-repo: ${{ github.event.inputs.safe_output_repo }} + target-repo: ${{ (inputs.safe_output_mode || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || inputs.target_repo }} threat-detection: false timeout-minutes: 30 diff --git a/.github/workflows/optimization.md b/.github/workflows/optimization.md index 656bdab..c58b76f 100644 --- a/.github/workflows/optimization.md +++ b/.github/workflows/optimization.md @@ -37,20 +37,22 @@ on: default: 0 type: number safe_output_mode: - default: "staged" + default: "review" type: choice options: - - staged - review - live env: - CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE || 'staged' }} - GH_AW_SAFE_OUTPUT_MODE: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE || 'staged') == 'preview' && 'staged' || (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE || 'staged') }} + CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED || 'true' }} + CENTRAL_AGENTIC_OPS_MODE: ${{ vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE || 'review' }} + GH_AW_SAFE_OUTPUT_MODE: ${{ inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE || 'review' }} REVIEW_OUTPUT_REPO: ${{ inputs.safe_output_repo || github.repository }} - SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE || 'staged') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} + SAFE_OUTPUT_REPO: ${{ (inputs.safe_output_mode || vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE || 'review') == 'review' && (inputs.safe_output_repo || github.repository) || '' }} TARGET_REPO: ${{ inputs.target_repo || '' }} +if: (vars.CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED || 'true') == 'true' + imports: - uses: shared/control.md with: diff --git a/.github/workflows/staged-smoke.yml b/.github/workflows/review-smoke.yml similarity index 56% rename from .github/workflows/staged-smoke.yml rename to .github/workflows/review-smoke.yml index 28ea1ea..f40de00 100644 --- a/.github/workflows/staged-smoke.yml +++ b/.github/workflows/review-smoke.yml @@ -1,4 +1,4 @@ -name: Staged smoke +name: Review smoke on: workflow_dispatch: @@ -15,27 +15,38 @@ on: description: Explicit OWNER/REPO canary target required: true type: string + safe_output_repo: + description: Dedicated private review repository + required: true + type: string + confirmation: + description: REVIEW OWNER/REPO + required: true + type: string permissions: actions: write contents: read concurrency: - group: staged-smoke-${{ inputs.bundle }}-${{ inputs.target_repo }} + group: review-smoke-${{ inputs.bundle }}-${{ inputs.target_repo }} cancel-in-progress: false jobs: smoke: runs-on: ubuntu-latest timeout-minutes: 75 + environment: central-agentic-ops-review steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 - - name: Dispatch, monitor, and verify staged canary + - name: Dispatch, monitor, and verify review canary env: - GH_TOKEN: ${{ github.token }} BUNDLE: ${{ inputs.bundle }} + CONFIRMATION: ${{ inputs.confirmation }} CONTROL_REF: ${{ github.ref_name }} - SAFE_OUTPUT_MODE: staged + GH_TOKEN: ${{ secrets.GH_AW_E2E_TOKEN || github.token }} + SAFE_OUTPUT_MODE: review + SAFE_OUTPUT_REPO: ${{ inputs.safe_output_repo }} TARGET_REPO: ${{ inputs.target_repo }} run: | bash tests/e2e/run-canary.sh \ No newline at end of file diff --git a/.github/workflows/shared/control-precompute.md b/.github/workflows/shared/control-precompute.md index 279fc2d..8c7af73 100644 --- a/.github/workflows/shared/control-precompute.md +++ b/.github/workflows/shared/control-precompute.md @@ -45,13 +45,10 @@ import-schema: default: "100" safe_output_mode: type: string - default: "staged" + default: "review" safe_output_repo: type: string default: "" - preview_only: - type: string - default: "true" enabled: type: string default: "true" @@ -60,7 +57,7 @@ import-schema: default: "true" worker_max_mode: type: string - default: "staged" + default: "review" correlation_id: type: string default: "" @@ -105,7 +102,6 @@ steps: ROLLOUT_PERCENT: ${{ github.aw.import-inputs.rollout_percent }} SAFE_OUTPUT_MODE: ${{ github.aw.import-inputs.safe_output_mode }} SAFE_OUTPUT_REPO: ${{ github.aw.import-inputs.safe_output_repo }} - PREVIEW_ONLY: ${{ github.aw.import-inputs.preview_only }} ENABLED: ${{ github.aw.import-inputs.enabled }} WORKER_ENABLED: ${{ github.aw.import-inputs.worker_enabled }} WORKER_MAX_MODE: ${{ github.aw.import-inputs.worker_max_mode }} @@ -118,9 +114,19 @@ steps: run: | set -euo pipefail mkdir -p /tmp/gh-aw/agent + OUT=/tmp/gh-aw/agent/control-precompute.json write_precompute() { - cp /tmp/gh-aw/agent/control-precompute.json /tmp/gh-aw/agent/dispatch-precompute.json + cp "$OUT" /tmp/gh-aw/agent/dispatch-precompute.json + } + + write_disabled_precompute() { + jq -n \ + --arg r "$ROLE" --arg b "$BUNDLE" \ + '{control_role:$r,bundle:$b,enabled:"false",effective_max_repos:0, + repo_error:"package disabled by its control-plane kill switch",candidate_repositories:[],worker_workflows:[]}' \ + > "$OUT" + write_precompute } write_worker_precompute() { @@ -132,43 +138,37 @@ steps: --arg target_repo "$TARGET_REPO" \ --arg safe_output_mode "$SAFE_OUTPUT_MODE" \ --arg safe_output_repo "$SAFE_OUTPUT_REPO" \ - --arg preview_only "$PREVIEW_ONLY" \ --arg correlation_id "$CORRELATION_ID" \ --arg central_repo "$CENTRAL_REPO" \ --arg control_plane_run_url "$CONTROL_PLANE_RUN_URL" \ - '{ - control_role: "worker", - bundle: $bundle, - enabled: $enabled, - worker_enabled: $worker_enabled, - worker_max_mode: $worker_max_mode, - target_repo: $target_repo, - safe_output_mode: $safe_output_mode, - safe_output_repo: $safe_output_repo, - preview_only: $preview_only, - correlation_id: $correlation_id, - central_repo: $central_repo, - control_plane_run_url: $control_plane_run_url, - candidate_repositories: [], - worker_workflows: [] - }' > /tmp/gh-aw/agent/control-precompute.json + '{control_role:"worker",$bundle,$enabled,$worker_enabled,$worker_max_mode, + $target_repo,$safe_output_mode,$safe_output_repo, + $correlation_id,$central_repo,$control_plane_run_url, + candidate_repositories:[],worker_workflows:[]}' > "$OUT" write_precompute } mode_rank() { case "$1" in - staged) printf '0\n' ;; - review) printf '1\n' ;; - live) printf '2\n' ;; - *) echo "$2 must be staged, review, or live" >&2; exit 1 ;; + review) printf '0\n' ;; + live) printf '1\n' ;; + *) echo "$2 must be review or live" >&2; exit 1 ;; esac } + repository_equal() { + awk 'BEGIN { exit(tolower(ARGV[1]) != tolower(ARGV[2])) }' "$1" "$2" + } + validate_worker_dispatch() { local requested_rank local maximum_rank local control_run_id + if [ -z "$TARGET_REPO" ]; then + echo "worker target_repo is required" >&2 + exit 1 + fi case "$WORKER_ENABLED" in true) ;; false) echo "worker is disabled by its control-plane policy" >&2; exit 1 ;; @@ -182,16 +182,11 @@ steps: exit 1 fi - if { [ "$SAFE_OUTPUT_MODE" = "staged" ] && [ "$PREVIEW_ONLY" != "true" ]; } || \ - { [ "$SAFE_OUTPUT_MODE" != "staged" ] && [ "$PREVIEW_ONLY" != "false" ]; }; then - echo "preview_only is inconsistent with safe_output_mode" >&2 - exit 1 - fi if [ "$CENTRAL_REPO" != "$GITHUB_REPOSITORY" ]; then echo "central_repo must identify the current control repository" >&2 exit 1 fi - if ! [[ "$CORRELATION_ID" =~ ^[0-9]+-[0-9]+$ ]]; then + if ! [[ "$CORRELATION_ID" =~ ^[1-9][0-9]*-[1-9][0-9]*$ ]]; then echo "correlation_id must identify an orchestrator run and attempt" >&2 exit 1 fi @@ -237,17 +232,24 @@ steps: echo "bundles.$BUNDLE.authority must use owner/repository form" >&2 exit 1 fi - if [ "$(printf '%s' "$authority" | tr '[:upper:]' '[:lower:]')" != \ - "$(printf '%s' "$CENTRAL_REPO" | tr '[:upper:]' '[:lower:]')" ]; then + if ! repository_equal "$authority" "$CENTRAL_REPO"; then echo "target assigns live authority for $BUNDLE to a different control repository" >&2 exit 1 fi } - validate_review_destination() { + validate_output_destination() { local is_private - [ "$SAFE_OUTPUT_MODE" != "review" ] && return + if [ "$SAFE_OUTPUT_MODE" = "live" ]; then + [ "$ROLE" != "worker" ] || repository_equal "$SAFE_OUTPUT_REPO" "$TARGET_REPO" || { + echo "live worker safe_output_repo must equal target_repo" >&2; exit 1; + } + return + fi + ! repository_equal "$SAFE_OUTPUT_REPO" "$TARGET_REPO" || { + echo "review safe_output_repo must differ from target_repo" >&2; exit 1; + } if ! is_private=$(gh api "repos/$SAFE_OUTPUT_REPO" --jq '.private'); then echo "review safe_output_repo must be accessible" >&2 exit 1 @@ -550,7 +552,6 @@ steps: --arg rollout_percent "$ROLLOUT_PERCENT" \ --arg safe_output_mode "$SAFE_OUTPUT_MODE" \ --arg safe_output_repo "$SAFE_OUTPUT_REPO" \ - --arg preview_only "$PREVIEW_ONLY" \ --arg orchestrator_credits "$ORCHESTRATOR_CREDITS" \ --arg worker_credits_per_target "$WORKER_CREDITS_PER_TARGET" \ --arg aggregate_credit_limit "$AGGREGATE_CREDIT_LIMIT" \ @@ -561,22 +562,15 @@ steps: --slurpfile workflows /tmp/gh-aw/agent/workflows.json \ --slurpfile candidates /tmp/gh-aw/agent/current-batch.json ' def worker_match($worker): - $workflows[0] - | map(select(.path == (".github/workflows/" + $worker + ".lock.yml"))) - | .[0]; + $workflows[0] | map(select(.path == (".github/workflows/" + $worker + ".lock.yml"))) | .[0]; + $inventory_metadata[0] as $m | $candidates[0] as $c | { - control_role: "orchestrator", - enabled: $enabled, - target_repo: $target_repo, - organization: $organization, - max_repos: $max_repos, - max_scan_repos: $max_scan_repos, - dispatch_max: $dispatch_max, - rollout_percent: $rollout_percent, + control_role:"orchestrator", $enabled, $target_repo, $organization, + $max_repos, $max_scan_repos, $dispatch_max, $rollout_percent, effective_max_repos: ( - (if ($candidates[0] | length) == 0 then 0 - else [1, (($candidates[0] | length) * ($rollout_percent | tonumber) / 100 | ceil)] | max + (if ($c | length) == 0 then 0 + else [1, (($c | length) * ($rollout_percent | tonumber) / 100 | ceil)] | max end) as $percent_cap | (if ($worker_credits_per_target | tonumber) == 0 then ($max_repos | tonumber) elif ($aggregate_credit_limit | tonumber) <= ($orchestrator_credits | tonumber) then 0 @@ -584,36 +578,25 @@ steps: end) as $credit_cap | [($max_repos | tonumber), $percent_cap, $credit_cap] | min ), - orchestrator_credits: ($orchestrator_credits | tonumber), - worker_credits_per_target: ($worker_credits_per_target | tonumber), - aggregate_credit_limit: ($aggregate_credit_limit | tonumber), - safe_output_mode: $safe_output_mode, - safe_output_repo: $safe_output_repo, - preview_only: $preview_only, - repo_source: $repo_source, - repo_error: $repo_error, - inventory_version: $inventory_metadata[0].inventory_version, - inventory_repository_count: $inventory_metadata[0].inventory_repository_count, - cell_count: $inventory_metadata[0].cell_count, - cell_index: $inventory_metadata[0].cell_index, - cell_repository_count: $inventory_metadata[0].cell_repository_count, - batch_size: $inventory_metadata[0].batch_size, - batch_index: $inventory_metadata[0].batch_index, - batch_count: $inventory_metadata[0].batch_count, - batch_id: $inventory_metadata[0].batch_id, - total_repositories_scanned: $inventory_metadata[0].inventory_repository_count, - candidate_repositories: $candidates[0], + orchestrator_credits:($orchestrator_credits | tonumber), + worker_credits_per_target:($worker_credits_per_target | tonumber), + aggregate_credit_limit:($aggregate_credit_limit | tonumber), + $safe_output_mode, $safe_output_repo, $repo_source, $repo_error, + inventory_version:$m.inventory_version, + inventory_repository_count:$m.inventory_repository_count, + cell_count:$m.cell_count, cell_index:$m.cell_index, + cell_repository_count:$m.cell_repository_count, + batch_size:$m.batch_size, batch_index:$m.batch_index, + batch_count:$m.batch_count, batch_id:$m.batch_id, + total_repositories_scanned:$m.inventory_repository_count, + candidate_repositories:$c, worker_workflows: [ $workers[0][] as $worker | (worker_match($worker)) as $match | { - configured: $worker, - matched: ($match != null), - id: $match.id, - name: $match.name, - path: $match.path, - state: ($match.state // ""), - eligible: (($match != null) and (($match.state // "") | startswith("disabled") | not)), + configured:$worker, matched:($match != null), + id:$match.id, name:$match.name, path:$match.path, state:($match.state // ""), + eligible:(($match != null) and (($match.state // "") | startswith("disabled") | not)), skip_reason: ( if $match == null then "worker workflow unavailable" elif (($match.state // "") | startswith("disabled")) then "worker workflow disabled" @@ -627,25 +610,33 @@ steps: | ($result.worker_workflows | map(select(.eligible)) | length) as $eligible_workers | $result | .effective_max_repos = ( - if $eligible_workers == 0 then 0 - else [.effective_max_repos, (($dispatch_max | tonumber) / $eligible_workers | floor)] | min + if $eligible_workers == 0 then 0 else + [.effective_max_repos, (($dispatch_max | tonumber) / $eligible_workers | floor)] | min end ) - ' > /tmp/gh-aw/agent/control-precompute.json + ' > "$OUT" write_precompute } + case "$ENABLED" in + true) ;; + false) write_disabled_precompute; exit 0 ;; + *) echo "enabled must be true or false" >&2; exit 1 ;; + esac + + mode_rank "$SAFE_OUTPUT_MODE" "safe_output_mode" >/dev/null validate_repository_owner "target_repo" "$TARGET_REPO" validate_repository_owner "safe_output_repo" "$SAFE_OUTPUT_REPO" - validate_review_destination if [ "$ROLE" = "worker" ]; then validate_worker_dispatch + validate_output_destination validate_live_authority write_worker_precompute exit 0 fi + validate_output_destination prepare_allowlist write_orchestrator_precompute --- diff --git a/.github/workflows/shared/control.md b/.github/workflows/shared/control.md index 4d91e9f..ab6a139 100644 --- a/.github/workflows/shared/control.md +++ b/.github/workflows/shared/control.md @@ -8,8 +8,12 @@ import-schema: options: [orchestrator, worker] required: true rollout_mode: + type: choice + options: [review, live] + default: "review" + package_enabled: type: string - default: "staged" + default: "true" rollout_percent: type: string default: "100" @@ -45,7 +49,7 @@ import-schema: default: "true" worker_max_mode: type: string - default: "staged" + default: "review" orchestrator_credits: type: string default: "0" @@ -83,10 +87,9 @@ imports: rollout_percent: "${{ github.aw.import-inputs.rollout_percent }}" safe_output_mode: ${{ env.GH_AW_SAFE_OUTPUT_MODE }} safe_output_repo: ${{ env.SAFE_OUTPUT_REPO }} - preview_only: ${{ (env.GH_AW_SAFE_OUTPUT_MODE == 'live' || env.GH_AW_SAFE_OUTPUT_MODE == 'review') && 'false' || 'true' }} - enabled: ${{ github.event_name == 'workflow_dispatch' || env.CENTRAL_AGENTIC_OPS_MODE == 'staged' || env.CENTRAL_AGENTIC_OPS_MODE == 'review' || env.CENTRAL_AGENTIC_OPS_MODE == 'live' }} + enabled: ${{ env.CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED || github.aw.import-inputs.package_enabled }} worker_enabled: ${{ env.CENTRAL_AGENTIC_OPS_WORKER_ENABLED || 'true' }} - worker_max_mode: ${{ env.CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE || 'staged' }} + worker_max_mode: ${{ env.CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE || 'review' }} correlation_id: ${{ github.event.inputs.correlation_id || '' }} central_repo: ${{ github.event.inputs.central_repo || '' }} control_plane_run_url: ${{ github.event.inputs.control_plane_run_url || '' }} @@ -95,9 +98,9 @@ imports: aggregate_credit_limit: "${{ github.aw.import-inputs.aggregate_credit_limit }}" --- -Read `/tmp/gh-aw/agent/control-precompute.json` before making control decisions. Treat it as authoritative for `control_role`, enablement state, target repository inputs, safe-output routing, and worker workflow availability. +Read `/tmp/gh-aw/agent/control-precompute.json` before making control decisions. Treat it as authoritative for `control_role`, package enablement state, target repository inputs, safe-output routing, and worker workflow availability. -If `control_role` is `worker`, this workflow is a dispatched worker. Do not select repositories and do not dispatch workflows. Use the importing workflow's mission instructions, and treat `target_repo`, `safe_output_mode`, `safe_output_repo`, `preview_only`, `correlation_id`, `central_repo`, and `control_plane_run_url` as the standard control-plane envelope. When `correlation_id` is present, include a short `### Control Plane` section in safe-output issues, pull requests, or comments with the correlation ID, central repository, and control plane run URL. Safe outputs are created in `SAFE_OUTPUT_REPO`. +If `control_role` is `worker`, this workflow is a dispatched worker. Do not select repositories and do not dispatch workflows. Use the importing workflow's mission instructions, and treat `target_repo`, `safe_output_mode`, `safe_output_repo`, `correlation_id`, `central_repo`, and `control_plane_run_url` as the standard control-plane envelope. When `correlation_id` is present, include a short `### Control Plane` section in safe-output issues, pull requests, or comments with the correlation ID, central repository, and control plane run URL. Safe outputs are created in `SAFE_OUTPUT_REPO`. When `target_repo` is present, prefer a dedicated `target/` checkout when the importing workflow provides one. Treat that checkout as the authoritative target-repository snapshot for analysis, and treat the workspace root as the repository where safe outputs land. In `review` mode, do not treat `SAFE_OUTPUT_REPO` as a live substitute for the target repository. Instead, prefer an artifact-backed review bundle in `SAFE_OUTPUT_REPO` for target-bound outputs that would otherwise mutate target git state. Use the same safe-output primitive only when gh-aw natively supports that primitive against the review repository; otherwise publish a clearly labeled review bundle that identifies the target repository, intended safe-output primitive, base branch when known, and the key evidence needed for human review. @@ -111,11 +114,11 @@ In `review` mode, built-in safe outputs operate against `SAFE_OUTPUT_REPO`. Neve If `control_role` is `orchestrator`, filter and prioritize target repositories, then dispatch the configured worker workflows. -Use the `enabled`, `inventory_version`, `batch_id`, `max_repos`, `rollout_percent`, `effective_max_repos`, `safe_output_mode`, `safe_output_repo`, and `preview_only` fields from `/tmp/gh-aw/agent/control-precompute.json`; do not infer those values from workflow inputs. +Use the `enabled`, `inventory_version`, `batch_id`, `max_repos`, `rollout_percent`, `effective_max_repos`, `safe_output_mode`, and `safe_output_repo` fields from `/tmp/gh-aw/agent/control-precompute.json`; do not infer those values from workflow inputs. For orchestrators, use the importing package's `Discovery` and `Workers` sections only for ranking, prioritization, and deciding whether a precomputed candidate is useful for this package. -- If `enabled` is not `true`, do not select repositories or dispatch workers. Call `report_incomplete` explaining that the package is installed but not enabled; set its rollout-mode variable to `staged`, `review`, or `live` after configuration and manual testing. +- If `enabled` is not `true`, do not select repositories or dispatch workers. Call `report_incomplete` explaining that the package is disabled by its package kill switch. - If `repo_error` is non-empty, select no repositories and dispatch no workers. Call `report_incomplete` with the precomputed error; do not retry discovery, fall back to inferred inventory, or wait for an API rate limit to reset. Continue with the repository targeting and workflow dispatch steps below. @@ -143,7 +146,6 @@ Continue with the repository targeting and workflow dispatch steps below. - `target_repo`: selected target repository - `safe_output_mode`: `safe_output_mode` - `safe_output_repo`: `effective_safe_output_repo` - - `preview_only`: `preview_only` - `correlation_id`: `${{ github.run_id }}-${{ github.run_number }}` - `central_repo`: `${{ github.repository }}` - `control_plane_run_url`: `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` @@ -162,7 +164,7 @@ Continue with the repository targeting and workflow dispatch steps below. - Selected targets: - Safe output mode: - Safe output repository: - - Staged outputs: + - Target changes allowed: ### Repository Decisions - Selected: diff --git a/README.md b/README.md index d2becba..0218394 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Run trusted agentic operations across your repository fleet from one private con Central Agentic Ops packages reusable orchestrators and focused workers so platform teams can automate repository operations without copying workflows into every repository. - **Reach more repositories:** deterministically discover and batch inventories of 100,000+ repositories while keeping each run bounded. -- **Roll out with confidence:** start in `staged`, review proposed outcomes, and promote each package independently. +- **Roll out with confidence:** review proposed outcomes away from the target, then promote each package independently to `live`. - **Keep work accountable:** every worker stays scoped to one repository and links its outcome to the originating control-plane run. ## Documentation diff --git a/advisory/README.md b/advisory/README.md index e74efb5..b06ea42 100644 --- a/advisory/README.md +++ b/advisory/README.md @@ -27,13 +27,14 @@ gh aw add-wizard githubnext/central-agentic-ops/advisory@ Configure the shared GitHub App or PAT described in the [authentication guide](../docs/authentication.md). Start with one representative repository and: -- `CENTRAL_AGENTIC_OPS_ADVISORY_MODE=staged` +- `CENTRAL_AGENTIC_OPS_ADVISORY_ENABLED=true` +- `CENTRAL_AGENTIC_OPS_ADVISORY_MODE=review` - `CENTRAL_AGENTIC_OPS_ADVISORY_MAX_REPOS=1` - `CENTRAL_AGENTIC_OPS_ADVISORY_ROLLOUT_PERCENT=100` - `CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_ENABLED=true` -- `CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_MAX_MODE=staged` +- `CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_MAX_MODE=review` -Run the **Advisory** workflow manually with an explicit `target_repo`, `max_repos` set to `1`, and `safe_output_mode` set to `staged`. Review repository selection, the worker's staged issue, source accessibility, sensitive-data handling, and control-plane correlation before promoting to `review` or `live`. +Run the **Advisory** workflow manually with an explicit `target_repo`, `max_repos` set to `1`, and `safe_output_mode` set to `review`. Review repository selection, the worker's review issue, source accessibility, sensitive-data handling, and control-plane correlation before promoting to `live`. ## Safety Boundaries diff --git a/ambient-context/README.md b/ambient-context/README.md index 13847ae..e10c9b2 100644 --- a/ambient-context/README.md +++ b/ambient-context/README.md @@ -53,29 +53,30 @@ To install only this package into an existing private control repository: gh aw add githubnext/central-agentic-ops/ambient-context@ ``` -The package is left in `staged` mode. Configure `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` before it can act. +The package is immediately runnable in `review` mode. Proposals are written to the control repository without changing the target. ## Configure | Setting | Type | Required | Purpose | | --- | --- | --- | --- | -| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` | Repository variable | Yes | Package mode: `staged`, `review`, or `live`. Defaults to `staged`. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED` | Repository variable | No | Package kill switch; defaults to `true`. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` | Repository variable | No | Package output mode: `review` or `live`. Defaults to `review`. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MAX_REPOS` | Repository variable | No | Scheduled selection cap; defaults to `1`. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ROLLOUT_PERCENT` | Repository variable | No | Percentage of discovered repositories eligible for selection. Accepts `1` through `100` and defaults to `100`. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_ENABLED` | Repository variable | No | `AGENTS.md` curator kill switch; defaults to `true`. | -| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_MAX_MODE` | Repository variable | No | `AGENTS.md` curator mode ceiling; defaults to `staged`. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_MAX_MODE` | Repository variable | No | `AGENTS.md` curator mode ceiling; defaults to `review`. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_ENABLED` | Repository variable | No | Skills curator kill switch; defaults to `true`. | -| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_MAX_MODE` | Repository variable | No | Skills curator mode ceiling; defaults to `staged`. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_MAX_MODE` | Repository variable | No | Skills curator mode ceiling; defaults to `review`. | Shared control-plane settings — `GH_AW_GITHUB_APP_ID`, `GH_AW_GITHUB_APP_PRIVATE_KEY`, `GH_AW_GITHUB_TOKEN`, `CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS`, `CENTRAL_AGENTIC_OPS_MAX_SCAN_REPOS`, cell and batch variables, and `CENTRAL_AGENTIC_OPS_MAX_AI_CREDITS_PER_RUN` — behave exactly as they do for the core packages. See the [configuration reference](../docs/configuration.md) and the [authentication guide](../docs/authentication.md). -## Validate in staged mode +## Validate in review mode 1. Open the generated **Ambient Context** workflow in the control repository's **Actions** tab. 2. Select **Run workflow**. 3. Set `target_repo` to one fully qualified `owner/repository` name that has a root `AGENTS.md`. -4. Keep `max_repos` at `1` and `safe_output_mode` at `staged`. -5. Inspect repository selection, the dispatched workers, the staged issue bodies, and the agentic prompts they contain before promoting the package. +4. Keep `max_repos` at `1` and `safe_output_mode` at `review`. +5. Inspect repository selection, the dispatched workers, and the review issues and agentic prompts in the control repository before promoting the package. Repeat with a repository that has no `AGENTS.md` and confirm that it is reported as skipped and that no worker produces an issue. @@ -83,11 +84,10 @@ Repeat with a repository that has no `AGENTS.md` and confirm that it is reported | Mode | Behavior | | --- | --- | -| `staged` | Safe outputs are generated without GitHub API writes. | | `review` | Issues are routed to the control-plane repository; manual runs may override it with `safe_output_repo`. | | `live` | Issues are created in the selected target repository. | -Promote in order: one-repository staged, private review, limited live, then scheduled live. +Promote in order: one-repository review, limited live, then scheduled live. ## Cadence @@ -129,7 +129,7 @@ The skills curator has no evaluator. It would have to claim the same merged pull ## Pause or Stop -Set `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` to `staged` to put future scheduled runs in staged mode. Clearing the mode stops scheduled selection and worker dispatch. For a control-plane-wide stop, follow the [emergency-stop procedure](../docs/operations.md#emergency-stop). +Set `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED` to `false` and cancel active runs. Re-enable in `review` mode after resolving the incident. For a control-plane-wide stop, follow the [emergency-stop procedure](../docs/operations.md#emergency-stop). ## More Information diff --git a/dependabot/README.md b/dependabot/README.md index 97c961e..36794b4 100644 --- a/dependabot/README.md +++ b/dependabot/README.md @@ -32,46 +32,47 @@ Install the package into a new private control repository owned by an organizati gh aw add-wizard githubnext/central-agentic-ops/dependabot@ ``` -The installer configures authentication and creates the package controls. It leaves the package in `staged` mode. +The installer configures authentication and creates the package controls. The package is immediately runnable in `review` mode. ## Configure -Configure a GitHub App, a fine-grained PAT, or both in the control repository for private targets or live operation. App authentication is preferred. A bounded staged scan of public repositories can use the automatically provided `GITHUB_TOKEN` without either configured credential. +Configure a GitHub App, a fine-grained PAT, or both in the control repository for private targets, alternate review repositories, or live operation. App authentication is preferred. A bounded review run against a public target can use the automatically provided `GITHUB_TOKEN` when outputs stay in the private control repository. | Setting | Type | Required | Purpose | | --- | --- | --- | --- | | `GH_AW_GITHUB_APP_ID` | Repository variable | With App authentication | GitHub App client ID. | | `GH_AW_GITHUB_APP_PRIVATE_KEY` | Repository secret | With App authentication | GitHub App private key. | -| `GH_AW_GITHUB_TOKEN` | Repository secret | For cross-repository access without a complete App configuration | Fine-grained PAT fallback; not required for public staged scans. | +| `GH_AW_GITHUB_TOKEN` | Repository secret | For cross-repository access without a complete App configuration | Fine-grained PAT fallback; not required for public targets reviewed in the control repository. | | `CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS` | Repository variable | No | Comma-separated permitted owners; defaults to the control repository owner. | | `CENTRAL_AGENTIC_OPS_MAX_SCAN_REPOS` | Repository variable | No | Bounded discovery size; defaults to `1000` and cannot exceed `100000`. | | `CENTRAL_AGENTIC_OPS_CELL_COUNT` / `CENTRAL_AGENTIC_OPS_CELL_INDEX` | Repository variables | No | Deterministically select one inventory cell; defaults to cell `0` of `1`. | | `CENTRAL_AGENTIC_OPS_BATCH_SIZE` / `CENTRAL_AGENTIC_OPS_BATCH_INDEX` | Repository variables | No | Select one bounded batch within the cell; defaults to batch `0` with size `100000`. | -| `CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE` | Repository variable | Yes | Package mode: `staged`, `review`, or `live`. Defaults to `staged`. | +| `CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED` | Repository variable | No | Package kill switch; defaults to `true`. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE` | Repository variable | No | Package output mode: `review` or `live`. Defaults to `review`. | | `CENTRAL_AGENTIC_OPS_DEPENDABOT_MAX_REPOS` | Repository variable | No | Scheduled selection cap; defaults to `1`. | | `CENTRAL_AGENTIC_OPS_DEPENDABOT_ROLLOUT_PERCENT` | Repository variable | No | Percentage of discovered repositories eligible for selection. Accepts `1` through `100` and defaults to `100`. | | `CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_ENABLED` | Repository variable | No | Worker kill switch; defaults to `true`. | -| `CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_MAX_MODE` | Repository variable | No | Worker mode ceiling; defaults to `staged`. | +| `CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_MAX_MODE` | Repository variable | No | Worker mode ceiling; defaults to `review`. | | `CENTRAL_AGENTIC_OPS_MAX_AI_CREDITS_PER_RUN` | Repository variable | No | Aggregate orchestration ceiling; defaults to `1100`. | | `GH_AW_CI_TOKEN` | Repository secret | Optional | Supports the updater path that requires an additional empty commit. | -The App installation or PAT must cover every private or internal target, alternate review repository, and live target the package needs to read or update. Public staged scans may use `GITHUB_TOKEN`, but unavailable target Actions, security, or Dependabot data makes the run incomplete rather than broadening access or guessing. See the [authentication guide](../docs/authentication.md) for the permission model and credential precedence. +The App installation or PAT must cover every private or internal target, alternate review repository, and live target the package needs to read or update. Public review runs may use `GITHUB_TOKEN`, but unavailable target Actions, security, or Dependabot data makes the run incomplete rather than broadening access or guessing. See the [authentication guide](../docs/authentication.md) for the permission model and credential precedence. -## Validate in staged mode +## Validate in review mode Start with one representative repository: 1. Open the generated **Dependabot** workflow in the control repository's **Actions** tab. 2. Select **Run workflow**. 3. Set `target_repo` to one fully qualified `owner/repository` name. -4. Keep `max_repos` at `1` and `safe_output_mode` at `staged`. -5. Trigger a `workflow_dispatch` run and inspect repository selection, the dispatched worker workflow, staged safe outputs, and control-plane correlation data. +4. Keep `max_repos` at `1` and `safe_output_mode` at `review`. +5. Trigger a `workflow_dispatch` run and inspect repository selection, the dispatched worker workflow, review outputs in the control repository, and control-plane correlation data. -To keep scheduled runs staged, set the package variable explicitly: +To keep scheduled runs in review, set the package variable explicitly: ```bash gh variable set CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE \ - --body staged \ + --body review \ --repo OWNER/CONTROL_REPOSITORY ``` @@ -81,11 +82,10 @@ Changing the variable affects future runs. Cancel active runs separately when ch | Mode | Behavior | | --- | --- | -| `staged` | Uses staged mode: safe outputs are generated without GitHub API writes. | | `review` | Routes safe outputs to the control-plane repository; manual runs may override it with `safe_output_repo`. | | `live` | Allows declared safe outputs to update the selected target repository. Pull requests remain unmerged. | -Promote in order: one-repository staged, private review, limited live, then scheduled live. Change only this package's mode variable; other Central Agentic Ops packages keep their own rollout state. +Promote in order: one-repository review, limited live, then scheduled live. Change only this package's mode variable; other Central Agentic Ops packages keep their own rollout state. ## Targeting @@ -112,9 +112,7 @@ Repositories without a recognized dependency ecosystem, readable manifests, or e ## Pause or Stop -Set `CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE` to `staged` to put future scheduled runs in staged mode. Clearing the mode or using an unrecognized value stops scheduled selection and worker workflow dispatch, but it does not prevent an authorized `workflow_dispatch` run. Legacy `preview` values are normalized to `staged` during migration. - -For a Dependabot-only stop, disable the generated Dependabot orchestrator or updater workflow in GitHub Actions and cancel active runs. For a control-plane-wide stop, follow the [emergency-stop procedure](../docs/operations.md#emergency-stop). +Set `CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED` to `false` and cancel active runs. Re-enable in `review` mode after resolving the incident. For a narrower worker-only stop, use `CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_ENABLED`. For a control-plane-wide stop, follow the [emergency-stop procedure](../docs/operations.md#emergency-stop). ## More Information diff --git a/docs/README.md b/docs/README.md index 32129e0..91e0f14 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,7 +7,7 @@ prev: false next: false hero: title: Agentic operations. At scale. One control plane. - tagline: Dispatch trusted workflows across organizations and more than 10,000 remote target repositories, with bounded execution, staged promotion, and traceable outcomes built in. + tagline: Dispatch trusted workflows across organizations and more than 10,000 remote target repositories, with bounded review, authorized live execution, and traceable outcomes built in. actions: - text: Get started link: /central-agentic-ops/getting-started/ diff --git a/docs/architecture.md b/docs/architecture.md index 7c64bf7..bb41553 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -21,7 +21,7 @@ The control plane is designed to: - A catalog release enters a private control repository, where an orchestrator selects and dispatches work to a worker that emits declared safe outputs in staged, review, or live mode. + A catalog release enters a private control repository, where an orchestrator selects and dispatches work to a worker that emits declared safe outputs in review or live mode. :::note[Three records, three jobs] @@ -36,13 +36,13 @@ The execution boundary is the key architectural fact: orchestrators and workers 2. Shared control resolves mode, routing, candidate repositories, limits, and eligible workers. 3. The orchestrator ranks candidates and dispatches one worker run per selected target. 4. Each worker analyzes only its dispatched target and emits only declared safe outputs. -5. Outputs are simulated in staged mode, sent to a review repository in review mode, or processed against the target in live mode. +5. Outputs are sent to a review repository in review mode or processed against the target in live mode. The orchestrator owns rollout and selection. Workers enforce the dispatched control envelope without escalating mode, discovering additional repositories, or duplicating credentials. ## Core Safety Properties -- staged mode is the default; +- review mode is the default; - target selection and dispatch are bounded; - owners, targets, and review destinations must pass explicit trust checks; - every live `(target repository, package)` pair has one target-approved mutation authority; @@ -62,4 +62,4 @@ Central Agentic Ops governs participating catalog workflows. Use GitHub rulesets | [Deployment and Governance](deployment-and-governance.md) | Organization and enterprise topologies, ownership, target enrollment, provenance, reporting identity, and the broader governance boundary | | [Execution and Safety](execution-and-safety.md) | Layer responsibilities, the full execution flow, dispatch fields, invariants, failure behavior, and implemented controls | | [Orchestrators and Workers](orchestrators-and-workers.md) | Package-specific authority, worker enforcement, eligibility, and worker ceilings | -| [Rollout and Routing](rollout-and-routing.md) | Staged, review, and live promotion; review destinations; authority checks; and rollback | +| [Rollout and Routing](rollout-and-routing.md) | Review-to-live promotion; review destinations; authority checks; and rollback | diff --git a/docs/assets/central-execution-how-it-works.svg b/docs/assets/central-execution-how-it-works.svg index 758d3e4..9c3c4ec 100644 --- a/docs/assets/central-execution-how-it-works.svg +++ b/docs/assets/central-execution-how-it-works.svg @@ -22,9 +22,8 @@ Policy, limits, credentials, and rollout mode stay here. ROLLOUT MODE - Staged - Review - Live + Review + Live Human approval required before live operation diff --git a/docs/assets/control-plane-scale.svg b/docs/assets/control-plane-scale.svg index 990415a..62432fc 100644 --- a/docs/assets/control-plane-scale.svg +++ b/docs/assets/control-plane-scale.svg @@ -1,6 +1,6 @@ Central Agentic Ops control plane at enterprise scale - One central control plane applies policy and identity, promotes work through staged, review, and live gates, and dispatches bounded workflows across organizations and thousands of repositories. + One central control plane applies policy and identity, routes work through private review and approval before authorized live execution, and dispatches bounded workflows across organizations and thousands of repositories. @@ -36,18 +36,18 @@ - STAGED - prove, no writes + REVIEW + route privately - REVIEW - inspect privately + APPROVE + verify boundaries LIVE - bounded writes + authorized writes diff --git a/docs/authentication.md b/docs/authentication.md index ab1b19f..9491616 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -10,15 +10,14 @@ Package maintainers can optionally turn these settings into a guided `gh aw add- | Your use case | Credential | | --- | --- | | Private or internal targets, alternate review repositories, or live writes | GitHub App preferred; fine-grained PAT supported | -| Public targets in `staged` mode | Built-in `GITHUB_TOKEN` | -| Review outputs kept in the control repository | Built-in token only when its repository permissions authorize the output | +| Public targets with review outputs kept in the control repository | Built-in token only when its repository permissions authorize the output | ```text Does the run need a private target, cross-repository data, or live writes? | +-- yes --> GitHub App (preferred) or fine-grained PAT | - +-- no ---> Public target in staged mode --> built-in GITHUB_TOKEN + +-- no ---> Public target reviewed in the control repository --> built-in GITHUB_TOKEN ``` :::tip[Default to a GitHub App] @@ -59,7 +58,7 @@ When manual workflow steps need `GH_TOKEN`, they select the imported App token f ## Public Read-Only Profile -An App or PAT is not required for a bounded `staged` scan when every target repository is public. GitHub Actions automatically provides `GITHUB_TOKEN`; the workflows use it for control-repository workflow discovery and can check out other public repositories. This is built-in-token operation, not anonymous or credential-free operation. +An App or PAT is not required for a bounded `review` run when every target repository is public and outputs remain in the private control repository. GitHub Actions automatically provides `GITHUB_TOKEN`; the workflows use it for control-repository workflow discovery, public checkout, and review outputs authorized in the control repository. This is built-in-token operation, not anonymous or credential-free operation. :::caution[Public does not mean fully readable] The built-in token may check out public code, but it does not automatically gain access to another repository's Actions logs, security data, issues, pull requests, or write APIs. @@ -67,10 +66,9 @@ The built-in token may check out public code, but it does not automatically gain Keep this profile within these boundaries: -- use `staged` mode for public target analysis; +- use `review` mode and keep safe outputs in the current control repository; - keep target owners allowlisted and all repository and dispatch caps in force; - treat unavailable cross-repository API data, including Actions logs or security data, as incomplete rather than weakening the requested analysis; -- use `review` only when safe outputs remain in the current control repository and its `GITHUB_TOKEN` permissions authorize the output; - configure an App or PAT for private or internal targets, an alternate review repository, or any `live` cross-repository write. The workflow token is scoped to the repository containing the workflow. Public checkout does not grant target-repository write access, and a public repository's visibility does not expand the token's Actions, security, issue, or pull-request permissions. If a worker cannot read required target evidence with the available token, it must report incomplete and produce no speculative result. @@ -113,7 +111,7 @@ The GitHub CLI prompts for the token without echoing it. Do not include the toke For a GitHub App: 1. Add the replacement private key to the existing repository secret. -2. Validate staged runs for each installed operation. +2. Validate review runs for each installed operation. 3. Revoke the old private key. 4. Recheck App installation repository access and permissions. @@ -121,10 +119,10 @@ For a PAT: 1. Create a replacement fine-grained PAT with the same or narrower repository access. 2. Replace `GH_AW_GITHUB_TOKEN`. -3. Validate staged runs. +3. Validate review runs. 4. Revoke the previous PAT. -For suspected credential exposure, disable scheduled Agentic Workflows or set operations to an unrecognized/empty mode, revoke the credential, inspect GitHub Actions logs and safe outputs, rotate credentials, and resume from staged mode. +For suspected credential exposure, set affected package kill switches to `false`, cancel active runs, revoke the credential, inspect GitHub Actions logs and safe outputs, rotate credentials, and resume in review mode. :::danger[Suspected exposure] Stopping an operation does not revoke its credential. Disable affected runs and revoke the App installation or PAT before investigating further. @@ -139,4 +137,4 @@ Before promotion, verify: - expected precedence when both are configured; - target repository coverage; - read operations for repository and workflow discovery; -- a staged safe output without credential material. +- a review output in the intended private repository without credential material. diff --git a/docs/bootstrap-configuration.md b/docs/bootstrap-configuration.md index e822ec9..9447c74 100644 --- a/docs/bootstrap-configuration.md +++ b/docs/bootstrap-configuration.md @@ -17,7 +17,7 @@ The current Central Agentic Ops manifests do not declare `config:`. Keep bootstr gh-aw currently reports `config:` as experimental. The `--no-config` flag disables inferred GitHub App permissions and events; it does not skip declared setup actions. A whole profile is optional only when the package omits `config:`. Within a profile, `optional: true` is supported for `repo-variable` and `repo-secret`, but not for `github-app`. ::: -Preserving the default manual path also keeps all three supported authentication choices available: built-in token for bounded public staged runs, GitHub App, and fine-grained PAT. Use the patterns below only when that tradeoff is appropriate for a specific package. +Preserving the default manual path also keeps all three supported authentication choices available: built-in token for bounded public review runs, GitHub App, and fine-grained PAT. Use the patterns below only when that tradeoff is appropriate for a specific package. ## Prefer a GitHub App Profile @@ -42,14 +42,14 @@ config: - type: repo-variable name: CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE prompt: Dependabot rollout mode - default: staged - enum: [staged, review, live] + default: review + enum: [review, live] - type: repo-variable name: CENTRAL_AGENTIC_OPS_DEPENDABOT_MAX_REPOS prompt: Maximum repositories per scheduled run default: "1" - type: handoff - message: Run one staged operation against one repository before promotion. + message: Run one reviewed operation against one repository before promotion. ``` Do not copy a broad `permissions:` list into the App action. gh-aw `v0.87.2` and later infer the minimum App permissions and webhook events from only the workflows resolved for that package, including their safe outputs. The wizard merges explicitly declared requirements with inferred requirements and shows the resulting App manifest before opening GitHub's creation flow. Current Central Agentic Ops packages require `v0.87.6` or later. @@ -83,13 +83,13 @@ config: name: CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS prompt: Comma-separated target repository owners - type: handoff - message: Run one staged operation against one repository before promotion. + message: Run one reviewed operation against one repository before promotion. ``` Create the PAT before running the wizard. Select only the control and target repositories needed by the package, then grant only the permissions listed in [Configure Authentication](authentication.md#permissions). Do not use a classic PAT or an organization-wide token as a shortcut. :::caution[The wizard cannot verify PAT scope] -`repo-secret` securely stores the supplied value, but it does not mint a PAT or inspect its repository selection and permissions. Least privilege remains an operator decision. Validate access with a staged run before enabling review or live mode. +`repo-secret` securely stores the supplied value, but it does not mint a PAT or inspect its repository selection and permissions. Least privilege remains an operator decision. Validate access with a review run before enabling live mode. ::: Central Agentic Ops workflows already declare `copilot-requests: write` and use organization billing for inference. Do not add a `copilot-auth` action or create a separate Copilot PAT for these packages. @@ -129,6 +129,6 @@ Before publishing a package with `config:`: 2. Install the package by pinned release or commit into a disposable private control repository. 3. Confirm existing variables and secrets are detected and left unchanged when the wizard is rerun. 4. Review the App's permissions and selected repositories, or review the fine-grained PAT's repository selection and permissions. -5. Run one explicit target with `max_repos` set to `1` and `safe_output_mode` set to `staged`. +5. Run one explicit target with `max_repos` set to `1` and `safe_output_mode` set to `review`. -`repo-variable` and `repo-secret` actions are idempotent by name: the wizard skips an existing value rather than overwriting it. A `github-app` action is skipped only when both its client ID variable and private key secret exist; a partial pair is not treated as configured. A successful setup still requires the staged validation in [Quickstart](getting-started.md#step-5---trigger-one-staged-run). \ No newline at end of file +`repo-variable` and `repo-secret` actions are idempotent by name: the wizard skips an existing value rather than overwriting it. A `github-app` action is skipped only when both its client ID variable and private key secret exist; a partial pair is not treated as configured. A successful setup still requires the review validation in [Quickstart](getting-started.md#step-5---trigger-one-review-run). \ No newline at end of file diff --git a/docs/components/Hero.astro b/docs/components/Hero.astro index ae7ad76..e652793 100644 --- a/docs/components/Hero.astro +++ b/docs/components/Hero.astro @@ -65,8 +65,8 @@ const { title = data.title, tagline, actions = [] } = data.hero || {};

Rollout mode

-
- StagedReviewLive +
+ ReviewLive
Human approval before live
@@ -334,7 +334,7 @@ const { title = data.title, tagline, actions = [] } = data.hero || {}; .mode-track { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(2, 1fr); overflow: hidden; border: 1px solid var(--sl-color-gray-5); border-radius: 5px; @@ -352,7 +352,7 @@ const { title = data.title, tagline, actions = [] } = data.hero || {}; border-inline-start: 1px solid var(--sl-color-gray-5); } - .mode-track span:nth-child(2) { + .mode-track span:first-child { color: var(--sl-color-white); } diff --git a/docs/components/OpsWizard.astro b/docs/components/OpsWizard.astro index 051169a..b69be9e 100644 --- a/docs/components/OpsWizard.astro +++ b/docs/components/OpsWizard.astro @@ -52,7 +52,7 @@ const agents = [

Start an operation in three steps

Pick the operation you want to run and the coding agent you use, then copy the generated prompt. The agent creates - your private control plane repository, installs the operation, and runs it once in staged mode. + your private control plane repository, installs the operation, and runs it once in review mode.

@@ -233,17 +233,17 @@ const agents = [ "## First run", "", "- Set `CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS` to the owner of the repositories the operation may touch.", - "- Keep the package rollout variables at `staged` mode and cap scheduled selection at one repository.", - "- Trigger one manual run of the orchestrator against a single low-risk target repository with `safe_output_mode=staged`.", + "- Keep the package rollout variables at `review` mode and cap scheduled selection at one repository.", + "- Trigger one manual run of the orchestrator against a single low-risk target repository with `safe_output_mode=review`.", "- Watch the run with `gh run watch` and summarize the orchestrator report.", ] : [ "## First run", "", `- \`gh variable set CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS --body "${owner}"\``, - `- \`gh variable set ${variablePrefix(operation.value)}_MODE --body "staged"\``, + `- \`gh variable set ${variablePrefix(operation.value)}_MODE --body "review"\``, `- \`gh variable set ${variablePrefix(operation.value)}_MAX_REPOS --body "1"\``, - `- Trigger one manual run: \`gh workflow run ${operation.value}.lock.yml --raw-field target_repo="${owner}/" --raw-field max_repos="1" --raw-field safe_output_mode="staged"\`.`, + `- Trigger one manual run: \`gh workflow run ${operation.value}.lock.yml --raw-field target_repo="${owner}/" --raw-field max_repos="1" --raw-field safe_output_mode="review"\`.`, "- Watch the run with `gh run watch` and summarize the orchestrator report.", ]; @@ -260,7 +260,7 @@ const agents = [ "## Rules", "", "- Keep the control repository private.", - "- Keep the operation in `staged` mode for the first run; never switch it to `live` without asking me.", + "- Keep the operation in `review` mode for the first run; never switch it to `live` without asking me.", "- Never edit generated `.lock.yml` files by hand; change the Markdown source and recompile.", "- Stop and ask me before adding secrets, widening permissions, or targeting additional repositories.", "", diff --git a/docs/configuration.md b/docs/configuration.md index 2c19746..07dd0cd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -5,7 +5,7 @@ description: Repository variables, secrets, and manual inputs for Central Agenti Control-plane configuration is stored as GitHub repository variables and secrets in the private central control repository. Scheduled runs use that configuration. Manual workflow inputs define a separate run without changing scheduled configuration. Values computed inside a workflow are runtime state and must not be configured directly. -For a first installation, follow [Install and run safely](getting-started.md) and return here only for exact setting names and defaults. Keep every operation in `staged` and `max_repos` at `1` until its promotion checks pass. +For a first installation, follow [Install and run safely](getting-started.md) and return here only for exact setting names and defaults. Keep every operation in `review` and `max_repos` at `1` until its promotion checks pass. ## Required Baseline @@ -14,15 +14,15 @@ For private or internal targets, alternate review repositories, or live target w 1. A GitHub App using `GH_AW_GITHUB_APP_ID` and `GH_AW_GITHUB_APP_PRIVATE_KEY` is preferred. 2. A fine-grained PAT can be supplied through `GH_AW_GITHUB_TOKEN` as a fallback or as the only authentication method. -For public targets only, bounded staged scans can instead use the automatically provided `GITHUB_TOKEN`; no App or PAT secret is required. Review is supported without an App or PAT only when outputs stay in the current control repository and its workflow-token permissions authorize them. See [Public Read-Only Profile](authentication.md#public-read-only-profile). +For public targets only, bounded review runs can instead use the automatically provided `GITHUB_TOKEN`; no App or PAT secret is required when outputs stay in the current control repository and its workflow-token permissions authorize them. See [Public Read-Only Profile](authentication.md#public-read-only-profile). -Every installed operation has an independent mode. Installation defaults each mode to `staged`. +Every installed operation has an independent output mode and kill switch. Installation defaults each mode to `review` and each kill switch to `true`, so packages are immediately runnable without target writes. :::tip[Variables describe policy; secrets prove identity] Put modes, limits, and owner names in repository variables. Put private keys and tokens in repository secrets. Never pass credentials through `workflow_dispatch` inputs. ::: -For a one-repository staged Dependabot rollout, set this baseline: +For a one-repository Dependabot review, set this baseline: ```bash CONTROL_REPO="acme/central-agentic-ops" @@ -30,12 +30,12 @@ CONTROL_REPO="acme/central-agentic-ops" gh variable set CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS \ --repo "$CONTROL_REPO" --body "acme" gh variable set CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE \ - --repo "$CONTROL_REPO" --body "staged" + --repo "$CONTROL_REPO" --body "review" gh variable set CENTRAL_AGENTIC_OPS_DEPENDABOT_MAX_REPOS \ --repo "$CONTROL_REPO" --body "1" ``` -Add an App or PAT when the target is private or internal. Keep the mode at `staged` until the promotion checks pass. +Add an App or PAT when the target is private or internal. Keep the mode at `review` until the promotion checks pass. ## Repository Variables @@ -49,52 +49,64 @@ Add an App or PAT when the target is private or internal. Keep the mode at `stag | `CENTRAL_AGENTIC_OPS_BATCH_SIZE` | Shared | No | `100000` | Maximum repositories exposed to an orchestrator from its selected cell. Accepts `1` through `100000`. | | `CENTRAL_AGENTIC_OPS_BATCH_INDEX` | Shared | No | `0` | Zero-based batch selected for a scheduled run. | | `CENTRAL_AGENTIC_OPS_MAX_AI_CREDITS_PER_RUN` | Shared | No | `1100` | Maximum declared orchestrator-plus-worker AI Credits admitted for one orchestration. | -| `CENTRAL_AGENTIC_OPS_ADVISORY_MODE` | Advisory | Yes when installed | `staged` | Sets the operation mode to `staged`, `review`, or `live`. | +| `CENTRAL_AGENTIC_OPS_ADVISORY_ENABLED` | Advisory | No | `true` | Package kill switch. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_ADVISORY_MODE` | Advisory | No | `review` | Sets the output mode to `review` or `live`. | | `CENTRAL_AGENTIC_OPS_ADVISORY_MAX_REPOS` | Advisory | No | `1` | Scheduled repository-selection cap. Accepts `1` through `1000`; dispatch and credit limits may reduce it further. | | `CENTRAL_AGENTIC_OPS_ADVISORY_ROLLOUT_PERCENT` | Advisory | No | `100` | Limits selection to this percentage of discovered repositories. Accepts integers from `1` through `100`. | | `CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_ENABLED` | Advisory worker | No | `true` | UK AI operational resilience worker kill switch. | -| `CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_MAX_MODE` | Advisory worker | No | `staged` | UK AI operational resilience worker mode ceiling. | -| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` | Ambient Context | Yes when installed | `staged` | Sets the operation mode to `staged`, `review`, or `live`. | +| `CENTRAL_AGENTIC_OPS_ADVISORY_UK_AI_OPERATIONAL_RESILIENCE_MAX_MODE` | Advisory worker | No | `review` | UK AI operational resilience worker mode ceiling. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED` | Ambient Context | No | `true` | Package kill switch. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` | Ambient Context | No | `review` | Sets the output mode to `review` or `live`. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MAX_REPOS` | Ambient Context | No | `1` | Scheduled repository-selection cap. Accepts `1` through `1000`; dispatch limits may reduce it further. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ROLLOUT_PERCENT` | Ambient Context | No | `100` | Limits selection to this percentage of discovered repositories. Accepts integers from `1` through `100`. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_ENABLED` | Ambient Context worker | No | `true` | Worker kill switch for the `AGENTS.md` curator. | -| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_MAX_MODE` | Ambient Context worker | No | `staged` | Maximum `AGENTS.md` curator mode. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_AGENTS_MD_MAX_MODE` | Ambient Context worker | No | `review` | Maximum `AGENTS.md` curator mode. | | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_ENABLED` | Ambient Context worker | No | `true` | Worker kill switch for the skills curator. | -| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_MAX_MODE` | Ambient Context worker | No | `staged` | Maximum skills curator mode. | -| `CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE` | AW Failures | Yes when installed | `staged` | Sets the operation mode to `staged`, `review`, or `live`. | +| `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_SKILLS_MAX_MODE` | Ambient Context worker | No | `review` | Maximum skills curator mode. | +| `CENTRAL_AGENTIC_OPS_AW_FAILURES_ENABLED` | AW Failures | No | `true` | Package kill switch. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE` | AW Failures | No | `review` | Sets the output mode to `review` or `live`. | | `CENTRAL_AGENTIC_OPS_AW_FAILURES_MAX_REPOS` | AW Failures | No | `1` | Scheduled repository-selection cap. Accepts `1` through `1000`; dispatch limits may reduce it further. | | `CENTRAL_AGENTIC_OPS_AW_FAILURES_ROLLOUT_PERCENT` | AW Failures | No | `100` | Limits selection to this percentage of discovered repositories. Accepts integers from `1` through `100`. | | `CENTRAL_AGENTIC_OPS_AW_FAILURES_INVESTIGATOR_ENABLED` | AW Failures worker | No | `true` | Worker kill switch for the investigator. | -| `CENTRAL_AGENTIC_OPS_AW_FAILURES_INVESTIGATOR_MAX_MODE` | AW Failures worker | No | `staged` | Maximum investigator mode: `staged`, `review`, or `live`. | -| `CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE` | Dependabot | Yes when installed | `staged` | Sets the operation mode to `staged`, `review`, or `live`. | +| `CENTRAL_AGENTIC_OPS_AW_FAILURES_INVESTIGATOR_MAX_MODE` | AW Failures worker | No | `review` | Maximum investigator mode: `review` or `live`. | +| `CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_ENABLED` | AW Maintenance | No | `true` | Package kill switch. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MODE` | AW Maintenance | No | `review` | Sets the output mode to `review` or `live`. | +| `CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_MAX_REPOS` | AW Maintenance | No | `1` | Scheduled repository-selection cap. Accepts `1` through `1000`; dispatch limits may reduce it further. | +| `CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_ROLLOUT_PERCENT` | AW Maintenance | No | `100` | Limits selection to this percentage of discovered repositories. Accepts integers from `1` through `100`. | +| `CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_UPGRADE_ENABLED` | AW Maintenance worker | No | `true` | Worker kill switch for the upgrade worker. | +| `CENTRAL_AGENTIC_OPS_AW_MAINTENANCE_UPGRADE_MAX_MODE` | AW Maintenance worker | No | `review` | Maximum upgrade worker mode: `review` or `live`. | +| `CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED` | Dependabot | No | `true` | Package kill switch. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE` | Dependabot | No | `review` | Sets the output mode to `review` or `live`. | | `CENTRAL_AGENTIC_OPS_DEPENDABOT_MAX_REPOS` | Dependabot | No | `1` | Scheduled repository-selection cap. Accepts `1` through `1000`; dispatch limits may reduce it further. | | `CENTRAL_AGENTIC_OPS_DEPENDABOT_ROLLOUT_PERCENT` | Dependabot | No | `100` | Limits selection to this percentage of discovered repositories. Accepts integers from `1` through `100`. | | `CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_ENABLED` | Dependabot worker | No | `true` | Worker kill switch. Set to `false` to reject updater runs. | -| `CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_MAX_MODE` | Dependabot worker | No | `staged` | Maximum updater mode: `staged`, `review`, or `live`. | -| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE` | EU CRA Advisor | Yes when installed | `staged` | Sets the operation mode to `staged`, `review`, or `live`. | +| `CENTRAL_AGENTIC_OPS_DEPENDABOT_UPDATER_MAX_MODE` | Dependabot worker | No | `review` | Maximum updater mode: `review` or `live`. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ENABLED` | EU CRA Advisor | No | `true` | Package kill switch. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MODE` | EU CRA Advisor | No | `review` | Sets the output mode to `review` or `live`. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_MAX_REPOS` | EU CRA Advisor | No | `1` | Scheduled repository-selection cap. Accepts `1` through `1000`; dispatch and credit limits may reduce it further. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ROLLOUT_PERCENT` | EU CRA Advisor | No | `100` | Limits selection to this percentage of discovered repositories. Accepts integers from `1` through `100`. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER_ENABLED` | EU CRA Advisor worker | No | `true` | Scope classifier kill switch. | -| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER_MAX_MODE` | EU CRA Advisor worker | No | `staged` | Scope classifier mode ceiling. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER_MAX_MODE` | EU CRA Advisor worker | No | `review` | Scope classifier mode ceiling. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR_ENABLED` | EU CRA Advisor worker | No | `true` | Security requirements auditor kill switch. | -| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR_MAX_MODE` | EU CRA Advisor worker | No | `staged` | Security requirements auditor mode ceiling. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR_MAX_MODE` | EU CRA Advisor worker | No | `review` | Security requirements auditor mode ceiling. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR_ENABLED` | EU CRA Advisor worker | No | `true` | Supply-chain/SBOM auditor kill switch. | -| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR_MAX_MODE` | EU CRA Advisor worker | No | `staged` | Supply-chain/SBOM auditor mode ceiling. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR_MAX_MODE` | EU CRA Advisor worker | No | `review` | Supply-chain/SBOM auditor mode ceiling. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR_ENABLED` | EU CRA Advisor worker | No | `true` | Vulnerability-handling auditor kill switch. | -| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR_MAX_MODE` | EU CRA Advisor worker | No | `staged` | Vulnerability-handling auditor mode ceiling. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR_MAX_MODE` | EU CRA Advisor worker | No | `review` | Vulnerability-handling auditor mode ceiling. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS_ENABLED` | EU CRA Advisor worker | No | `true` | Article 14 reporting-readiness kill switch. | -| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS_MAX_MODE` | EU CRA Advisor worker | No | `staged` | Article 14 reporting-readiness mode ceiling. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS_MAX_MODE` | EU CRA Advisor worker | No | `review` | Article 14 reporting-readiness mode ceiling. | | `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE_ENABLED` | EU CRA Advisor worker | No | `true` | Conformity/release-evidence kill switch. | -| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE_MAX_MODE` | EU CRA Advisor worker | No | `staged` | Conformity/release-evidence mode ceiling. | -| `CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE` | Optimization | Yes when installed | `staged` | Sets the operation mode to `staged`, `review`, or `live`. | +| `CENTRAL_AGENTIC_OPS_EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE_MAX_MODE` | EU CRA Advisor worker | No | `review` | Conformity/release-evidence mode ceiling. | +| `CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED` | Optimization | No | `true` | Package kill switch. Set to `false` to stop orchestrator and worker dispatches. | +| `CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE` | Optimization | No | `review` | Sets the output mode to `review` or `live`. | | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_MAX_REPOS` | Optimization | No | `1` | Scheduled repository-selection cap. Accepts `1` through `1000`; dispatch limits may reduce it further. | | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_ROLLOUT_PERCENT` | Optimization | No | `100` | Limits selection to this percentage of discovered repositories. Accepts integers from `1` through `100`. | | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_AUDITOR_ENABLED` | Optimization worker | No | `true` | Worker kill switch for the auditor. | -| `CENTRAL_AGENTIC_OPS_OPTIMIZATION_AUDITOR_MAX_MODE` | Optimization worker | No | `staged` | Maximum auditor mode. | +| `CENTRAL_AGENTIC_OPS_OPTIMIZATION_AUDITOR_MAX_MODE` | Optimization worker | No | `review` | Maximum auditor mode. | | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_OPTIMIZER_ENABLED` | Optimization worker | No | `true` | Worker kill switch for the optimizer. | -| `CENTRAL_AGENTIC_OPS_OPTIMIZATION_OPTIMIZER_MAX_MODE` | Optimization worker | No | `staged` | Maximum optimizer mode. | +| `CENTRAL_AGENTIC_OPS_OPTIMIZATION_OPTIMIZER_MAX_MODE` | Optimization worker | No | `review` | Maximum optimizer mode. | -An empty or unrecognized operation mode disables scheduled selection and worker workflow dispatch. It does not block a `workflow_dispatch` run. Scheduled review mode routes safe outputs to the current control-plane repository. For an all-stop procedure, see [Emergency Stop](operations.md#emergency-stop). +Only `review` and `live` are valid output modes. Set the package's `CENTRAL_AGENTIC_OPS__ENABLED` variable to `false` to disable both scheduled and manual dispatches. Scheduled review mode routes safe outputs to the current control-plane repository. For an all-stop procedure, see [Emergency Stop](operations.md#emergency-stop). ### Pages Report Destinations @@ -113,7 +125,7 @@ Ops Publish prefers the existing GitHub App configuration. PAT fallback uses sep | Name | Scope | Required | Purpose | | --- | --- | --- | --- | | `GH_AW_GITHUB_APP_PRIVATE_KEY` | Shared | With App authentication | Private key paired with `GH_AW_GITHUB_APP_ID`. | -| `GH_AW_GITHUB_TOKEN` | Shared | For cross-repository access without a complete App configuration | Fine-grained PAT fallback for control-plane GitHub access. Not required for the public read-only profile. | +| `GH_AW_GITHUB_TOKEN` | Shared | For cross-repository access without a complete App configuration | Fine-grained PAT fallback for control-plane GitHub access. Not required for public targets reviewed in the control repository. | | `GH_AW_CI_TOKEN` | Dependabot | Optional | Token used by the Dependabot safe output path when an additional empty commit is required. | Keep secrets in the control repository. Do not place credentials in variables, workflow inputs, dispatch envelopes, or target repositories. See [Authentication](authentication.md) for permissions, precedence, rotation, and revocation. @@ -132,9 +144,9 @@ Both operation orchestrators expose the same inputs under **Run workflow**: | `cell_index` | Number | `0` | Selects one zero-based inventory cell. | | `batch_size` | Number | `100000` | Bounds the repositories supplied to the orchestrator from that cell. | | `batch_index` | Number | `0` | Selects one zero-based batch from that cell. | -| `safe_output_mode` | Choice | `staged` | Selects staged mode, review routing, or live safe output processing for this `workflow_dispatch` run. | +| `safe_output_mode` | Choice | `review` | Selects review routing or live safe output processing for this `workflow_dispatch` run. | -`workflow_dispatch` inputs affect only the dispatched run. They do not update repository variables or another operation's policy. Precompute emits a content-addressed `inventory_version` and deterministic `batch_id`; the same inventory and scheduling inputs produce the same batch. These controls do not auto-advance batches, retry work, or provide durable completion tracking. The percentage cap is rounded up so a non-empty candidate set can select at least one repository. `max_repos`, the percentage cap, and the target count permitted by the orchestrator workflow's remaining dispatch budget are cumulative; the smallest cap wins. Invalid or out-of-range caps fail precomputation. During validation, specify one `target_repo`, keep `max_repos` at `1`, and begin in staged mode. +`workflow_dispatch` inputs affect only the dispatched run. They do not update repository variables or another operation's policy. Precompute emits a content-addressed `inventory_version` and deterministic `batch_id`; the same inventory and scheduling inputs produce the same batch. These controls do not auto-advance batches, retry work, or provide durable completion tracking. The percentage cap is rounded up so a non-empty candidate set can select at least one repository. `max_repos`, the percentage cap, and the target count permitted by the orchestrator workflow's remaining dispatch budget are cumulative; the smallest cap wins. Invalid or out-of-range caps fail precomputation. During validation, specify one `target_repo`, keep `max_repos` at `1`, and begin in review mode. Example cap calculation: @@ -172,7 +184,8 @@ Control values resolve in this order: | Decision | Resolution | | --- | --- | | Authentication | GitHub App, then `GH_AW_GITHUB_TOKEN`, then the run's `GITHUB_TOKEN` where that token can authorize the operation. | -| Mode | Schedule-triggered runs use the operation mode variable. `workflow_dispatch` runs use the `safe_output_mode` workflow input and do not change or depend on the scheduled mode. Missing values default to `staged`; legacy `preview` values normalize to `staged`. | +| Package enablement | `CENTRAL_AGENTIC_OPS__ENABLED`, then `true`. A false value stops scheduled and manual package dispatches before repository access. | +| Mode | Schedule-triggered runs use the operation mode variable. `workflow_dispatch` runs use the `safe_output_mode` workflow input and do not change the scheduled mode. Missing values default to `review`; only `review` and `live` are valid. | | Review destination | `safe_output_repo` workflow input for a manual run, otherwise `github.repository`. | | Allowed repository owners | `CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS`, otherwise `github.repository_owner`. Applies to orchestrated and directly dispatched workers. | | Absolute repository cap | `max_repos` workflow input, then the operation max-repositories variable, then `1`. | @@ -204,11 +217,10 @@ The following names appear in workflow execution but are derived by shared contr | `TARGET_REPO` | The `target_repo` workflow input or the worker workflow dispatch envelope. | | `REVIEW_OUTPUT_REPO` | The `safe_output_repo` workflow input or current `github.repository`. | | `SAFE_OUTPUT_REPO` | The effective destination computed for the selected mode. | -| `preview_only` | Whether the effective mode requires staged mode for safe outputs. | | `GH_TOKEN` | The credential selected for explicit GitHub CLI steps. | | `GITHUB_TOKEN` | A token supplied by GitHub Actions for the current run. | -Other `GH_AW_*` values, including safe-output files and staging flags, are managed by the gh-aw runtime and are not control-plane configuration. +Other `GH_AW_*` values, including safe-output files, are managed by the gh-aw runtime and are not control-plane configuration. ## Sources of Truth diff --git a/docs/dashboard-language-specification.md b/docs/dashboard-language-specification.md index 27c6730..b65f995 100644 --- a/docs/dashboard-language-specification.md +++ b/docs/dashboard-language-specification.md @@ -252,7 +252,7 @@ A grader applies a named grading criterion and produces a deterministic grader o - **DLS-SEM-018:** Each logical source **MUST** preserve the grain declared in Section 5.1; duplicated observations **MUST** retain distinct observation identifiers in provenance. - **DLS-SEM-019:** A `usage` row **MUST** represent one model invocation and **MUST NOT** repeat invocation-level AIC across token-class rows. - **DLS-SEM-020:** Grader values, eval results, AIC, each raw-token measure, outcome states, and operational value **MUST** remain separately named throughout filtering, aggregation, and presentation. -- **DLS-SEM-021:** `rollout-mode` **MUST** use `staged`, `review`, `live`, or `unknown`. +- **DLS-SEM-021:** `rollout-mode` **MUST** use `review`, `live`, or `unknown`. --- diff --git a/docs/deployment-and-governance.md b/docs/deployment-and-governance.md index 1166c35..f9ad6d4 100644 --- a/docs/deployment-and-governance.md +++ b/docs/deployment-and-governance.md @@ -49,7 +49,7 @@ A single control repository can address an explicitly named repository in anothe These levels are complementary, but they have no implicit precedence. Catalog ownership grants publication authority, not execution authority. Installing a package grants a runtime the ability to execute only within its credential scope and approved target inventory; it does not transfer ownership of target repositories. -Before a package enters `live`, assign exactly one live mutation authority for each `(target repository, package)` pair. Enterprise and organization runtimes may both perform staged analysis or produce review output, but they must not concurrently mutate the same target for the same package. A live worker reads the target-owned authority file from the target's default branch and fails before agent execution unless that package names the worker's control repository. Separate GitHub Actions repositories still do not provide shared cancellation or a cross-repository concurrency group for runs already in progress. +Before a package enters `live`, assign exactly one live mutation authority for each `(target repository, package)` pair. Enterprise and organization runtimes may both produce review output, but they must not concurrently mutate the same target for the same package. A live worker reads the target-owned authority file from the target's default branch and fails before agent execution unless that package names the worker's control repository. Separate GitHub Actions repositories still do not provide shared cancellation or a cross-repository concurrency group for runs already in progress. ## Catalog Ownership and Discovery @@ -65,7 +65,7 @@ GitHub repository custom properties may project selected fields from those recor | `central-ops-catalog` | `githubnext/central-agentic-ops` | Projects the authoritative catalog source. | | `central-ops-version` | Release tag or commit SHA | Projects the catalog revision installed by the control repository. | | `central-ops-owner` | `organization/platform-team` | Identifies the team responsible for operation and incidents. | -| `central-ops-status` | `staged`, `active`, or `suspended` | Records the installation lifecycle state. | +| `central-ops-status` | `review`, `active`, or `suspended` | Records the installation lifecycle state. | The `central-agentic-ops-control-plane` repository topic is an optional lightweight discovery aid. Where custom properties are available, they provide the structured searchable projection. If neither mechanism covers an installation, an enterprise may maintain a small derived registry containing only organization, control repository, catalog revision, owner, and status. Rebuild that registry from repository-owned records where practical; it must not become a dispatcher or contain credentials, policy overrides, runtime health, or dispatch state. @@ -79,7 +79,7 @@ An allowed owner and a reachable credential are security boundaries, not evidenc - the approval and review date; - the revocation path. -Store this evidence in an enterprise- or organization-approved inventory, such as governed custom properties or a reviewed registry. The current workflows do not query or reconcile that inventory automatically. Until they do, scope the GitHub App installation or fine-grained PAT to enrolled repositories and treat broad owner discovery as staged or review-only. Owner allowlists remain mandatory but are not sufficient for live enrollment. +Store this evidence in an enterprise- or organization-approved inventory, such as governed custom properties or a reviewed registry. The current workflows do not query or reconcile that inventory automatically. Until they do, scope the GitHub App installation or fine-grained PAT to enrolled repositories and treat broad owner discovery as review-only. Owner allowlists remain mandatory but are not sufficient for live enrollment. The target repository enforces its live mutation authority in `.github/central-agentic-ops.yml`: @@ -92,11 +92,11 @@ bundles: authority: acme/central-ops ``` -Protect this file on the default branch with a ruleset and CODEOWNERS approval from the target repository owner. Missing, malformed, or mismatched authority fails closed in `live` before the agent starts. The file records consent and authority only; keep credentials, rollout modes, schedules, and runtime state in the control repository. Staged and review runs do not require it because they cannot mutate the target. +Protect this file on the default branch with a ruleset and CODEOWNERS approval from the target repository owner. Missing, malformed, or mismatched authority fails closed in `live` before the agent starts. The file records consent and authority only; keep credentials, rollout modes, schedules, and runtime state in the control repository. Review runs do not require it because they cannot mutate the target. ## Downstream Fan-Out and Provenance -Each central control repository fans out enabled packages to selected targets, subject to repository allowlists, credential scope, enrollment, live mutation ownership, and dispatch limits. Orchestrator and worker workflows run from that central repository. Each worker workflow checks out one target repository, inspects only that target, and creates only declared safe outputs in the configured downstream destination. A target repository may receive staged or review output from both enterprise and organization control repositories without storing either source's Agentic Workflow definitions, but only its assigned runtime may perform live mutation for a given package. +Each central control repository fans out enabled packages to selected targets, subject to repository allowlists, credential scope, enrollment, live mutation ownership, and dispatch limits. Orchestrator and worker workflows run from that central repository. Each worker workflow checks out one target repository, inspects only that target, and creates only declared safe outputs in the configured downstream destination. A target repository may receive review output from both enterprise and organization control repositories without storing either source's Agentic Workflow definitions, but only its assigned runtime may perform live mutation for a given package. The standard `central_repo`, `control_plane_run_url`, and `correlation_id` fields identify the originating central runtime and run. Because `central_repo` differs between enterprise and organization control repositories, downstream safe outputs retain their runtime source. diff --git a/docs/execution-and-safety.md b/docs/execution-and-safety.md index f407b31..ff7c86f 100644 --- a/docs/execution-and-safety.md +++ b/docs/execution-and-safety.md @@ -27,9 +27,9 @@ The execution boundary is the key architectural fact: orchestrators and workers 4. The orchestrator workflow ranks eligible repositories using package-specific discovery rules and applies `max_repos` and dispatch limits. 5. The orchestrator workflow dispatches each eligible worker workflow with the standard control envelope. 6. The worker workflow imports shared control as `role: worker`, analyzes only `target_repo`, and emits only its declared safe outputs. -7. safe outputs are simulated in staged mode, routed to the review repository, or processed against the target repository according to the effective mode. +7. safe outputs are routed to the review repository or processed against the target repository according to the effective mode. -Pages report routing participates in the control plane. staged mode stages report source data without deployment. Review routes report source data to the private `safe_output_repo` and publishes an access-controlled review Pages site owned by that repository. Live routes durable report source data to its normal destination and publishes the production Pages site. Conventional deterministic workflows perform both deployments and own `pages: write` and `id-token: write`; AI agent jobs do not. +Pages report routing participates in the control plane. Review routes report source data to the private `safe_output_repo` and publishes an access-controlled review Pages site owned by that repository. Live routes durable report source data to its normal destination and publishes the production Pages site. Conventional deterministic workflows perform both deployments and own `pages: write` and `id-token: write`; AI agent jobs do not. ## Standard Control Envelope @@ -38,9 +38,8 @@ Every worker workflow dispatch carries: | Field | Purpose | | --- | --- | | `target_repo` | The only target repository the worker workflow may analyze or update | -| `safe_output_mode` | `staged`, `review`, or `live` | +| `safe_output_mode` | `review` or `live` | | `safe_output_repo` | safe output destination; review mode defaults this to the current control-plane repository | -| `preview_only` | Enables staged mode for safe outputs when `true` | | `correlation_id` | Joins worker workflow safe outputs to the orchestrator workflow run | | `central_repo` | Identifies the control-plane repository | | `control_plane_run_url` | Provides the originating run for audit and diagnosis | @@ -54,7 +53,6 @@ An effective dispatch envelope resembles: target_repo: acme/example-service safe_output_mode: review safe_output_repo: acme/central-agentic-ops-review -preview_only: false correlation_id: optimization-2026-08-25-001 central_repo: acme/central-agentic-ops control_plane_run_url: https://github.com/acme/central-agentic-ops/actions/runs/123456 @@ -67,7 +65,7 @@ Never add an App key, PAT, installation token, or other secret to this envelope. ## Invariants -- staged mode is the default mode. +- review mode is the default mode. - Automatic discovery scans at most `1000` repositories by default and never more than `100000`. - Orchestrator precompute versions each inventory and deterministically selects one bounded cell and batch before agent ranking begins. - Repository selection defaults to one target and is bounded by absolute, percentage, and dispatch-derived caps. diff --git a/docs/getting-started.md b/docs/getting-started.md index 0f7b7a3..4ec6ba6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -5,9 +5,9 @@ description: Create a private control plane, install one operation, and run it s Central Agentic Ops lets you run governed agentic operations across many repositories from one private GitHub repository, which we call the central control plane. Operation packages, credentials, rollout policy, and workflow runs stay in the control plane; target repositories do not receive copies of the workflows. -By the end of this guide, you will have created a control plane, installed the Dependabot operation, and completed one `staged` run against a public target repository. You will verify that the operation selected the expected target and proposed work without changing it. +By the end of this guide, you will have created a control plane, installed the Dependabot operation, and completed one `review` run against a public target repository. You will verify that the operation selected the expected target, saved any proposal in the private control repository, and did not change the target. -## Run a Staged Dependabot Operation +## Run a Reviewed Dependabot Operation Estimated time: 15 minutes @@ -86,7 +86,7 @@ The package installs: 3. shared authentication, routing, and fail-closed controls; 4. generated `.lock.yml` workflows that GitHub Actions executes. -Keep the operation in `staged` mode when the wizard asks for its rollout settings. Then commit and push the installed files: +The installed operation defaults to `review` and is immediately runnable. Commit and push the installed files: ```bash git add .github @@ -98,19 +98,19 @@ Do not edit generated `.lock.yml` files directly. Update their Markdown sources ### Step 4 - Set the first-run boundary -Configure the target owner, keep the scheduled operation staged, and cap scheduled selection at one repository: +Configure the target owner, keep the scheduled operation in review, and cap scheduled selection at one repository: ```bash TARGET_OWNER="${TARGET_REPO%%/*}" gh variable set CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS --body "$TARGET_OWNER" -gh variable set CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE --body "staged" +gh variable set CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE --body "review" gh variable set CENTRAL_AGENTIC_OPS_DEPENDABOT_MAX_REPOS --body "1" ``` -These variables configure future scheduled runs. The manual run in the next step also names one explicit target and requests `staged` mode. +These variables configure future scheduled runs. The manual run in the next step also names one explicit target and requests `review` mode. -### Step 5 - Trigger one staged run +### Step 5 - Trigger one review run Run the installed orchestrator against the target repository: @@ -119,12 +119,12 @@ gh workflow run dependabot.lock.yml \ --raw-field target_repo="$TARGET_REPO" \ --raw-field max_repos="1" \ --raw-field rollout_percent="100" \ - --raw-field safe_output_mode="staged" + --raw-field safe_output_mode="review" ``` You can also open the control repository's **Actions** tab, select **Dependabot**, and choose **Run workflow** with the same values. -The orchestrator should select only the named repository and dispatch at most one updater. In `staged` mode, proposed safe outputs are recorded without creating or changing issues, pull requests, branches, or files. +The orchestrator should select only the named repository and dispatch at most one updater. In `review` mode, proposed safe outputs are saved in the private control repository without creating or changing issues, pull requests, branches, or files in the target. ### Step 6 - Wait for the operation to complete @@ -140,7 +140,7 @@ Copy the run ID from the first row, then watch it until completion: gh run watch --exit-status ``` -The orchestrator may dispatch a separate updater run. Open the orchestrator run in the **Actions** tab to follow its correlated worker and inspect the staged output. +The orchestrator may dispatch a separate updater run. Open the orchestrator run in the **Actions** tab to follow its correlated worker and inspect the review output. ## Verify the Result @@ -148,8 +148,8 @@ A successful first run proves the boundary: - the orchestrator selected exactly `TARGET_REPO`; - no more than one updater was dispatched; -- the worker remained in `staged` mode; -- the staged output links back to the control-plane run; +- the worker remained in `review` mode; +- the review output in the control repository links back to the control-plane run; - no issue, pull request, branch, or file was written to the target repository. The worker may report that no dependency work is needed. That is still a successful first run when target selection, routing, and zero-write behavior are correct. @@ -158,7 +158,7 @@ Having trouble? Check [Configure Authentication](authentication.md) for reposito ## What's Next? -- Learn how to promote the operation through [staged, review, and live](rollout-and-routing.md). +- Learn how to promote the operation from [review to live](rollout-and-routing.md). - Read [How the Control Plane Works](architecture.md) before adding organizations or broader repository discovery. - Use the [Configuration Reference](configuration.md) to tune schedules, repository limits, and worker ceilings. - Review [Orchestrators and Workers](orchestrators-and-workers.md) before creating another operation. \ No newline at end of file diff --git a/docs/operations.md b/docs/operations.md index 90b61ad..135a0f3 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -20,7 +20,7 @@ Is unsafe activity active or broadly possible? | +-- yes --> disable Actions, cancel runs, revoke credentials if needed | - +-- no ---> isolate one package or worker, collect evidence, return to staged + +-- no ---> disable one package or worker, collect evidence, resume in review ``` :::danger[Stop first when scope is unclear] @@ -29,13 +29,12 @@ If shared control, authentication, or multiple packages may be affected, use the ## Validate Before Scheduled Live Runs -Before scheduled live operation, run one target through three manual checks: +Before scheduled live operation, run one target through two manual checks: -1. `staged`: verify selection, worker completion, staged outputs, and correlation. -2. `review`: set the worker `MAX_MODE` to `review`; verify the private review destination and no target writes. -3. `live`: set the worker `MAX_MODE` to `live`; use one low-risk target and verify the declared output and downstream CI. +1. `review`: set the worker `MAX_MODE` to `review`; verify the private review destination and no target writes. +2. `live`: set the worker `MAX_MODE` to `live`; use one low-risk target and verify the declared output and downstream CI. -Record the three run URLs and restore the intended worker ceiling after the canary. A failed check returns the worker and package to `staged`. +Record both run URLs and restore the intended worker ceiling after the canary. A failed check disables the affected package and cancels its active runs until it can resume in `review`. Use the same bounded profile in every gate: @@ -44,7 +43,6 @@ target_repo: acme/disposable-canary max_repos: 1 rollout_percent: 100 expected_target_writes: - staged: 0 review: 0 live: declared outputs only ``` @@ -53,19 +51,19 @@ expected_target_writes: Keep the target and repository limits fixed while changing the mode. That makes routing differences attributable to the promotion gate rather than a different repository sample. ::: -The catalog source repository's `Staged smoke` Actions workflow automates the first check for catalog maintainers. It is repository-only test tooling and is not installed by `aw.yml`. Run it manually, select one package, and provide one explicit `OWNER/REPO` target. It dispatches that orchestrator with `max_repos: 1`, `rollout_percent: 100`, and `safe_output_mode: staged`, waits for the orchestrator and correlated workers, and verifies that target issue and branch snapshots remain unchanged. It has no schedule and cannot request review or live processing. +The catalog source repository's `Review smoke` Actions workflow automates the first check for catalog maintainers. It is repository-only test tooling and is not installed by `aw.yml`. Run it manually, select one package, and provide one explicit `OWNER/REPO` target plus a private review repository. It dispatches that orchestrator with `max_repos: 1`, `rollout_percent: 100`, and `safe_output_mode: review`, waits for the orchestrator and correlated workers, and verifies that target issue and branch snapshots remain unchanged. It has no schedule and cannot request live processing. -The repository-only `Enterprise canary` Actions workflow automates all three modes for catalog maintainers while keeping review and live deliberate: +The repository-only `Enterprise canary` Actions workflow automates both modes for catalog maintainers while keeping review and live deliberate: -1. Create repository environments named `central-agentic-ops-staged`, `central-agentic-ops-review`, and `central-agentic-ops-live`. Require reviewers for review and live; restricting deployment branches to the default branch is recommended. +1. Create repository environments named `central-agentic-ops-review` and `central-agentic-ops-live`. Require reviewers for both; restricting deployment branches to the default branch is recommended. 2. Add `GH_AW_E2E_TOKEN` to the environments when the built-in token cannot read the target/review repository or inspect cross-repository refs and issues. Scope it only to the dedicated canary repositories and required metadata, issues, pull requests, contents, and Actions access. 3. Use dedicated disposable target and private review repositories under an allowed owner. Never point review or live canaries at production repositories. -4. For review, enter `REVIEW OWNER/REPO` in `confirmation`; for live, enter `LIVE OWNER/REPO`. Staged requires no confirmation. +4. For review, enter `REVIEW OWNER/REPO` in `confirmation`; for live, enter `LIVE OWNER/REPO`. 5. Leave `require_output` false when a legitimate no-op is acceptable. Set it true only after preparing repository evidence that should deterministically produce a durable output. Review then requires a review-repository change; live requires a target-repository change. -The canary snapshots issues, pull requests (through the issues API), and branch refs before dispatch. Staged and review must leave the target snapshot unchanged. Review may change only its private review destination; live may change only the dedicated target. Repository snapshots are a routing guard, not semantic approval of generated content, so operators must still inspect the output and correlation metadata. +The canary snapshots issues, pull requests (through the issues API), and branch refs before dispatch. Review must leave the target snapshot unchanged and may change only its private review destination; live may change only the dedicated target. Repository snapshots are a routing guard, not semantic approval of generated content, so operators must still inspect the output and correlation metadata. -The repository-only `Enterprise staged stress` workflow sends only `2`, `3`, or `5` same-scope staged runs and requires `STRESS OWNER/REPO RUNS` confirmation plus approval through the `central-agentic-ops-stress` environment. It verifies that concurrency supersedes all but the newest run and that the target snapshot remains unchanged. Real stress remains manual because every run consumes AI Credits; `npm run test:load` supplies the CI-scale test with 100,000 synthetic repositories and no model calls. +The repository-only `Enterprise review stress` workflow sends only `2`, `3`, or `5` same-scope review runs and requires `STRESS OWNER/REPO RUNS` confirmation plus approval through the `central-agentic-ops-stress` environment. It routes outputs to an explicit private review repository, verifies that concurrency supersedes all but the newest run, and confirms that the target snapshot remains unchanged. Real stress remains manual because every run consumes AI Credits; `npm run test:load` supplies the CI-scale test with 100,000 synthetic repositories and no model calls. ## Routine Monitoring @@ -76,7 +74,7 @@ Review the following for scheduled runs: | Authentication | App token or PAT resolves without exposing credential data | | Candidate selection | Targets match package discovery rules and configured limits | | worker workflow eligibility | Installed worker workflows match and disabled worker workflows are skipped | -| safe output routing | staged mode performs no GitHub API writes, review routes privately, and live targets the selected repository | +| safe output routing | review routes privately without target writes, and live targets the selected repository | | Correlation | worker workflow safe outputs identify the orchestrator workflow run | | safe outputs | Type, count, branch, files, and destination stay within declarations | | Quality | safe outputs are actionable, non-duplicative, and supported by evidence | @@ -171,7 +169,6 @@ Pages report destinations are selected by the control-plane mode, while conventi | Mode | Published result | | --- | --- | -| `staged` | No Pages deployment. | | `review` | Access-controlled review Pages in the private `safe_output_repo`. | | `live` | Production Pages. | @@ -185,14 +182,14 @@ To operate a report publisher: Review Pages must be private and access-controlled for the intended reviewers. If the repository plan or policy cannot provide that boundary, review publication fails closed. Never publish review content to a public fallback site. Agents must not receive `pages: write`, `id-token: write`, or authority to promote review content to production. -Changing a package to `staged` prevents new Pages deployments but does not remove an already deployed site. Changing from `live` to `review` redirects future publication to review Pages but does not unpublish production. To stop or roll back either site, disable its conventional Pages workflow, use its protected environment to block deployment, or redeploy a known-good source revision through normal repository procedures. Handle sensitive-data exposure as a Pages incident in addition to stopping the affected agentic package. +Setting a package's `*_ENABLED` variable to `false` prevents new package runs but does not remove an already deployed site. Changing from `live` to `review` redirects future publication to review Pages but does not unpublish production. To stop or roll back either site, disable its conventional Pages workflow, use its protected environment to block deployment, or redeploy a known-good source revision through normal repository procedures. Handle sensitive-data exposure as a Pages incident in addition to stopping the affected agentic package. ## Emergency Stop Disabling GitHub Actions for the private control repository is the control-plane-wide stop. It prevents new orchestrator and worker runs from starting, including manual dispatches. A repository administrator, or an organization or enterprise administrator with authority over Actions policy, should: -:::caution[Mode changes are not an all-stop] -Changing a package variable cannot stop a run that has already started and does not prevent authorized manual dispatches. Disable Actions and cancel active runs when a complete stop is required. +:::caution[Package switches are not an all-stop] +A package kill switch is evaluated only after a workflow starts. It does not cancel active runs or block unrelated packages and workflows. Disable Actions and cancel active runs when a complete stop is required. ::: 1. Open the control repository's **Settings > Actions > General** and disable Actions for the repository. An organization or enterprise administrator may instead apply an Actions policy that disables the repository. @@ -211,7 +208,7 @@ Use narrower controls when a full stop is unnecessary: | Scope | Control | Limitation | | --- | --- | --- | -| One scheduled package | Clear its recognized mode or set it to an unrecognized value | Stops scheduled selection and worker workflow dispatch, but `workflow_dispatch` runs remain possible. | +| One package | Set its `CENTRAL_AGENTIC_OPS__ENABLED` variable to `false` and cancel active runs | Stops scheduled and manual package work after control precomputation; does not cancel work already in progress. | | One Orchestrator or worker workflow | Disable that workflow in GitHub Actions | Other enabled workflows can continue. | | Repository credentials | Revoke the App installation or PAT | Does not itself prevent runs that can use another available credential. | | Entire control plane | Disable Actions for the control repository and cancel active runs | Also stops unrelated Actions workflows in that repository. | @@ -219,9 +216,9 @@ Use narrower controls when a full stop is unnecessary: To resume after an all-stop: 1. Resolve the incident and rotate or narrow credentials when needed. -2. Set every installed package to `staged`. +2. Set every installed package mode to `review` and keep its package kill switch `false`. 3. Re-enable Actions for the control repository. -4. Run one `workflow_dispatch` target with `max_repos: 1` and verify routing, permissions, and safe outputs. +4. Re-enable one package, run one `workflow_dispatch` target with `max_repos: 1`, and verify routing, permissions, and safe outputs. 5. Promote each package independently through the normal review gates. ## Incident Response @@ -229,14 +226,14 @@ To resume after an all-stop: For unexpected writes, unsafe routing, excessive dispatch, or credential concerns: 1. Use the [emergency stop](#emergency-stop) when the incident affects shared control, authentication, or multiple packages. -2. Otherwise, move the affected package to staged mode or clear its recognized mode and disable a specific worker workflow when the incident is worker-local. +2. Otherwise, set the affected package's `*_ENABLED` variable to `false` and disable a specific worker workflow when the incident is worker-local. 3. Cancel active orchestrator and worker runs; mode changes do not alter runs already in progress. 4. Revoke or rotate credentials when exposure is possible. 5. Trace `correlation_id`, `central_repo`, and `control_plane_run_url` across safe outputs. 6. Record affected targets and safe outputs. 7. Revert or close safe outputs through normal repository procedures. 8. Fix and compile the affected workflows. -9. Resume with a one-repository staged run, then review, before returning to live. +9. Resume with a one-repository review run before returning to live. Capture enough evidence to reconstruct the boundary and the outcome: @@ -263,9 +260,9 @@ A catalog maintainer cannot remotely disable workflows already installed in inde 1. publish the affected release or commit and a known-good replacement; 2. identify installations through package manifests and the approved control-repository inventory; -3. move affected packages to `staged` and cancel active runs in every installation; +3. set affected package kill switches to `false` and cancel active runs in every installation; 4. revoke credentials when repository access must stop immediately; -5. pin or restore the known-good package revision, compile affected workflows, and validate one staged target; +5. pin or restore the known-good package revision, compile affected workflows, and validate one review target; 6. update projected catalog versions and lifecycle status after validation; 7. resume each runtime through review and limited-live promotion. @@ -282,7 +279,7 @@ A new package should: 5. Keep GitHub tools read-only. 6. Declare only worker workflow dispatches as orchestrator workflow safe outputs. 7. Document discovery, ranking, dispatch, completion, and no-op behavior. -8. Start in staged mode and complete all promotion gates independently. +8. Start in review mode and complete all promotion gates independently. ## Adding a Worker @@ -294,7 +291,7 @@ A new worker should: 4. Request minimum permissions, tools, network access, and AI credits. 5. Declare narrow safe outputs with explicit count, file, branch, and destination limits. 6. Avoid repository discovery and downstream dispatch. -7. Support staged and review modes before live operation. +7. Support review mode before live operation. 8. Be added to exactly the orchestrators that are allowed to dispatch it. 9. Receive a worker ceiling when its risk or maturity differs from its package peers. @@ -313,7 +310,7 @@ git diff --check - zero compile errors and warnings; - no duplicated workflow-local authentication blocks; - package manifests and docs agree on variables and modes; -- staged and review routing remain fail closed; +- review and live routing remain fail closed; - worker safe-output limits remain intact; - `git diff --check` passes; - compile-generated metadata is handled according to repository policy. diff --git a/docs/orchestrators-and-workers.md b/docs/orchestrators-and-workers.md index cca1fb7..51b7213 100644 --- a/docs/orchestrators-and-workers.md +++ b/docs/orchestrators-and-workers.md @@ -38,13 +38,13 @@ A worker receives one target and performs one bounded mission. It must: - treat control precomputation as authoritative; - analyze only `target_repo`; -- honor `safe_output_mode`, `safe_output_repo`, and `preview_only`; +- honor `safe_output_mode` and `safe_output_repo`; - use only declared permissions, network access, tools, and safe outputs; - include correlation metadata in user-visible outputs when provided; - avoid organization-wide discovery and downstream workflow dispatch; - fail closed when routing or required evidence is incomplete. -The worker may apply stricter behavior than requested, such as returning no output when evidence is insufficient. It may never promote itself from staged to review or live. +The worker may apply stricter behavior than requested, such as returning no output when evidence is insufficient. It may never promote itself from review to live. A worker receives control data shaped like: @@ -52,7 +52,6 @@ A worker receives control data shaped like: target_repo: acme/example-service safe_output_mode: review safe_output_repo: acme/central-agentic-ops-review -preview_only: false correlation_id: dependabot-2026-08-25-001 central_repo: acme/central-agentic-ops control_plane_run_url: https://github.com/acme/central-agentic-ops/actions/runs/123456 @@ -105,12 +104,12 @@ Add worker-specific configuration only when a worker has a materially different | Control | Purpose | Default | | --- | --- | --- | | `enabled` | Explicitly includes or excludes a worker workflow from dispatch | `true` for installed worker workflows | -| `max_mode` | Caps the most permissive mode a worker workflow can execute | `staged` | +| `max_mode` | Caps the most permissive mode a worker workflow can execute | `review` | | worker workflow limit | Caps worker workflow-specific volume or resource use | Existing Agentic Workflow limit | Mode ordering is: -`staged < review < live` +`review < live` The effective worker mode is the less permissive of the requested operation mode and the worker ceiling: diff --git a/docs/rollout-and-routing.md b/docs/rollout-and-routing.md index 1fad968..0a02316 100644 --- a/docs/rollout-and-routing.md +++ b/docs/rollout-and-routing.md @@ -1,39 +1,39 @@ --- title: Roll Out an Operation Safely -description: Promote one operation through staged, review, limited live, and scheduled live operation. +description: Promote one operation from review through limited and scheduled live operation. --- -Roll out each operation independently. Begin with one explicit target in `staged`, inspect the proposed output in `review`, and allow target writes only after the same bounded scenario succeeds in both modes. +Roll out each operation independently. Begin with one explicit target in `review`, inspect the proposal in the private review repository, and allow target writes only after that bounded scenario succeeds. ## Promotion at a Glance -1. Keep the installed operation in `staged` and validate one target. -2. Route the same scenario to a private review destination. +1. Run the installed operation in `review` against one target. +2. Verify the private review destination changed and the target did not. 3. Run one low-risk target in `live` and verify the resulting output and downstream checks. 4. Enable scheduled live operation with `max_repos` kept small. 5. Increase limits only from observed evidence. -Move the operation back to `staged` whenever authentication, routing, output quality, cost, or provenance is uncertain. +Set the package kill switch to `false` whenever authentication, routing, output quality, cost, or provenance is uncertain. Resume in `review` after correcting the issue. -![A control plane promotes bounded operations from staged through review to live across organization repositories.](assets/control-plane-scale.svg) +![A control plane promotes bounded operations from review to live across organization repositories.](assets/control-plane-scale.svg) ```text -staged --inspect--> review --approve--> limited live --observe--> scheduled live - ^ | | | - +--------------------+--------------------+-------------------------+ - uncertainty or failed evidence +review --approve--> limited live --observe--> scheduled live + ^ | | + +-----------------------+-------------------------+ + uncertainty: disable, then review ``` ## Operation-Level Control Each operation has its own mode. Review safe outputs route to the current control-plane repository unless a manual run supplies `safe_output_repo`. This is the primary unit of gradual rollout. -| Operation | Mode variable | Scheduled absolute cap | Rollout percentage variable | -| --- | --- | --- | --- | -| Ambient Context | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ROLLOUT_PERCENT` | -| AW Failures | `CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE` | `CENTRAL_AGENTIC_OPS_AW_FAILURES_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_AW_FAILURES_ROLLOUT_PERCENT` | -| Dependabot | `CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE` | `CENTRAL_AGENTIC_OPS_DEPENDABOT_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_DEPENDABOT_ROLLOUT_PERCENT` | -| Optimization | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE` | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_ROLLOUT_PERCENT` | +| Operation | Kill switch | Mode variable | Scheduled absolute cap | Rollout percentage variable | +| --- | --- | --- | --- | --- | +| Ambient Context | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ENABLED` | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MODE` | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_AMBIENT_CONTEXT_ROLLOUT_PERCENT` | +| AW Failures | `CENTRAL_AGENTIC_OPS_AW_FAILURES_ENABLED` | `CENTRAL_AGENTIC_OPS_AW_FAILURES_MODE` | `CENTRAL_AGENTIC_OPS_AW_FAILURES_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_AW_FAILURES_ROLLOUT_PERCENT` | +| Dependabot | `CENTRAL_AGENTIC_OPS_DEPENDABOT_ENABLED` | `CENTRAL_AGENTIC_OPS_DEPENDABOT_MODE` | `CENTRAL_AGENTIC_OPS_DEPENDABOT_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_DEPENDABOT_ROLLOUT_PERCENT` | +| Optimization | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_ENABLED` | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_MODE` | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_MAX_REPOS` | `CENTRAL_AGENTIC_OPS_OPTIMIZATION_ROLLOUT_PERCENT` | Changing one operation does not change another. For example, Dependabot may be live while Optimization remains in review. @@ -49,7 +49,7 @@ Automatic discovery scans at most `CENTRAL_AGENTIC_OPS_MAX_SCAN_REPOS` repositor Discovery, an allowed owner, and credential access do not prove target enrollment. Before promoting an operation to `live`, add the operation and assigned control repository to `.github/central-agentic-ops.yml` on the target's default branch. Protect that file with target-owner review. Also verify the approved inventory records the target, operation, approving repository owner, review date, and revocation path. -Every live worker reads the target-owned file before agent execution. It fails closed when the file is missing or malformed, the operation is absent, or `authority` does not match the dispatched `central_repo`. Staged and review runs do not require the file. This prevents a second runtime from beginning a new live run for the same operation, but it does not cancel an already-running workflow in another control repository. +Every live worker reads the target-owned file before agent execution. It fails closed when the file is missing or malformed, the operation is absent, or `authority` does not match the dispatched `central_repo`. Review runs do not require the file because they cannot mutate the target. This prevents a second runtime from beginning a new live run for the same operation, but it does not cancel an already-running workflow in another control repository. ```yaml # .github/central-agentic-ops.yml in the target repository @@ -65,27 +65,25 @@ bundles: Require target-owner review for changes to `.github/central-agentic-ops.yml`. Credential access and an allowed owner are not substitutes for target consent. ::: -If an enterprise and organization runtime both select the same pair, keep both in `staged` or `review` until operators assign one live authority. Do not rely on run timing, workflow concurrency, or repository protections to resolve the conflict. Separate control repositories have independent queues and kill switches. +If an enterprise and organization runtime both select the same pair, keep both in `review` until operators assign one live authority. Do not rely on run timing, workflow concurrency, or repository protections to resolve the conflict. Separate control repositories have independent queues and kill switches. ## Modes | Mode | Target behavior | Intended use | | --- | --- | --- | -| `staged` | staged mode generates safe outputs without GitHub API writes | Initial validation, prompt inspection, and policy testing | | `review` | safe outputs route to the current control-plane repository, with an optional manual `safe_output_repo` override | Human review of proposed effects before target mutation | | `live` | Declared worker workflow safe outputs may write to the selected target | Production operation after promotion gates pass | -staged mode is the installation default. Review mode resolves its destination from the manual `safe_output_repo` workflow input, then `github.repository`. Legacy `preview` configuration is normalized to `staged` during migration. +Review mode is the installation default. It resolves its destination from the manual `safe_output_repo` workflow input, then `github.repository`. In review mode, the review repository is not treated as a clone of the target. When a target-bound mutation cannot be represented natively against the review repository, the worker should publish an artifact-backed review bundle describing the target, intended output primitive, base branch, and supporting evidence. ## Pages Report Routing -Pages report routing follows the control-plane modes. Deployment is still conventional deterministic GitHub Actions automation, but the effective mode selects whether there is no site update, an access-controlled review site update, or a production site update. +Pages report routing follows the control-plane modes. Deployment is still conventional deterministic GitHub Actions automation, but the effective mode selects an access-controlled review site update or a production site update. | Mode | Report source behavior | | --- | --- | -| `staged` | Proposed report source data is staged and is not input to the published site. | | `review` | Proposed report source data is routed to the private `safe_output_repo` and published to its access-controlled review Pages site. Production Pages is unchanged. | | `live` | Declared report source data is written to its normal durable destination and published to the production Pages site. | @@ -101,7 +99,7 @@ A `workflow_dispatch` run can set the `target_repo`, `max_repos`, `rollout_perce - specify one `target_repo`; - keep `max_repos` at `1`; -- use `staged` first; +- use `review` first; - use the control-plane repository for scheduled review runs, and use `safe_output_repo` only when a manual run needs a private override; - do not use a manual live run to bypass failed promotion gates. @@ -111,7 +109,7 @@ Example canary inputs: target_repo: acme/example-service max_repos: 1 rollout_percent: 100 -safe_output_mode: staged +safe_output_mode: review safe_output_repo: "" ``` @@ -119,12 +117,11 @@ safe_output_repo: "" Promote each operation independently: -1. **Installed but inactive**: credentials and repository access are configured; schedules must not produce writes. -2. **Enrolled**: record target-owner approval and commit the assigned control repository to the target's protected `.github/central-agentic-ops.yml`. -3. **Staged**: run against one representative repository and inspect selection, prompts, staged safe outputs, permissions, and correlation data. -4. **Review**: route one representative repository to a private review destination; verify that no target mutation occurs and the proposal is actionable. For a Pages report, also verify that the access-controlled review site updates and production Pages does not. -5. **Limited live**: confirm no other control repository has live authority for the same operation, then manually target one low-risk repository and verify the resulting safe output and downstream CI. For a Pages report, verify the production site update independently of the review site. -6. **Scheduled live**: enable scheduled operation with `max_repos` kept small, then increase limits only from observed evidence. +1. **Installed in review**: credentials and repository access are configured; proposals route to the private review destination without target writes. +2. **Review verified**: run against one representative repository; inspect selection, prompts, permissions, correlation data, and the actionable proposal. For a Pages report, also verify that the access-controlled review site updates and production Pages does not. +3. **Enrolled**: record target-owner approval and commit the assigned control repository to the target's protected `.github/central-agentic-ops.yml`. +4. **Limited live**: confirm no other control repository has live authority for the same operation, then manually target one low-risk repository and verify the resulting safe output and downstream CI. For a Pages report, verify the production site update independently of the review site. +5. **Scheduled live**: enable scheduled operation with `max_repos` kept small, then increase limits only from observed evidence. Promotion evidence should cover successful authentication, correct target selection, safe output routing, no unexpected writes, worker workflow completion, useful safe output quality, and acceptable AI Credit consumption. @@ -134,15 +131,15 @@ An operation does not become safer because it remained in a mode for several day ## Rollback -The first rollback action is to move the affected operation to `staged`. For a narrower incident, disable the affected worker workflow so precomputation marks it ineligible. Then: +The first rollback action is to set the affected package's `ENABLED` variable to `false`. For a narrower incident, disable the affected worker workflow so precomputation marks it ineligible. Then: 1. stop new dispatches; 2. inspect the orchestrator run and correlated worker runs; 3. close, revert, or supersede unintended safe outputs using normal repository procedures; 4. if a workflow or package release caused the incident, restore its last known-good Git revision, compile every affected workflow, and deploy that revision through the normal reviewed change process; 5. otherwise, correct the affected policy or worker behavior and compile every affected workflow; -6. restart in staged mode and repeat promotion gates. +6. re-enable the package in review mode and repeat promotion gates. Do not reduce another operation's mode unless the incident involves shared authentication or shared control behavior. -If two runtimes were found mutating the same `(target repository, operation)` pair, move that operation to `staged` in every conflicting control repository, cancel active runs, and assign one live authority before resuming. Stopping only one runtime is insufficient until its queued and in-progress runs are also canceled. +If two runtimes were found mutating the same `(target repository, operation)` pair, disable that operation in every conflicting control repository, cancel active runs, and assign one live authority before resuming in review. Stopping only one runtime is insufficient until its queued and in-progress runs are also canceled. diff --git a/eu-cra-compliance/README.md b/eu-cra-compliance/README.md index 3f7a363..88dd371 100644 --- a/eu-cra-compliance/README.md +++ b/eu-cra-compliance/README.md @@ -20,7 +20,7 @@ The EU CRA Advisor package helps a private Central Agentic Ops control repositor | [`eu-cra-compliance-conformity-release-evidence`](../.github/workflows/eu-cra-compliance-conformity-release-evidence.md) | Audits technical documentation, conformity, declaration, and release-gate evidence. | | [`eu-cra-compliance-package-maintainer`](../.github/workflows/eu-cra-compliance-package-maintainer.md) | Daily audits fleet coverage against the current Act and maintains the implementation ledger. | -The orchestrator dispatches at most 48 repository-level workers per run. Each worker creates at most one evidence issue, uses the shared control plane, and defaults to staged output. +The orchestrator dispatches at most 48 repository-level workers per run. Each worker creates at most one evidence issue, uses the shared control plane, and defaults to review output. The package maintainer runs independently of repository dispatch. It updates the [implementation-status ledger](implementation-status.md) only through a draft pull request and may open at most one deduplicated issue for the highest-priority concrete fleet gap. Installed packages keep the ledger at `.github/aw/eu-cra-compliance/implementation-status.md`. @@ -32,11 +32,12 @@ gh aw add-wizard githubnext/central-agentic-ops/eu-cra-compliance@_ENABLED` kill switch and `_MAX_MODE` ceiling listed in the [configuration reference](../docs/configuration.md). Promote through `staged`, `review`, and then limited `live` only after reviewing evidence handling and credential access. +Each worker has an independent `_ENABLED` kill switch and `_MAX_MODE` ceiling listed in the [configuration reference](../docs/configuration.md). Promote from `review` to limited `live` only after reviewing evidence handling and credential access. ## Safety Boundaries diff --git a/public/assets/control-plane-mental-model-dark.svg b/public/assets/control-plane-mental-model-dark.svg index d2327d5..80d0cf0 100644 --- a/public/assets/control-plane-mental-model-dark.svg +++ b/public/assets/control-plane-mental-model-dark.svg @@ -1,6 +1,6 @@ Central Agentic Ops mental model - A catalog release enters a private control repository, where an orchestrator selects and dispatches work to a worker that emits declared safe outputs in staged, review, or live mode. + A catalog release enters a private control repository, where an orchestrator selects and dispatches work to a worker that emits declared safe outputs in review or live mode. @@ -51,12 +51,10 @@ Declared outputs - - staged - - review - - live + + review + + live Each worker stays bounded to one target and the rollout mode selected by the control plane. diff --git a/public/assets/control-plane-mental-model-light.svg b/public/assets/control-plane-mental-model-light.svg index 8b22451..0c17953 100644 --- a/public/assets/control-plane-mental-model-light.svg +++ b/public/assets/control-plane-mental-model-light.svg @@ -1,6 +1,6 @@ Central Agentic Ops mental model - A catalog release enters a private control repository, where an orchestrator selects and dispatches work to a worker that emits declared safe outputs in staged, review, or live mode. + A catalog release enters a private control repository, where an orchestrator selects and dispatches work to a worker that emits declared safe outputs in review or live mode. @@ -51,12 +51,10 @@ Declared outputs - - staged - - review - - live + + review + + live Each worker stays bounded to one target and the rollout mode selected by the control plane. diff --git a/tests/README.md b/tests/README.md index 233d205..d64b11f 100644 --- a/tests/README.md +++ b/tests/README.md @@ -4,7 +4,7 @@ Use this as a lookup from configuration to verified behavior. Examples assume 25 Run dependency-free contract tests with `npm run test:unit`. Run the networked clean-room package and failure-injection tests with `npm run test:integration`; package tests require gh-aw and public GitHub access. Run synthetic enterprise scale tests with `npm run test:load`. `npm test` runs unit and integration tests, while `npm run check` adds load tests and compilation. CI sets `CENTRAL_AGENTIC_OPS_PACKAGE_SOURCE` to the exact commit under test so package installation validates pull-request contents rather than only the default branch. -The automated suite checks source `.md` contracts, ops-value interfaces, smoke-workflow safety, generated workflows, and `gh aw add`/`gh aw update` package behavior. It does not execute agentic workflows or spend AI Credits; the manual `Staged smoke` Actions workflow performs that opt-in runtime check. +The automated suite checks source `.md` contracts, ops-value interfaces, smoke-workflow safety, generated workflows, and `gh aw add`/`gh aw update` package behavior. It does not execute agentic workflows or spend AI Credits; the manual `Review smoke` Actions workflow performs that opt-in runtime check. ## Test Suite @@ -14,9 +14,9 @@ The automated suite checks source `.md` contracts, ops-value interfaces, smoke-w | Integration | `tests/integration/` | `npm run test:integration` | Clean-room `gh aw add`/`update` behavior and fail-closed execution of the actual control precompute shell. | | Load | `tests/load/` | `npm run test:load` | Actual pagination, deterministic batching, and admission logic over 100,000 synthetic repositories, including bounded API failure. | | Compilation | Source workflows | `npm run compile` | All agentic workflow sources compile without emitting repository artifacts. | -| Runtime staged | `.github/workflows/staged-smoke.yml` | Manual Actions dispatch | One bounded target and its workers complete; target refs and issues remain unchanged. | -| Runtime modes | `.github/workflows/enterprise-canary.yml` | Manual protected Actions dispatch | Repository-local staged/review/live routing against dedicated repositories with mode-specific write assertions. | -| Runtime stress | `.github/workflows/enterprise-stress.yml` | Manual protected Actions dispatch | Repository-local two, three, or five same-scope staged runs verify cancellation and no target mutation. | +| Runtime review | `.github/workflows/review-smoke.yml` | Manual Actions dispatch | One bounded target and its workers complete; outputs route to a private review repository and target refs and issues remain unchanged. | +| Runtime modes | `.github/workflows/enterprise-canary.yml` | Manual protected Actions dispatch | Repository-local review/live routing against dedicated repositories with mode-specific write assertions. | +| Runtime stress | `.github/workflows/enterprise-stress.yml` | Manual protected Actions dispatch | Repository-local two, three, or five same-scope review runs verify cancellation and no target mutation. | | Ops Publish | `tests/unit/ops-publish*.test.mjs` | `node --test tests/unit/ops-publish*.test.mjs` | Reviewer, provenance, routing, authority, least-privilege, API failure, retry, pagination, and publication contracts. | ## Package Lifecycle Integration @@ -32,18 +32,18 @@ The integration suite creates disposable consumer repositories under the system | Test result | Scenario | Checked behavior | | --- | --- | --- | -| 🟢 Pass | Invalid scope, mode, correlation, caps, and budgets | The actual control precompute shell rejects 12 malformed or unauthorized inputs before execution. | +| 🟢 Pass | Control validation and authorization | The actual control precompute shell passes 54 success, failure, disablement, review-isolation, live-authorization, and output-binding cases. | | 🟢 Pass | 100,000-repository inventory | Pagination stops at 1,000 pages, retains exactly 100,000 candidates, and applies the 10%/1,000 target cap within 120 seconds. | | 🟢 Pass | Deterministic cell and batch selection | Stable repository IDs assign every selected candidate to one cell; bounded batches share an inventory version and have distinct batch IDs. | | 🟢 Pass | Inventory API rate limit | Organization and user lookup each run once, then produce zero candidates, zero target capacity, and a durable error instead of retrying. | -| Manual | Staged canary | Orchestrator and correlated workers complete while target issue/ref snapshots remain identical. | +| Manual | Review smoke | Orchestrator and correlated workers complete, outputs route privately, and target issue/ref snapshots remain identical. | | Manual, approved | Review canary | Target remains unchanged; optional `require_output` asserts a durable proposal in the private review repository. | | Manual, approved | Live canary | Optional `require_output` asserts a durable issue, pull request, branch, or comment change in the dedicated target. | -| Manual, approved | Staged stress | Bounded same-scope runs are superseded by concurrency controls and do not mutate the target. | +| Manual, approved | Review stress | Bounded same-scope runs are superseded by concurrency controls and do not mutate the target. | ## Modes -Mode controls how declared [safe outputs](https://github.github.com/gh-aw/reference/glossary/#safe-outputs) are processed: simulated without GitHub API writes in [staged mode](https://github.github.com/gh-aw/reference/glossary/#staged-mode) (`staged`), routed to a review repository (`review`), or processed against their live destination (`live`). All triggers use the same three modes; the trigger determines where the mode is read from. +Mode controls how declared [safe outputs](https://github.github.com/gh-aw/reference/glossary/#safe-outputs) are processed: routed to a private review repository (`review`) or processed against their authorized live destination (`live`). All triggers use the same two modes; the trigger determines where the mode is read from. ### Trigger: Schedule (`on.schedule`) @@ -51,7 +51,6 @@ Schedule-triggered runs use the configured package mode. | Test result | Configured mode | Checked scheduled behavior | | --- | --- | --- | -| 🟢 Pass | `staged` | safe outputs are staged; no GitHub API writes are performed. | | 🟢 Pass | `review` | safe outputs route to the control-plane repository. | | 🟢 Pass | `live` | Declared safe outputs may be processed against the live destination. | @@ -61,10 +60,9 @@ Manual-triggered runs use the `safe_output_mode` workflow input. They run indepe | Test result | `safe_output_mode` workflow input | Checked behavior | | --- | --- | --- | -| 🟢 Pass | `staged` | Run starts in staged mode; safe outputs perform no GitHub API writes. | | 🟢 Pass | `review` | Run starts and safe outputs default to the control-plane repository. | | 🟢 Pass | `live` | Run starts and declared safe outputs may target the live destination. | -| 🟢 Pass | Any recognized mode with scheduled mode disabled | Run starts independently of scheduled configuration. | +| 🟢 Pass | Either mode with package enabled | Run starts independently of scheduled configuration. | ## Routing safe outputs for Review @@ -74,7 +72,6 @@ Review routing sends proposed safe outputs to the explicit review destination wh | --- | --- | --- | --- | | 🟢 Pass | `review` | provided | workflow input repository used. | | 🟢 Pass | `review` | - | Current control-plane repository used. | -| 🟢 Pass | `staged` | provided | Repository ignored; safe outputs are staged. | | 🟢 Pass | `live` | provided | Repository ignored; live routing used. | ## Setting Absolute Caps @@ -83,8 +80,6 @@ Review routing sends proposed safe outputs to the explicit review destination wh | Test result | Effective mode | Rollout | `max_repos` | Checked limit | | --- | --- | --- | --- | --- | -| 🟢 Pass | `staged` | 100% | 1 | 1; safe outputs staged. | -| 🟢 Pass | `staged` | 100% | 10 | 10; safe outputs staged. | | 🟢 Pass | `review` | 100% | 1 | 1; safe outputs routed for review. | | 🟢 Pass | `review` | 100% | 10 | 10; safe outputs routed for review. | | 🟢 Pass | `live` | 100% | 1 | 1. | @@ -98,7 +93,6 @@ Review routing sends proposed safe outputs to the explicit review destination wh | Test result | Effective mode | Rollout | `max_repos` | Checked limit | | --- | --- | --- | --- | --- | -| 🟢 Pass | `staged` | 10% | - | 3; safe outputs staged. | | 🟢 Pass | `review` | 10% | - | 3; safe outputs routed for review. | | 🟢 Pass | `live` | 10% | - | 3. | | 🟢 Pass | Any | 100% | 1000 | 25. | @@ -121,24 +115,26 @@ Invalid caps, out-of-scope owners, and incomplete control facts stop before work | 🟢 Pass | `max_scan_repos` below `1` or above `100000` | Rejected. | | 🟢 Pass | Invalid cell count/index or batch size/index | Rejected. | | 🟢 Pass | Target or review repository outside `CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS` | Rejected. | -| 🟢 Pass | Unknown scheduled mode | Scheduled package disabled. | -| 🟢 Pass | Legacy `preview` mode | Normalized to staged mode; safe outputs perform no GitHub API writes. | +| 🟢 Pass | Unknown or removed mode | Rejected before agent execution. | +| 🟢 Pass | Invalid package kill-switch value | Rejected before agent execution. | +| 🟢 Pass | Package kill switch set to `false` | Produces zero capacity and dispatches without repository inspection. | +| 🟢 Pass | Missing worker target or non-positive correlation ID | Rejected before target or review repository access. | ## Enterprise Safety | Test result | Scenario | Checked behavior | | --- | --- | --- | -| 🟢 Pass | Missing settings | staged mode, one target, 1000-repository scan ceiling, control-owner allowlist. | +| 🟢 Pass | Missing settings | review mode, one target, 1000-repository scan ceiling, control-owner allowlist. | | 🟢 Pass | Inventory up to 1,000,000 repositories | Selection remains within absolute, percentage, and dispatch caps. | | 🟢 Pass | Optimization with two eligible workers | 20-dispatch budget permits at most 10 targets. | | 🟢 Pass | All workers disabled | Effective target cap is zero; no dispatch. | | 🟢 Pass | Duplicate workflow display names | Workers resolve only by exact generated path; analytics group by workflow path. | | 🟢 Pass | Enterprise and organization planes target the same repository | Independent provenance, policy, credentials, and kill switches are preserved. | | 🟢 Pass | Direct worker dispatch | Target and safe-output owners still pass the trusted allowlist. | -| 🟢 Pass | Worker ceiling omitted | Worker remains enabled but staged-only. | +| 🟢 Pass | Worker ceiling omitted | Worker remains enabled but review-only. | | 🟢 Pass | Review destination is public or inaccessible | Rejected before agent execution. | | 🟢 Pass | Aggregate AI Credit request exceeds `1100` default | Repository selection is reduced to fit the shared cap. | -| 🟢 Pass | Public targets without an App or PAT | Built-in `GITHUB_TOKEN` supports bounded staged scans; private access, alternate review repositories, and live target writes remain prohibited. | +| 🟢 Pass | Public targets without an App or PAT | Built-in `GITHUB_TOKEN` supports bounded review runs in the control repository; private access, alternate review repositories, and live target writes remain prohibited. | | 🟢 Pass | Runaway prevention | Every workflow has finite AI credits and timeout; overlapping same-scope runs cancel. | | 🟢 Pass | API rate limit or budget exhaustion | No internal retry/wait loop or self-dispatch; unresolved work is incomplete and requires a new bounded run. | | 🟢 Pass | Same-scope queue pressure | Newest run supersedes older running or pending work; no unbounded Actions backlog. | @@ -155,11 +151,11 @@ Compilation checks prove the source policy reaches the generated GitHub Actions | 🟢 Pass | Release Train Updater | Standard dispatch envelope and safe output settings compile. | | 🟢 Pass | AI Credit Auditor | Standard dispatch envelope and safe output settings compile. | | 🟢 Pass | AI Credit Optimizer | Standard dispatch envelope and safe output settings compile. | -| 🟢 Pass | All worker workflow safe outputs | staged mode and review/live routing vocabulary checked. | -| 🟢 Pass | All generated workflows | Emitted GitHub Actions settings checked in a clean-room compile. | +| 🟢 Pass | All worker workflow safe outputs | Review/live routing vocabulary checked. | +| 🟢 Pass | All generated package workflows | Emitted activation gates, transitive job dependencies, review isolation, live authority, output binding, and removed-mode settings checked in a clean-room compile. | | 🟢 Pass | Core catalog package | Installs no Pages workflow, renderer, or Pages permission surface. | | 🟢 Pass | Operational value | Schema-v4 evaluators are registered by workers and Pages consumes actual `grader_results.json` observations. | | 🟢 Pass | Pages add-on | Conventional publisher remains outside the reusable Agentic Workflow packages. | | 🟡 Upstream blocked | Grader package transport | The gh-aw operational-value merge commit does not install referenced `.github/graders/*.sh` files into a clean package consumer. | -Exhaustive coverage: 18 scheduled plus 108 manual cases, for 126 unique policy configurations. \ No newline at end of file +Exhaustive coverage: 24 scheduled plus 96 manual cases, for 120 unique policy configurations and 22 user-facing scenarios. The custom review-bundle job retains gh-aw's internal `GH_AW_SAFE_OUTPUTS_STAGED` dry-run signal; it is compiler plumbing, not a public package mode. \ No newline at end of file diff --git a/tests/e2e/run-canary.sh b/tests/e2e/run-canary.sh index 983594a..74ee90f 100644 --- a/tests/e2e/run-canary.sh +++ b/tests/e2e/run-canary.sh @@ -43,9 +43,6 @@ esac || { printf 'target_repo must use OWNER/REPO form\n' >&2; exit 1; } case "$SAFE_OUTPUT_MODE" in - staged) - [[ -z "$SAFE_OUTPUT_REPO" ]] || { printf 'staged mode does not accept safe_output_repo\n' >&2; exit 1; } - ;; review) [[ "$SAFE_OUTPUT_REPO" =~ ^[A-Za-z0-9][A-Za-z0-9-]*/[A-Za-z0-9._-]+$ ]] \ || { printf 'review mode requires safe_output_repo in OWNER/REPO form\n' >&2; exit 1; } @@ -61,7 +58,7 @@ case "$SAFE_OUTPUT_MODE" in [[ "$CONFIRMATION" == "LIVE $TARGET_REPO" ]] \ || { printf 'confirmation must be LIVE %s\n' "$TARGET_REPO" >&2; exit 1; } ;; - *) printf 'safe_output_mode must be staged, review, or live\n' >&2; exit 1 ;; + *) printf 'safe_output_mode must be review or live\n' >&2; exit 1 ;; esac case "$REQUIRE_OUTPUT" in @@ -142,10 +139,6 @@ done < "$worker_ids_file" target_after=$(snapshot_repository "$TARGET_REPO") case "$SAFE_OUTPUT_MODE" in - staged) - [[ "$target_after" == "$target_before" ]] \ - || { printf 'staged canary mutated target repository state\n' >&2; exit 1; } - ;; review) [[ "$target_after" == "$target_before" ]] \ || { printf 'review canary mutated target repository state\n' >&2; exit 1; } diff --git a/tests/e2e/run-stress.sh b/tests/e2e/run-stress.sh index 03bbb78..cb487e0 100644 --- a/tests/e2e/run-stress.sh +++ b/tests/e2e/run-stress.sh @@ -3,6 +3,7 @@ set -euo pipefail : "${BUNDLE:?BUNDLE is required}" : "${TARGET_REPO:?TARGET_REPO is required}" +: "${SAFE_OUTPUT_REPO:?SAFE_OUTPUT_REPO is required}" : "${CONTROL_REF:?CONTROL_REF is required}" : "${RUNS:?RUNS is required}" : "${CONFIRMATION:?CONFIRMATION is required}" @@ -20,8 +21,14 @@ case "$RUNS" in esac [[ "$TARGET_REPO" =~ ^[A-Za-z0-9][A-Za-z0-9-]*/[A-Za-z0-9._-]+$ ]] \ || { printf 'target_repo must use OWNER/REPO form\n' >&2; exit 1; } -[[ "$CONFIRMATION" == "STRESS $TARGET_REPO $RUNS" ]] \ - || { printf 'confirmation must be STRESS %s %s\n' "$TARGET_REPO" "$RUNS" >&2; exit 1; } +[[ "$SAFE_OUTPUT_REPO" =~ ^[A-Za-z0-9][A-Za-z0-9-]*/[A-Za-z0-9._-]+$ ]] \ + || { printf 'safe_output_repo must use OWNER/REPO form\n' >&2; exit 1; } +[[ "$SAFE_OUTPUT_REPO" != "$TARGET_REPO" ]] \ + || { printf 'review and target repositories must differ\n' >&2; exit 1; } +[[ "$CONFIRMATION" == "STRESS $TARGET_REPO REVIEW $SAFE_OUTPUT_REPO $RUNS" ]] \ + || { printf 'confirmation must be STRESS %s REVIEW %s %s\n' "$TARGET_REPO" "$SAFE_OUTPUT_REPO" "$RUNS" >&2; exit 1; } +[[ $(gh api "repos/$SAFE_OUTPUT_REPO" --jq '.private') == true ]] \ + || { printf 'review repository must be private\n' >&2; exit 1; } snapshot_repository() { local repository=$1 @@ -37,9 +44,10 @@ for _ in $(seq 1 "$RUNS"); do gh workflow run "$workflow_file" \ --ref "$CONTROL_REF" \ --raw-field "target_repo=$TARGET_REPO" \ + --raw-field "safe_output_repo=$SAFE_OUTPUT_REPO" \ --raw-field "max_repos=1" \ --raw-field "rollout_percent=100" \ - --raw-field "safe_output_mode=staged" + --raw-field "safe_output_mode=review" done run_ids_file=$(mktemp) @@ -75,12 +83,13 @@ done < "$run_ids_file" [[ "$cancelled" -ge $((RUNS - 1)) ]] \ || { printf 'Expected at least %s superseded runs, observed %s\n' "$((RUNS - 1))" "$cancelled" >&2; exit 1; } [[ $(snapshot_repository "$TARGET_REPO") == "$target_before" ]] \ - || { printf 'staged stress run mutated target repository state\n' >&2; exit 1; } + || { printf 'review stress run mutated target repository state\n' >&2; exit 1; } { printf '## Enterprise stress canary\n' printf -- '- Package: `%s`\n' "$BUNDLE" printf -- '- Target: `%s`\n' "$TARGET_REPO" + printf -- '- Review destination: `%s`\n' "$SAFE_OUTPUT_REPO" printf -- '- Requested runs: `%s`\n' "$RUNS" printf -- '- Superseded runs: `%s`\n' "$cancelled" } >> "$GITHUB_STEP_SUMMARY" \ No newline at end of file diff --git a/tests/helpers/control-precompute.mjs b/tests/helpers/control-precompute.mjs index a075869..aa9cc58 100644 --- a/tests/helpers/control-precompute.mjs +++ b/tests/helpers/control-precompute.mjs @@ -46,12 +46,11 @@ export function controlEnvironment(overrides = {}) { ALLOWED_REPOS: "", DISPATCH_MAX: "1", ROLLOUT_PERCENT: "100", - SAFE_OUTPUT_MODE: "staged", - SAFE_OUTPUT_REPO: "", - PREVIEW_ONLY: "true", + SAFE_OUTPUT_MODE: "review", + SAFE_OUTPUT_REPO: "acme/control", ENABLED: "true", WORKER_ENABLED: "true", - WORKER_MAX_MODE: "staged", + WORKER_MAX_MODE: "review", CORRELATION_ID: "123-1", CENTRAL_REPO: "acme/control", CONTROL_PLANE_RUN_URL: "https://github.com/acme/control/actions/runs/123", diff --git a/tests/integration/control-failure.test.mjs b/tests/integration/control-failure.test.mjs index d764ea1..2a7d779 100644 --- a/tests/integration/control-failure.test.mjs +++ b/tests/integration/control-failure.test.mjs @@ -10,11 +10,18 @@ const script = controlPrecomputeScript(); const failures = [ ["malformed target repository", { TARGET_REPO: "not-a-repository" }, "target_repo must use owner/repository form"], + ["missing worker target repository", { TARGET_REPO: "" }, "worker target_repo is required"], ["disallowed target owner", { TARGET_REPO: "outside/target" }, "target_repo owner is outside CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS"], + ["malformed review repository", { SAFE_OUTPUT_REPO: "not-a-repository" }, "safe_output_repo must use owner/repository form"], + ["disallowed review owner", { SAFE_OUTPUT_REPO: "outside/review" }, "safe_output_repo owner is outside CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS"], ["disabled worker", { WORKER_ENABLED: "false" }, "worker is disabled by its control-plane policy"], - ["worker mode ceiling", { SAFE_OUTPUT_MODE: "live", PREVIEW_ONLY: "false" }, "safe_output_mode exceeds the worker_max_mode ceiling"], - ["inconsistent staged flag", { PREVIEW_ONLY: "false" }, "preview_only is inconsistent with safe_output_mode"], + ["worker mode ceiling", { SAFE_OUTPUT_MODE: "live", SAFE_OUTPUT_REPO: "acme/target" }, "safe_output_mode exceeds the worker_max_mode ceiling"], + ["invalid worker kill switch", { WORKER_ENABLED: "False" }, "worker_enabled must be true or false"], + ["removed worker ceiling", { WORKER_MAX_MODE: "preview" }, "worker_max_mode must be review or live"], + ["invalid safe-output mode", { SAFE_OUTPUT_MODE: "staged" }, "safe_output_mode must be review or live"], + ["invalid package kill switch", { ENABLED: "invalid" }, "enabled must be true or false"], ["invalid correlation ID", { CORRELATION_ID: "invalid" }, "correlation_id must identify an orchestrator run and attempt"], + ["zero correlation ID", { CORRELATION_ID: "0-0" }, "correlation_id must identify an orchestrator run and attempt"], ["mismatched control repository", { CENTRAL_REPO: "acme/other" }, "central_repo must identify the current control repository"], ["mismatched control run URL", { CONTROL_PLANE_RUN_URL: "https://github.com/acme/control/actions/runs/999" }, "control_plane_run_url must match correlation_id and central_repo"], ["oversized repository cap", { ROLE: "orchestrator", TARGET_REPO: "", MAX_REPOS: "1001" }, "max_repos must be an integer from 1 through 1000"], @@ -27,18 +34,121 @@ const failures = [ ["invalid credit budget", { ROLE: "orchestrator", TARGET_REPO: "", AGGREGATE_CREDIT_LIMIT: "0" }, "AI Credit admission values must be integers"], ]; -for (const [name, overrides, expectedError] of failures) { - test(`control precompute rejects ${name}`, () => { - const result = spawnSync("bash", ["-c", script], { +function runPrecompute(overrides = {}, ghScript = "printf 'true\\n'") { + const directory = mkdtempSync(join(tmpdir(), "central-ops-precompute-")); + const gh = join(directory, "gh"); + writeFileSync(gh, `#!/bin/sh +${ghScript} +`); + chmodSync(gh, 0o755); + + try { + return spawnSync("bash", ["-c", script], { encoding: "utf8", - env: controlEnvironment(overrides), + env: controlEnvironment({ + PATH: `${directory}:${process.env.PATH}`, + ...overrides, + }), }); + } finally { + rmSync(directory, { recursive: true, force: true }); + } +} + +for (const [name, overrides, expectedError] of failures) { + test(`control precompute rejects ${name}`, () => { + const result = runPrecompute(overrides); assert.notEqual(result.status, 0, `${name} unexpectedly succeeded`); assert.match(result.stderr, new RegExp(expectedError)); }); } +for (const role of ["orchestrator", "worker"]) { + for (const safeOutputMode of ["review", "live"]) { + test(`control precompute disables a ${role} in ${safeOutputMode} before validation or repository access`, () => { + const result = runPrecompute( + { + ENABLED: "false", + ROLE: role, + SAFE_OUTPUT_MODE: safeOutputMode, + TARGET_REPO: "not-a-repository", + SAFE_OUTPUT_REPO: "also-invalid", + }, + "echo 'GitHub must not be called for a disabled package' >&2; exit 99", + ); + + assert.equal(result.status, 0, result.stderr); + const precompute = JSON.parse(readFileSync("/tmp/gh-aw/agent/control-precompute.json", "utf8")); + const dispatchPrecompute = JSON.parse(readFileSync("/tmp/gh-aw/agent/dispatch-precompute.json", "utf8")); + assert.deepEqual(precompute, dispatchPrecompute); + assert.equal(precompute.control_role, role); + assert.equal(precompute.enabled, "false"); + assert.equal(precompute.effective_max_repos, 0); + assert.deepEqual(precompute.candidate_repositories, []); + assert.deepEqual(precompute.worker_workflows, []); + }); + } +} + +test("control precompute disables a worker before review repository access", () => { + const result = runPrecompute( + { WORKER_ENABLED: "false" }, + "echo 'GitHub must not be called for a disabled worker' >&2; exit 99", + ); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /worker is disabled by its control-plane policy/); + assert.doesNotMatch(result.stderr, /GitHub must not be called/); +}); + +for (const safeOutputMode of ["preview", "preview_only", "Review", "LIVE", "review "]) { + test(`control precompute rejects unsupported mode ${JSON.stringify(safeOutputMode)}`, () => { + const result = runPrecompute({ SAFE_OUTPUT_MODE: safeOutputMode }); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /safe_output_mode must be review or live/); + }); +} + +test("control precompute writes a complete mirrored review worker envelope", () => { + const result = runPrecompute(); + + assert.equal(result.status, 0, result.stderr); + const precompute = JSON.parse(readFileSync("/tmp/gh-aw/agent/control-precompute.json", "utf8")); + const dispatchPrecompute = JSON.parse(readFileSync("/tmp/gh-aw/agent/dispatch-precompute.json", "utf8")); + assert.deepEqual(precompute, dispatchPrecompute); + assert.deepEqual(precompute, { + control_role: "worker", + bundle: "dependabot", + enabled: "true", + worker_enabled: "true", + worker_max_mode: "review", + target_repo: "acme/target", + safe_output_mode: "review", + safe_output_repo: "acme/control", + correlation_id: "123-1", + central_repo: "acme/control", + control_plane_run_url: "https://github.com/acme/control/actions/runs/123", + candidate_repositories: [], + worker_workflows: [], + }); +}); + +test("control precompute rejects an inaccessible review destination", () => { + const result = runPrecompute({}, "exit 1"); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /review safe_output_repo must be accessible/); +}); + +test("control precompute rejects a public review destination", () => { + const result = runPrecompute({}, "printf 'false\\n'"); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /review safe_output_repo must be private/); +}); + function runLiveAuthority(authorityContent, overrides = {}) { const directory = mkdtempSync(join(tmpdir(), "central-ops-authority-")); const gh = join(directory, "gh"); @@ -59,7 +169,7 @@ esac env: controlEnvironment({ PATH: `${directory}:${process.env.PATH}`, SAFE_OUTPUT_MODE: "live", - PREVIEW_ONLY: "false", + SAFE_OUTPUT_REPO: "acme/target", WORKER_MAX_MODE: "live", AUTHORITY_CONTENT: authorityContent, ...overrides, @@ -82,6 +192,36 @@ bundles: assert.equal(precompute.bundle, "dependabot"); }); +test("control precompute accepts live authority case-insensitively", () => { + const result = runLiveAuthority(`version: 1 +bundles: + dependabot: + authority: ACME/CONTROL +`); + + assert.equal(result.status, 0, result.stderr); +}); + +for (const safeOutputRepo of ["acme/target", "ACME/TARGET"]) { + test(`control precompute rejects review destination ${safeOutputRepo} when it is the target`, () => { + const result = runPrecompute({ SAFE_OUTPUT_REPO: safeOutputRepo }); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /review safe_output_repo must differ from target_repo/); + }); +} + +test("control precompute binds live worker output to the authorized target", () => { + const result = runLiveAuthority(`version: 1 +bundles: + dependabot: + authority: acme/control +`, { SAFE_OUTPUT_REPO: "acme/other" }); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /live worker safe_output_repo must equal target_repo/); +}); + test("control precompute rejects a different live authority", () => { const result = runLiveAuthority(`version: 1 bundles: @@ -93,6 +233,37 @@ bundles: assert.match(result.stderr, /target assigns live authority for dependabot to a different control repository/); }); +for (const [name, authorityContent] of [ + ["empty document", ""], + ["non-object document", "- dependabot\n"], + ["wrong version", "version: 2\nbundles: {}\n"], + ["string version", "version: '1'\nbundles: {}\n"], + ["missing bundles", "version: 1\n"], + ["non-object bundles", "version: 1\nbundles: []\n"], + ["missing package", "version: 1\nbundles:\n optimization:\n authority: acme/control\n"], + ["non-object package", "version: 1\nbundles:\n dependabot: acme/control\n"], + ["non-string authority", "version: 1\nbundles:\n dependabot:\n authority: 1\n"], + ["YAML alias", "version: 1\ndefaults: &defaults\n authority: acme/control\nbundles:\n dependabot: *defaults\n"], +]) { + test(`control precompute rejects live authority with ${name}`, () => { + const result = runLiveAuthority(authorityContent); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /target authority file must declare version 1 and bundles.dependabot.authority/); + }); +} + +test("control precompute rejects malformed live authority repository", () => { + const result = runLiveAuthority(`version: 1 +bundles: + dependabot: + authority: not-a-repository +`); + + assert.notEqual(result.status, 0); + assert.match(result.stderr, /bundles.dependabot.authority must use owner\/repository form/); +}); + test("control precompute rejects missing target-owned live authority", () => { const result = runLiveAuthority("", { AUTHORITY_MODE: "missing" }); diff --git a/tests/integration/package-lifecycle.test.mjs b/tests/integration/package-lifecycle.test.mjs index ee4119d..e9c88c2 100644 --- a/tests/integration/package-lifecycle.test.mjs +++ b/tests/integration/package-lifecycle.test.mjs @@ -71,7 +71,7 @@ const repositoryOnlyFiles = [ ".github/aw/e2e/run-stress.sh", ".github/workflows/enterprise-canary.yml", ".github/workflows/enterprise-stress.yml", - ".github/workflows/staged-smoke.yml", + ".github/workflows/review-smoke.yml", ]; function run(command, args, cwd) { diff --git a/tests/load/control-plane-load.test.mjs b/tests/load/control-plane-load.test.mjs index 9508f1a..88af58a 100644 --- a/tests/load/control-plane-load.test.mjs +++ b/tests/load/control-plane-load.test.mjs @@ -19,7 +19,9 @@ function mockGh(directory) { writeFileSync(executable, `#!/bin/bash set -euo pipefail arguments="$*" -if [[ "$arguments" == *"contents/.github/workflows/dependabot.md"* ]]; then +if [[ "$arguments" == "api repos/acme/control --jq .private" ]]; then + printf 'true\n' +elif [[ "$arguments" == *"contents/.github/workflows/dependabot.md"* ]]; then printf '%s\\n' "$CONTROL_SOURCE_B64" elif [[ "$arguments" == *"actions/workflows?per_page=100"* ]]; then printf '%s\\n' '{"id":1,"name":"worker","path":".github/workflows/worker.lock.yml","state":"active"}' diff --git a/tests/unit/workflow-contract.matrix.mjs b/tests/unit/workflow-contract.matrix.mjs index 75b9aae..2dd8d62 100644 --- a/tests/unit/workflow-contract.matrix.mjs +++ b/tests/unit/workflow-contract.matrix.mjs @@ -1,156 +1,145 @@ export const policyMatrix = Object.freeze({ - configuredModes: ["staged", "review", "live"], - selectedModes: ["staged", "review", "live"], + configuredModes: ["review", "live"], + selectedModes: ["review", "live"], manualReviewRepos: ["", "acme/manual-review"], rolloutPercents: [10, 100], maxReposValues: [1, 10, 1000], + packageEnabledValues: [true, false], totalRepositories: 25, }); export const userFacingScenarios = Object.freeze([ - { - group: "Scheduled modes", - name: "Scheduled default staged", - inputs: { eventName: "schedule", configuredMode: "staged", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 1, dispatchAllowed: true }, - }, - { - group: "Scheduled modes", - name: "Scheduled staged at 10 percent", - inputs: { eventName: "schedule", configuredMode: "staged", manualMode: "", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1000 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 3, dispatchAllowed: true }, - }, - { - group: "Scheduled modes", - name: "Scheduled staged ignores review destination", - inputs: { eventName: "schedule", configuredMode: "staged", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 10, dispatchAllowed: true }, - }, { group: "Scheduled modes", name: "Scheduled review uses control repository", inputs: { eventName: "schedule", configuredMode: "review", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", previewOnly: false, effectiveMaxRepos: 10, dispatchAllowed: true }, - }, - { - group: "Scheduled modes", - name: "Scheduled review defaults to control repository", - inputs: { eventName: "schedule", configuredMode: "review", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", previewOnly: false, effectiveMaxRepos: 10, dispatchAllowed: true }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", effectiveMaxRepos: 10, dispatchAllowed: true }, }, { group: "Scheduled modes", name: "Scheduled review at 10 percent", inputs: { eventName: "schedule", configuredMode: "review", manualMode: "", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1000 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", previewOnly: false, effectiveMaxRepos: 3, dispatchAllowed: true }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", effectiveMaxRepos: 3, dispatchAllowed: true }, }, { group: "Scheduled modes", name: "Scheduled live at 100 percent", inputs: { eventName: "schedule", configuredMode: "live", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1000 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 25, dispatchAllowed: true }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 25, dispatchAllowed: true }, }, { group: "Scheduled modes", name: "Scheduled live at 10 percent", inputs: { eventName: "schedule", configuredMode: "live", manualMode: "", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1000 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 3, dispatchAllowed: true }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 3, dispatchAllowed: true }, }, { group: "Rollout limits", name: "Absolute cap is stricter than 10 percent", inputs: { eventName: "schedule", configuredMode: "live", manualMode: "", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 1, dispatchAllowed: true }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 1, dispatchAllowed: true }, }, { group: "Manual runs", - name: "Manual staged runs while schedules are configured live", - inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "staged", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 1, dispatchAllowed: true }, - }, - { - group: "Manual runs", - name: "Manual review runs while schedules are configured staged", - inputs: { eventName: "workflow_dispatch", configuredMode: "staged", manualMode: "review", manualReviewRepo: "acme/manual-review", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/manual-review", previewOnly: false, effectiveMaxRepos: 1, dispatchAllowed: true }, + name: "Manual review overrides scheduled live", + inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "review", manualReviewRepo: "acme/manual-review", rolloutPercent: 100, maxRepos: 1 }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/manual-review", effectiveMaxRepos: 1, dispatchAllowed: true }, }, { group: "Review routing", name: "Manual review defaults to control repository", inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "review", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", previewOnly: false, effectiveMaxRepos: 1, dispatchAllowed: true }, - }, - { - group: "Review routing", - name: "Manual review uses control repository while schedules are staged", - inputs: { eventName: "workflow_dispatch", configuredMode: "staged", manualMode: "review", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", previewOnly: false, effectiveMaxRepos: 1, dispatchAllowed: true }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", effectiveMaxRepos: 1, dispatchAllowed: true }, }, { group: "Review routing", name: "Manual destination overrides control repository", inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "review", manualReviewRepo: "acme/manual-review", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/manual-review", previewOnly: false, effectiveMaxRepos: 1, dispatchAllowed: true }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/manual-review", effectiveMaxRepos: 1, dispatchAllowed: true }, }, { group: "Rollout limits", name: "Manual live at 10 percent", - inputs: { eventName: "workflow_dispatch", configuredMode: "staged", manualMode: "live", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 10 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 3, dispatchAllowed: true }, + inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "live", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 10 }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 3, dispatchAllowed: true }, }, { group: "Rollout limits", name: "Manual live with absolute cap", - inputs: { eventName: "workflow_dispatch", configuredMode: "staged", manualMode: "live", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 10, dispatchAllowed: true }, + inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "live", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 10, dispatchAllowed: true }, }, { group: "Manual runs", name: "Manual live runs while schedules are configured review", inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "live", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1000 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 25, dispatchAllowed: true }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 25, dispatchAllowed: true }, }, { group: "Manual runs", - name: "Manual staged runs while schedules are configured review", - inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "staged", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 10, dispatchAllowed: true }, + name: "Manual review runs while schedules are configured review", + inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "review", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", effectiveMaxRepos: 10, dispatchAllowed: true }, }, { group: "Review routing", - name: "Manual staged ignores manual destination", - inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "staged", manualReviewRepo: "acme/manual-review", rolloutPercent: 100, maxRepos: 10 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 10, dispatchAllowed: true }, + name: "Scheduled review defaults to control repository", + inputs: { eventName: "schedule", configuredMode: "review", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1 }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", effectiveMaxRepos: 1, dispatchAllowed: true }, }, { group: "Review routing", name: "Manual live ignores manual destination", - inputs: { eventName: "workflow_dispatch", configuredMode: "staged", manualMode: "live", manualReviewRepo: "acme/manual-review", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 1, dispatchAllowed: true }, + inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "live", manualReviewRepo: "acme/manual-review", rolloutPercent: 100, maxRepos: 1 }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 1, dispatchAllowed: true }, }, { group: "Rollout limits", - name: "Manual staged at 10 percent with bounded high cap", - inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "staged", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1000 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 3, dispatchAllowed: true }, + name: "Manual live at 10 percent with bounded high cap", + inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "live", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1000 }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 3, dispatchAllowed: true }, }, { group: "Rollout limits", name: "Manual review at 10 percent with bounded high cap", - inputs: { eventName: "workflow_dispatch", configuredMode: "staged", manualMode: "review", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1000 }, - expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", previewOnly: false, effectiveMaxRepos: 3, dispatchAllowed: true }, + inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "review", manualReviewRepo: "", rolloutPercent: 10, maxRepos: 1000 }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", effectiveMaxRepos: 3, dispatchAllowed: true }, }, { group: "Rollout limits", name: "Absolute cap of 1 wins at 100 percent", - inputs: { eventName: "workflow_dispatch", configuredMode: "staged", manualMode: "live", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1 }, - expected: { safeOutputMode: "live", safeOutputRepo: "", previewOnly: false, effectiveMaxRepos: 1, dispatchAllowed: true }, + inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "live", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 1 }, + expected: { safeOutputMode: "live", safeOutputRepo: "", effectiveMaxRepos: 1, dispatchAllowed: true }, }, { group: "Rollout limits", - name: "Absolute cap of 10 wins at 100 percent staged", - inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "staged", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, - expected: { safeOutputMode: "staged", safeOutputRepo: "", previewOnly: true, effectiveMaxRepos: 10, dispatchAllowed: true }, + name: "Absolute cap of 10 wins at 100 percent review", + inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "review", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10 }, + expected: { safeOutputMode: "review", safeOutputRepo: "acme/control-plane", effectiveMaxRepos: 10, dispatchAllowed: true }, + }, + { + group: "Kill switch", + name: "Disabled scheduled review has no capacity", + inputs: { eventName: "schedule", configuredMode: "review", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10, packageEnabled: false }, + expected: { safeOutputMode: null, safeOutputRepo: "", effectiveMaxRepos: 0, dispatchAllowed: false }, + }, + { + group: "Kill switch", + name: "Disabled scheduled live has no capacity", + inputs: { eventName: "schedule", configuredMode: "live", manualMode: "", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10, packageEnabled: false }, + expected: { safeOutputMode: null, safeOutputRepo: "", effectiveMaxRepos: 0, dispatchAllowed: false }, + }, + { + group: "Kill switch", + name: "Disabled manual review has no capacity", + inputs: { eventName: "workflow_dispatch", configuredMode: "live", manualMode: "review", manualReviewRepo: "acme/manual-review", rolloutPercent: 100, maxRepos: 10, packageEnabled: false }, + expected: { safeOutputMode: null, safeOutputRepo: "", effectiveMaxRepos: 0, dispatchAllowed: false }, + }, + { + group: "Kill switch", + name: "Disabled manual live has no capacity", + inputs: { eventName: "workflow_dispatch", configuredMode: "review", manualMode: "live", manualReviewRepo: "", rolloutPercent: 100, maxRepos: 10, packageEnabled: false }, + expected: { safeOutputMode: null, safeOutputRepo: "", effectiveMaxRepos: 0, dispatchAllowed: false }, }, ]); @@ -166,6 +155,7 @@ export function policyCases() { ["configuredMode", policyMatrix.configuredModes], ["rolloutPercent", policyMatrix.rolloutPercents], ["maxRepos", policyMatrix.maxReposValues], + ["packageEnabled", policyMatrix.packageEnabledValues], ]; const scheduled = combinations(sharedDimensions).map((values) => ({ eventName: "schedule", diff --git a/tests/unit/workflow-contract.test.mjs b/tests/unit/workflow-contract.test.mjs index c89ebe5..3a57824 100644 --- a/tests/unit/workflow-contract.test.mjs +++ b/tests/unit/workflow-contract.test.mjs @@ -9,12 +9,38 @@ import { policyCases, userFacingScenarios } from "./workflow-contract.matrix.mjs const root = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); const workflowsDirectory = join(root, ".github", "workflows"); -const modes = ["staged", "review", "live"]; +const modes = ["review", "live"]; function workflow(name, directory = workflowsDirectory) { return readFileSync(join(directory, name), "utf8"); } +function generatedJobs(source) { + const jobsStart = source.indexOf("\njobs:\n"); + assert.notEqual(jobsStart, -1, "generated workflow has no jobs section"); + const jobsSource = source.slice(jobsStart + 7); + const matches = [...jobsSource.matchAll(/^ ([A-Za-z0-9_-]+):\n/gm)]; + + return new Map(matches.map((match, index) => { + const block = jobsSource.slice(match.index, matches[index + 1]?.index ?? jobsSource.length); + const inlineNeeds = /^ needs: ([A-Za-z0-9_-]+)$/m.exec(block); + const listNeeds = /^ needs:\n((?: - [A-Za-z0-9_-]+\n)+)/m.exec(block); + const needs = inlineNeeds + ? [inlineNeeds[1]] + : [...(listNeeds?.[1].matchAll(/^ - ([A-Za-z0-9_-]+)$/gm) ?? [])].map((item) => item[1]); + + return [match[1], { block, needs }]; + })); +} + +function transitivelyNeeds(jobs, jobName, dependency, visited = new Set()) { + if (visited.has(jobName)) return false; + visited.add(jobName); + const needs = jobs.get(jobName)?.needs ?? []; + return needs.includes(dependency) + || needs.some((name) => transitivelyNeeds(jobs, name, dependency, visited)); +} + function resolvePolicy({ eventName, configuredMode, @@ -29,7 +55,20 @@ function resolvePolicy({ orchestratorCredits = 0, workerCreditsPerTarget = 0, aggregateCreditLimit = 1100, + packageEnabled = true, }) { + if (packageEnabled === false) { + return { + enabled: false, + safeOutputMode: null, + safeOutputRepo: "", + effectiveMaxRepos: 0, + dispatchAllowed: false, + }; + } + if (packageEnabled !== true) { + throw new TypeError("packageEnabled must be true or false"); + } if (!Number.isInteger(maxRepos) || maxRepos < 1 || maxRepos > 1000) { throw new RangeError("maxRepos must be an integer from 1 through 1000"); } @@ -43,9 +82,12 @@ function resolvePolicy({ } const requestedMode = eventName === "workflow_dispatch" - ? manualMode || "staged" - : configuredMode || "staged"; - const safeOutputMode = requestedMode === "preview" ? "staged" : requestedMode; + ? manualMode || "review" + : configuredMode || "review"; + if (!modes.includes(requestedMode)) { + throw new RangeError("safeOutputMode must be review or live"); + } + const safeOutputMode = requestedMode; const reviewOutputRepo = manualReviewRepo || controlRepository; const percentCap = totalRepositories === 0 ? 0 @@ -57,10 +99,9 @@ function resolvePolicy({ const effectiveMaxRepos = Math.min(maxRepos, percentCap, dispatchCap, creditCap); return { - enabled: eventName === "workflow_dispatch" || modes.includes(configuredMode), + enabled: packageEnabled, safeOutputMode, safeOutputRepo: safeOutputMode === "review" ? reviewOutputRepo : "", - previewOnly: !["review", "live"].includes(safeOutputMode), effectiveMaxRepos, dispatchAllowed: true, }; @@ -70,9 +111,9 @@ test("all scheduled configurations and manual selections route safely", () => { const cases = policyCases(); const uniqueInputs = new Set(cases.map(({ id, ...values }) => JSON.stringify(values))); - assert.equal(cases.length, 126); - assert.equal(cases.filter(({ eventName }) => eventName === "schedule").length, 18); - assert.equal(cases.filter(({ eventName }) => eventName === "workflow_dispatch").length, 108); + assert.equal(cases.length, 120); + assert.equal(cases.filter(({ eventName }) => eventName === "schedule").length, 24); + assert.equal(cases.filter(({ eventName }) => eventName === "workflow_dispatch").length, 96); assert.equal(uniqueInputs.size, cases.length, "matrix contains duplicate policy inputs"); for (const scenario of cases) { @@ -83,21 +124,23 @@ test("all scheduled configurations and manual selections route safely", () => { const expectedReviewRepo = scenario.manualReviewRepo || "acme/control-plane"; const percentageCap = scenario.rolloutPercent === 10 ? 3 : 25; - assert.equal(policy.safeOutputMode, expectedMode, scenario.id); - assert.equal(policy.previewOnly, expectedMode === "staged", scenario.id); + assert.equal(policy.enabled, scenario.packageEnabled, scenario.id); + assert.equal(policy.safeOutputMode, scenario.packageEnabled ? expectedMode : null, scenario.id); assert.equal( policy.safeOutputRepo, - expectedMode === "review" ? expectedReviewRepo : "", + scenario.packageEnabled && expectedMode === "review" ? expectedReviewRepo : "", scenario.id, ); assert.equal( policy.dispatchAllowed, - true, + scenario.packageEnabled, scenario.id, ); assert.equal( policy.effectiveMaxRepos, - scenario.maxRepos ? Math.min(scenario.maxRepos, percentageCap) : percentageCap, + scenario.packageEnabled + ? (scenario.maxRepos ? Math.min(scenario.maxRepos, percentageCap) : percentageCap) + : 0, scenario.id, ); } @@ -107,19 +150,23 @@ test("every checked user-facing scenario is backed by the exhaustive matrix", () const cases = policyCases(); const groupCounts = Object.groupBy(userFacingScenarios, ({ group }) => group); - assert.equal(userFacingScenarios.length, 24); - assert.equal(new Set(userFacingScenarios.map(({ name }) => name)).size, 24); - assert.equal(groupCounts["Scheduled modes"].length, 8); - assert.equal(groupCounts["Manual runs"].length, 4); - assert.equal(groupCounts["Review routing"].length, 5); + assert.equal(userFacingScenarios.length, 22); + assert.equal(new Set(userFacingScenarios.map(({ name }) => name)).size, 22); + assert.equal(groupCounts["Scheduled modes"].length, 4); + assert.equal(groupCounts["Manual runs"].length, 3); + assert.equal(groupCounts["Review routing"].length, 4); assert.equal(groupCounts["Rollout limits"].length, 7); + assert.equal(groupCounts["Kill switch"].length, 4); for (const scenario of userFacingScenarios) { const matrixCase = cases.find(({ id, totalRepositories, ...inputs }) => - Object.entries(scenario.inputs).every(([name, value]) => inputs[name] === value)); + Object.entries(scenario.inputs).every(([name, value]) => inputs[name] === value) + && inputs.packageEnabled === (scenario.inputs.packageEnabled ?? true)); assert.ok(matrixCase, `${scenario.name} is missing from the exhaustive matrix`); - const { enabled, ...actual } = resolvePolicy(matrixCase); + const policy = resolvePolicy(matrixCase); + assert.equal(policy.enabled, scenario.inputs.packageEnabled ?? true, scenario.name); + const { enabled, ...actual } = policy; assert.deepEqual(actual, scenario.expected, scenario.name); } }); @@ -140,27 +187,50 @@ test("percentage rollout rejects invalid settings and handles an empty organizat } assert.equal(resolvePolicy({ maxRepos: 1, rolloutPercent: 10, totalRepositories: 0 }).effectiveMaxRepos, 0); + for (const configuredMode of ["unknown", "preview", "preview_only", "staged", "Review", "LIVE", "review "]) { + assert.throws(() => resolvePolicy({ + eventName: "schedule", + configuredMode, + maxRepos: 1, + rolloutPercent: 100, + totalRepositories: 25, + }), RangeError); + } + assert.deepEqual(resolvePolicy({ + eventName: "schedule", + configuredMode: "invalid-but-disabled", + packageEnabled: false, + maxRepos: 0, + rolloutPercent: 0, + totalRepositories: 25, + }), { + enabled: false, + safeOutputMode: null, + safeOutputRepo: "", + effectiveMaxRepos: 0, + dispatchAllowed: false, + }); assert.equal(resolvePolicy({ eventName: "schedule", - configuredMode: "unknown", + configuredMode: "", maxRepos: 1, rolloutPercent: 100, totalRepositories: 25, - }).enabled, false); + }).safeOutputMode, "review"); assert.equal(resolvePolicy({ - eventName: "schedule", - configuredMode: "preview", + eventName: "workflow_dispatch", + manualMode: "", maxRepos: 1, rolloutPercent: 100, totalRepositories: 25, - }).safeOutputMode, "staged"); + }).safeOutputMode, "review"); }); test("manual requests run independently of scheduled configuration", () => { for (const manualMode of modes) { const policy = resolvePolicy({ eventName: "workflow_dispatch", - configuredMode: "disabled", + configuredMode: manualMode === "review" ? "live" : "review", manualMode, manualReviewRepo: manualMode === "review" ? "acme/manual-review" : "", maxRepos: 1, @@ -317,7 +387,7 @@ test("deterministic workflows pin third-party actions by commit SHA", () => { join(".github", "workflows", "copilot-setup-steps.yml"), join(".github", "workflows", "enterprise-canary.yml"), join(".github", "workflows", "enterprise-stress.yml"), - join(".github", "workflows", "staged-smoke.yml"), + join(".github", "workflows", "review-smoke.yml"), join("pages", "pages.yml"), ]) { const source = readFileSync(join(root, relativePath), "utf8"); @@ -330,7 +400,7 @@ test("deterministic workflows pin third-party actions by commit SHA", () => { test("package manifests exclude repository-only tests", () => { for (const relativePath of ["aw.yml", join("advisory", "aw.yml"), join("ambient-context", "aw.yml"), join("aw-failures", "aw.yml"), join("aw-maintenance", "aw.yml"), join("dependabot", "aw.yml"), join("eu-cra-compliance", "aw.yml"), join("optimization", "aw.yml")]) { const manifest = readFileSync(join(root, relativePath), "utf8"); - assert.doesNotMatch(manifest, /(?:staged-smoke|enterprise-canary|enterprise-stress|tests\/e2e|\.github\/aw\/e2e)/, relativePath); + assert.doesNotMatch(manifest, /(?:review-smoke|enterprise-canary|enterprise-stress|tests\/e2e|\.github\/aw\/e2e)/, relativePath); } }); @@ -392,19 +462,21 @@ test("operational-value graders expose deterministic run-scoped contracts", () = assert.match(optimizerEvaluator, /target-workflow:\$\{target_repo\}:\$\{workflow\}:\$\{optimizer_run_id\}/); }); -test("staged smoke is manual, bounded, and cannot request writes", () => { - const smoke = workflow("staged-smoke.yml"); +test("review smoke is manual, protected, bounded, and cannot change the target", () => { + const smoke = workflow("review-smoke.yml"); const harness = readFileSync(join(root, "tests", "e2e", "run-canary.sh"), "utf8"); assert.match(smoke, /workflow_dispatch:/); assert.doesNotMatch(smoke, /^\s+schedule:/m); assert.match(smoke, /actions: write/); assert.match(smoke, /timeout-minutes: 75/); - assert.match(smoke, /SAFE_OUTPUT_MODE: staged/); + assert.match(smoke, /environment: central-agentic-ops-review/); + assert.match(smoke, /SAFE_OUTPUT_MODE: review/); + assert.match(smoke, /SAFE_OUTPUT_REPO: \$\{\{ inputs\.safe_output_repo \}\}/); assert.match(smoke, /bash tests\/e2e\/run-canary\.sh/); - assert.match(smoke, /group: staged-smoke-/); + assert.match(smoke, /group: review-smoke-/); assert.match(harness, /max_repos=1/); assert.match(harness, /snapshot_repository/); - assert.match(harness, /staged canary mutated target repository state/); + assert.match(harness, /review canary mutated target repository state/); assert.match(harness, /No correlated worker run was found/); }); @@ -425,7 +497,7 @@ test("enterprise canaries are manual, protected, confirmed, and bounded", () => assert.match(canary, /bash tests\/e2e\/run-canary\.sh/); assert.match(stress, /bash tests\/e2e\/run-stress\.sh/); - assert.match(canary, /options: \[staged, review, live\]/); + assert.match(canary, /options: \[review, live\]/); assert.match(canary, /environment: central-agentic-ops-\$\{\{ inputs\.safe_output_mode \}\}/); assert.match(canary, /require_output:/); assert.match(canaryHarness, /confirmation must be REVIEW/); @@ -436,10 +508,10 @@ test("enterprise canaries are manual, protected, confirmed, and bounded", () => assert.match(stress, /environment: central-agentic-ops-\$\{\{ 'stress' \}\}/); assert.match(stress, /options: \[2, 3, 5\]/); assert.match(stressHarness, /target_repo must use OWNER\/REPO form/); - assert.match(stressHarness, /STRESS \$TARGET_REPO \$RUNS/); + assert.match(stressHarness, /STRESS \$TARGET_REPO REVIEW \$SAFE_OUTPUT_REPO \$RUNS/); assert.match(stressHarness, /RUNS - 1/); - assert.match(stressHarness, /safe_output_mode=staged/); - assert.match(stressHarness, /staged stress run mutated target repository state/); + assert.match(stressHarness, /safe_output_mode=review/); + assert.match(stressHarness, /review stress run mutated target repository state/); }); test("ownership, provenance, and workflow identity fail closed", () => { @@ -477,8 +549,8 @@ test("public read-only operation uses the built-in token without widening access assert.match(precompute, /GH_TOKEN:.*GH_AW_GITHUB_TOKEN.*secrets\.GITHUB_TOKEN/); assert.match(precompute, /\{id, full_name, archived, disabled, private, pushed_at, default_branch\}/); - assert.match(authentication, /App or PAT is not required for a bounded `staged` scan when every target repository is public/); - assert.match(authentication, /use `review` only when safe outputs remain in the current control repository/); + assert.match(authentication, /App or PAT is not required for a bounded `review` run when every target repository is public/); + assert.match(authentication, /use `review` mode and keep safe outputs in the current control repository/); assert.match(authentication, /configure an App or PAT for private or internal targets, an alternate review repository, or any `live` cross-repository write/); assert.match(authentication, /report incomplete and produce no speculative result/); assert.match(configuration, /no App or PAT secret is required/); @@ -506,7 +578,7 @@ test("live workers require target-owned package authority before agent execution assert.match(precompute, /contents\/\.github\/central-agentic-ops\.yml/); assert.match(precompute, /YAML\.safe_load/); assert.match(precompute, /target assigns live authority for \$BUNDLE to a different control repository/); - assert.match(precompute, /validate_worker_dispatch\n\s+validate_live_authority\n\s+write_worker_precompute/); + assert.match(precompute, /validate_worker_dispatch\n\s+validate_output_destination\n\s+validate_live_authority\n\s+write_worker_precompute/); for (const [name, bundle] of [ ["advisory.md", "advisory"], @@ -547,8 +619,10 @@ test("orchestrators expose scheduled variables and independent manual inputs", ( ]) { const source = workflow(name); + assert.match(source, new RegExp(`^if: \\(vars\\.CENTRAL_AGENTIC_OPS_${packageName}_ENABLED \\|\\| 'true'\\) == 'true'$`, "m")); assert.match(source, /rollout_percent:\n\s+default: 100\n\s+type: number/); - assert.match(source, new RegExp(`CENTRAL_AGENTIC_OPS_${packageName}_MODE \\|\\| 'staged'`)); + assert.match(source, new RegExp(`CENTRAL_AGENTIC_OPS_${packageName}_ENABLED \\|\\| 'true'`)); + assert.match(source, new RegExp(`CENTRAL_AGENTIC_OPS_${packageName}_MODE \\|\\| 'review'`)); assert.match(source, new RegExp(`CENTRAL_AGENTIC_OPS_${packageName}_MAX_REPOS \\|\\| '1'`)); assert.doesNotMatch(source, new RegExp(`CENTRAL_AGENTIC_OPS_${packageName}_REVIEW_REPO`)); assert.match(source, new RegExp(`CENTRAL_AGENTIC_OPS_${packageName}_ROLLOUT_PERCENT \\|\\| '100'`)); @@ -562,22 +636,34 @@ test("orchestrators expose scheduled variables and independent manual inputs", ( } }); -test("review destinations must be accessible and private", () => { +test("review destinations must be isolated, accessible, and private", () => { const precompute = workflow("shared/control-precompute.md"); - assert.match(precompute, /validate_review_destination/); + assert.match(precompute, /validate_output_destination/); + assert.match(precompute, /review safe_output_repo must differ from target_repo/); + assert.match(precompute, /live worker safe_output_repo must equal target_repo/); assert.match(precompute, /gh api "repos\/\$SAFE_OUTPUT_REPO" --jq '\.private'/); assert.match(precompute, /review safe_output_repo must be accessible/); assert.match(precompute, /review safe_output_repo must be private/); }); +test("safe-output modes are review and live with a separate package kill switch", () => { + const control = workflow("shared/control.md"); + const precompute = workflow("shared/control-precompute.md"); + + assert.match(control, /rollout_mode:[\s\S]*?options: \[review, live\][\s\S]*?default: "review"/); + assert.match(control, /package_enabled:[\s\S]*?default: "true"/); + assert.doesNotMatch(`${control}\n${precompute}`, /preview_only|\bstaged\b/); +}); + test("shared control keeps manual and scheduled routing event-scoped", () => { const control = workflow("shared/control.md"); const precompute = workflow("shared/control-precompute.md"); for (const name of ["advisory.md", "ambient-context.md", "aw-failures.md", "aw-maintenance.md", "dependabot.md", "eu-cra-compliance.md", "optimization.md"]) { const orchestrator = workflow(name); - assert.match(orchestrator, /GH_AW_SAFE_OUTPUT_MODE:.*== 'preview' && 'staged'/); + assert.match(orchestrator, /GH_AW_SAFE_OUTPUT_MODE:.*inputs\.safe_output_mode.*\|\| 'review'/); + assert.match(orchestrator, /CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED:.*_ENABLED \|\| 'true'/); assert.match(orchestrator, /REVIEW_OUTPUT_REPO:.*inputs\.safe_output_repo \|\| github\.repository/); assert.match(orchestrator, /SAFE_OUTPUT_REPO:.*== 'review'/); } @@ -585,7 +671,7 @@ test("shared control keeps manual and scheduled routing event-scoped", () => { assert.match(control, /safe_output_repo: \$\{\{ env\.SAFE_OUTPUT_REPO \}\}/); assert.doesNotMatch(control, /review_repo/); assert.match(control, /rollout_percent: "\$\{\{ github\.aw\.import-inputs\.rollout_percent \}\}"/); - assert.match(control, /GH_AW_SAFE_OUTPUT_MODE == 'live'.*GH_AW_SAFE_OUTPUT_MODE == 'review'.*'false' \|\| 'true'/); + assert.match(control, /enabled: \$\{\{ env\.CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED \|\| github\.aw\.import-inputs\.package_enabled \}\}/); assert.match(control, /select no more than `effective_max_repos` repositories/); assert.match(precompute, /rollout_percent must be an integer from 1 through 100/); @@ -596,30 +682,33 @@ test("shared control keeps manual and scheduled routing event-scoped", () => { test("every worker uses the standard dispatch envelope and safe mode vocabulary", () => { const workerNames = [ - "advisory-uk-ai-operational-resilience.md", - "ambient-context-agents-md-curator.md", - "ambient-context-skills-curator.md", - "aw-failures-investigator.md", - "aw-maintenance-upgrade.md", - "dependabot-release-train-updater.md", - "eu-cra-compliance-article-14-reporting-readiness.md", - "eu-cra-compliance-conformity-release-evidence.md", - "eu-cra-compliance-scope-classifier.md", - "eu-cra-compliance-security-requirements-auditor.md", - "eu-cra-compliance-supply-chain-sbom-auditor.md", - "eu-cra-compliance-vulnerability-handling-auditor.md", - "optimization-ai-credit-auditor.md", - "optimization-ai-credit-optimizer.md", + ["advisory-uk-ai-operational-resilience.md", "ADVISORY", "ADVISORY_UK_AI_OPERATIONAL_RESILIENCE"], + ["ambient-context-agents-md-curator.md", "AMBIENT_CONTEXT", "AMBIENT_CONTEXT_AGENTS_MD"], + ["ambient-context-skills-curator.md", "AMBIENT_CONTEXT", "AMBIENT_CONTEXT_SKILLS"], + ["aw-failures-investigator.md", "AW_FAILURES", "AW_FAILURES_INVESTIGATOR"], + ["aw-maintenance-upgrade.md", "AW_MAINTENANCE", "AW_MAINTENANCE_UPGRADE"], + ["dependabot-release-train-updater.md", "DEPENDABOT", "DEPENDABOT_UPDATER"], + ["eu-cra-compliance-article-14-reporting-readiness.md", "EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS"], + ["eu-cra-compliance-conformity-release-evidence.md", "EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE"], + ["eu-cra-compliance-scope-classifier.md", "EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER"], + ["eu-cra-compliance-security-requirements-auditor.md", "EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR"], + ["eu-cra-compliance-supply-chain-sbom-auditor.md", "EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR"], + ["eu-cra-compliance-vulnerability-handling-auditor.md", "EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR"], + ["optimization-ai-credit-auditor.md", "OPTIMIZATION", "OPTIMIZATION_AUDITOR"], + ["optimization-ai-credit-optimizer.md", "OPTIMIZATION", "OPTIMIZATION_OPTIMIZER"], ]; - for (const name of workerNames) { + for (const [name, packageName, workerName] of workerNames) { const source = workflow(name); + assert.match( + source, + new RegExp(`^if: >-\\n \\(vars\\.CENTRAL_AGENTIC_OPS_${packageName}_ENABLED \\|\\| 'true'\\) == 'true' &&\\n \\(vars\\.CENTRAL_AGENTIC_OPS_${workerName}_ENABLED \\|\\| 'true'\\) == 'true'$`, "m"), + ); for (const input of [ "target_repo", "safe_output_repo", "safe_output_mode", - "preview_only", "correlation_id", "central_repo", "control_plane_run_url", @@ -627,14 +716,20 @@ test("every worker uses the standard dispatch envelope and safe mode vocabulary" assert.match(source, new RegExp(`^ ${input}:`, "m"), `${name} is missing ${input}`); } - assert.match(source, /safe-outputs:\n\s+staged: \$\{\{ inputs\.preview_only == 'true' \}\}/); + assert.doesNotMatch(source, /^ preview_only:/m); + assert.doesNotMatch(source, /^\s+staged:/m); assert.doesNotMatch(source, /safe_output_mode == 'private'/); + assert.match(source, /CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED:.*_ENABLED \|\| 'true'/); assert.match(source, /CENTRAL_AGENTIC_OPS_WORKER_ENABLED:.*\|\| 'true'/); - assert.match(source, /CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE:.*\|\| 'staged'/); - assert.match(source, /GH_AW_SAFE_OUTPUT_MODE: \$\{\{ inputs\.safe_output_mode \|\| 'staged' \}\}/); + assert.match(source, /CENTRAL_AGENTIC_OPS_WORKER_MAX_MODE:.*\|\| 'review'/); + assert.match(source, /GH_AW_SAFE_OUTPUT_MODE: \$\{\{ inputs\.safe_output_mode \|\| 'review' \}\}/); + assert.match(source, /SAFE_OUTPUT_REPO:.*safe_output_mode.*'review'.*safe_output_repo.*github\.repository.*target_repo/); for (const line of source.match(/^\s+target-repo:.*$/gm) || []) { - assert.match(line, /github\.event\.inputs\.safe_output_repo/); + assert.match(line, /safe_output_mode.*'review'.*safe_output_repo.*github\.repository.*target_repo/); + } + for (const line of source.match(/^\s+- repository:.*inputs\.safe_output_repo.*$/gm) || []) { + assert.match(line, /safe_output_mode.*'review'.*safe_output_repo.*github\.repository.*target_repo/); } } }); @@ -689,8 +784,8 @@ test("Advisory preserves UK AI guidance and human-review boundaries", () => { assert.match(maintainer, /^name: "Advisory \/ Package Maintainer"$/m); assert.match(maintainer, /schedule: weekly/); - assert.match(maintainer, /safe_output_mode:\n\s+default: staged/); - assert.match(maintainer, /staged: \$\{\{ github\.event_name == 'workflow_dispatch' && inputs\.safe_output_mode != 'live' \}\}/); + assert.match(maintainer, /safe_output_mode:\n\s+default: review/); + assert.doesNotMatch(maintainer, /^\s+staged:/m); assert.match(maintainer, /original specification and current authoritative GOV\.UK guidance/); assert.match(maintainer, /https:\/\/www\.gov\.uk\/guidance\/ai-open-code-and-vulnerability-risk-in-the-public-sector/); assert.match(maintainer, /update only the applicable ledger path/i); @@ -760,8 +855,8 @@ test("EU CRA Advisor workflows preserve advisory and human-review boundaries", ( } assert.match(maintainer, /schedule: daily/); - assert.match(maintainer, /safe_output_mode:\n\s+default: staged/); - assert.match(maintainer, /staged: \$\{\{ github\.event_name == 'workflow_dispatch' && inputs\.safe_output_mode != 'live' \}\}/); + assert.match(maintainer, /safe_output_mode:\n\s+default: review/); + assert.doesNotMatch(maintainer, /^\s+staged:/m); assert.match(maintainer, /Systematically account for the complete Act: Articles 1–71, Annexes I–VIII/); assert.match(maintainer, /update only the applicable ledger path/i); assert.match(maintainer, /allowed-files:\n\s+- "eu-cra-compliance\/implementation-status\.md"\n\s+- "\.github\/aw\/eu-cra-compliance\/implementation-status\.md"/); @@ -820,10 +915,12 @@ test("workers reject disabled, malformed, or over-ceiling dispatches before exec assert.match(control, new RegExp(`${input}:`)); assert.match(precompute, new RegExp(`${input}:`)); } - assert.match(precompute, /validate_worker_dispatch\n\s+validate_live_authority\n\s+write_worker_precompute/); + assert.match(precompute, /validate_worker_dispatch\n\s+validate_output_destination\n\s+validate_live_authority\n\s+write_worker_precompute/); assert.match(precompute, /worker is disabled by its control-plane policy/); assert.match(precompute, /safe_output_mode exceeds the worker_max_mode ceiling/); - assert.match(precompute, /preview_only is inconsistent with safe_output_mode/); + assert.match(precompute, /must be review or live/); + assert.match(precompute, /enabled must be true or false/); + assert.match(precompute, /package disabled by its control-plane kill switch/); assert.match(precompute, /central_repo must identify the current control repository/); assert.match(precompute, /control_plane_run_url must match correlation_id and central_repo/); }); @@ -938,26 +1035,83 @@ test("clean-room compilation emits the expected GitHub Actions settings", { time assert.match(generated, /inventory_version/); assert.match(generated, /batch_id/); assert.match(generated, /outside CENTRAL_AGENTIC_OPS_ALLOWED_OWNERS/); + assert.match(generated, /review safe_output_repo must differ from target_repo/); + assert.match(generated, /review safe_output_repo must be accessible/); + assert.match(generated, /review safe_output_repo must be private/); + assert.match(generated, /live worker safe_output_repo must equal target_repo/); + assert.match(generated, /target assigns live authority for .+ to a different control repository/); + assert.doesNotMatch(generated, /PREVIEW_ONLY|preview_only/); + assert.doesNotMatch(generated, /== 'preview'/); assert.doesNotMatch(generated, /safe_output_mode == 'private'/); } - for (const name of ["advisory.lock.yml", "ambient-context.lock.yml", "aw-failures.lock.yml", "aw-maintenance.lock.yml", "dependabot.lock.yml", "eu-cra-compliance.lock.yml", "optimization.lock.yml"]) { + const orchestratorGates = new Map([ + ["advisory.lock.yml", "ADVISORY"], + ["ambient-context.lock.yml", "AMBIENT_CONTEXT"], + ["aw-failures.lock.yml", "AW_FAILURES"], + ["aw-maintenance.lock.yml", "AW_MAINTENANCE"], + ["dependabot.lock.yml", "DEPENDABOT"], + ["eu-cra-compliance.lock.yml", "EU_CRA_COMPLIANCE"], + ["optimization.lock.yml", "OPTIMIZATION"], + ]); + for (const [name, packageName] of orchestratorGates) { const generated = workflow(name, generatedDirectory); - assert.match(generated, /GH_AW_SAFE_OUTPUT_MODE:.*== 'preview' && 'staged'/); + const jobs = generatedJobs(generated); + assert.match( + jobs.get("activation").block, + new RegExp(`^ if: \\(vars\\.CENTRAL_AGENTIC_OPS_${packageName}_ENABLED \\|\\| 'true'\\) == 'true'$`, "m"), + ); + for (const jobName of jobs.keys()) { + if (jobName !== "activation") { + assert.ok(transitivelyNeeds(jobs, jobName, "activation"), `${name} job ${jobName} bypasses activation`); + } + } + assert.match(generated, /GH_AW_SAFE_OUTPUT_MODE:.*inputs\.safe_output_mode.*\|\| 'review'/); + assert.match(generated, /CENTRAL_AGENTIC_OPS_PACKAGE_ENABLED:.*_ENABLED \|\| 'true'/); assert.match(generated, /ROLLOUT_PERCENT: \$\{\{ inputs\.rollout_percent \|\| vars\.CENTRAL_AGENTIC_OPS_.+_ROLLOUT_PERCENT \|\| '100' \}\}/); assert.match(generated, /rollout_percent:\n\s+default: 100\n\s+type: number/); assert.match(generated, /timeout-minutes: 15/); assert.match(generated, /cancel-in-progress: true/); } - for (const name of packageLockNames.filter((name) => !["advisory.lock.yml", "ambient-context.lock.yml", "aw-failures.lock.yml", "aw-maintenance.lock.yml", "dependabot.lock.yml", "eu-cra-compliance.lock.yml", "optimization.lock.yml"].includes(name))) { + const workerGates = new Map([ + ["advisory-uk-ai-operational-resilience.lock.yml", ["ADVISORY", "ADVISORY_UK_AI_OPERATIONAL_RESILIENCE"]], + ["ambient-context-agents-md-curator.lock.yml", ["AMBIENT_CONTEXT", "AMBIENT_CONTEXT_AGENTS_MD"]], + ["ambient-context-skills-curator.lock.yml", ["AMBIENT_CONTEXT", "AMBIENT_CONTEXT_SKILLS"]], + ["aw-failures-investigator.lock.yml", ["AW_FAILURES", "AW_FAILURES_INVESTIGATOR"]], + ["aw-maintenance-upgrade.lock.yml", ["AW_MAINTENANCE", "AW_MAINTENANCE_UPGRADE"]], + ["dependabot-release-train-updater.lock.yml", ["DEPENDABOT", "DEPENDABOT_UPDATER"]], + ["eu-cra-compliance-article-14-reporting-readiness.lock.yml", ["EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_ARTICLE_14_REPORTING_READINESS"]], + ["eu-cra-compliance-conformity-release-evidence.lock.yml", ["EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_CONFORMITY_RELEASE_EVIDENCE"]], + ["eu-cra-compliance-scope-classifier.lock.yml", ["EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_SCOPE_CLASSIFIER"]], + ["eu-cra-compliance-security-requirements-auditor.lock.yml", ["EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_SECURITY_REQUIREMENTS_AUDITOR"]], + ["eu-cra-compliance-supply-chain-sbom-auditor.lock.yml", ["EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_SUPPLY_CHAIN_SBOM_AUDITOR"]], + ["eu-cra-compliance-vulnerability-handling-auditor.lock.yml", ["EU_CRA_COMPLIANCE", "EU_CRA_COMPLIANCE_VULNERABILITY_HANDLING_AUDITOR"]], + ["optimization-ai-credit-auditor.lock.yml", ["OPTIMIZATION", "OPTIMIZATION_AUDITOR"]], + ["optimization-ai-credit-optimizer.lock.yml", ["OPTIMIZATION", "OPTIMIZATION_OPTIMIZER"]], + ]); + for (const [name, [packageName, workerName]] of workerGates) { const generated = workflow(name, generatedDirectory); - assert.match(generated, /GH_AW_SAFE_OUTPUT_MODE: \$\{\{ inputs\.safe_output_mode \|\| 'staged' \}\}/); + const jobs = generatedJobs(generated); + const activation = jobs.get("activation").block; + const normalizedActivation = activation.replace(/\s+/g, " "); + assert.match(normalizedActivation, new RegExp(`vars\\.CENTRAL_AGENTIC_OPS_${packageName}_ENABLED \\|\\| 'true'`)); + assert.match(normalizedActivation, new RegExp(`vars\\.CENTRAL_AGENTIC_OPS_${workerName}_ENABLED \\|\\| 'true'`)); + for (const jobName of jobs.keys()) { + if (jobName !== "activation") { + assert.ok(transitivelyNeeds(jobs, jobName, "activation"), `${name} job ${jobName} bypasses activation`); + } + } + assert.match(generated, /GH_AW_SAFE_OUTPUT_MODE: \$\{\{ inputs\.safe_output_mode \|\| 'review' \}\}/); + assert.match(generated, /SAFE_OUTPUT_REPO:.*safe_output_mode.*'review'.*safe_output_repo.*github\.repository.*inputs\.target_repo/); assert.match(generated, /ROLLOUT_PERCENT: "100"/); assert.match(generated, /GH_AW_SAFE_OUTPUTS_CONFIG:/); - assert.match(generated, /PREVIEW_ONLY: \$\{\{ \(env\.GH_AW_SAFE_OUTPUT_MODE == 'live' \|\| env\.GH_AW_SAFE_OUTPUT_MODE == 'review'\) && 'false' \|\| 'true' \}\}/); } + const generatedReviewBundle = workflow("dependabot-release-train-updater.lock.yml", generatedDirectory); + assert.match(generatedReviewBundle, /GH_AW_SAFE_OUTPUTS_STAGED/); + assert.doesNotMatch(generatedReviewBundle, /GH_AW_SAFE_OUTPUTS_STAGED:.*preview_only/); + const advisoryMaintainer = workflow("advisory-package-maintainer.lock.yml", generatedDirectory); assert.match(advisoryMaintainer, /schedule:/); assert.match(advisoryMaintainer, /advisory\/implementation-status\.md/);