Files
acdl/.gitea/workflows/pipeline.yml
T
grimacing 72b359c9a9 ship: phase-04 pipeline-and-approval-gates (v1.0.4)
Squash merge of phase/04-pipeline-and-approval-gates; pipeline.yml + issue-to-contract.yml + finalize_evidence.py; verify_phase04.sh green; 1 P0 fixed (shell injection).
2026-07-21 13:42:25 +00:00

153 lines
6.0 KiB
YAML

# ACDL pipeline workflow (Phase 04 implementation).
#
# 3-dispatch approval-gate topology (D-027 / D-028; ARCHITECTURE.md
# "Phase 04 pipeline topology"):
#
# Dispatch 1 (initial): approve_qa=false, approve_prod=false
# -> runs the `dev` job (policy check, confidence
# gate, mock_executor, evidence + finalize).
# Dispatch 2 (QA approve): approve_qa=true, approve_prod=false
# -> runs the `qa-gate` job (records QA approval
# in the audit chain via evidence_writer +
# finalize_evidence).
# Dispatch 3 (Prod approve): approve_prod=true
# -> runs the `prod-gate` job, then the `finalize`
# job (needs: prod-gate) which writes the final
# evidence event and commits audit.json to
# acdl-evidence.
#
# Gitea Actions limitations driving this design:
# - No `repository_dispatch` trigger (D-014).
# - No environments API / `environment:` blocks are ignored (D-013).
# - Re-dispatch starts a NEW run; artifacts do NOT survive between runs,
# so state is persisted to acdl-evidence via the file-contents API
# (D-028 / finalize_evidence.py) instead of via artifacts.
#
# Branch-pin rule (ARCHITECTURE.md "Branch pinning rule"):
# This workflow lives on `acdl`'s default branch `milestone/v1.0-initial`.
# Cross-repo `uses:` references (e.g. the issue-trigger's checkout of
# l3b_agent_stub.py) MUST pin to `@milestone/v1.0-initial`, NOT `@main`
# (the `acdl` repo has no `main` branch). This workflow is invoked via
# the workflow_dispatch API (D-014), NOT via `workflow_call`, so the
# `uses:` rule applies to the issue-trigger's checkout of the acdl repo,
# not to this file itself.
name: acdl-pipeline
"on":
workflow_dispatch:
inputs:
contract-ref:
description: "Ref on acdl-contracts that carries the contract"
required: false
type: string
default: main
approve_qa:
description: "Human approval to advance past QA"
required: false
type: boolean
default: false
approve_prod:
description: "Human approval to advance past Prod"
required: false
type: boolean
default: false
jobs:
dev:
name: "Dev (autonomous)"
if: inputs.approve_qa != true && inputs.approve_prod != true
runs-on: ubuntu-latest
steps:
- name: "Checkout acdl (this repo, pinned to milestone/v1.0-initial)"
uses: actions/checkout@v4
with:
ref: milestone/v1.0-initial
- name: "Checkout acdl-contracts at contract-ref"
uses: actions/checkout@v4
with:
repository: continuous-intelligence/acdl-contracts
ref: ${{ inputs.contract-ref }}
token: ${{ secrets.GITEA_TOKEN }}
path: acdl-contracts
- name: "Policy check"
run: |
python3 scripts/policy_checker.py acdl-contracts/contract.yaml
- name: "Confidence signal"
id: confidence
run: |
set +e
SCORE_JSON=$(python3 scripts/confidence_signal.py acdl-contracts/contract.yaml)
echo "$SCORE_JSON"
echo "score_json=$SCORE_JSON" >> "$GITHUB_OUTPUT"
- name: "Apply or reject based on confidence (gate < 0.50)"
run: |
set +e
SCORE=$(python3 -c "import json,sys; print(json.load(sys.stdin)['score'])" <<< '${{ steps.confidence.outputs.score_json }}')
python3 -c "import sys; sys.exit(0 if float('${SCORE}') >= 0.50 else 1)"
THRESHOLD_RC=$?
if [ "$THRESHOLD_RC" -ne 0 ]; then
python3 scripts/evidence_writer.py --stage dev --event "dev rejected: confidence < 0.50" --audit audit.json
python3 scripts/finalize_evidence.py --audit audit.json
exit 1
fi
STACK=$(python3 -c 'import yaml; print(yaml.safe_load(open("acdl-contracts/contract.yaml"))["stack"])')
bash scripts/mock_executor.sh acdl-contracts/contract.yaml
python3 scripts/evidence_writer.py --stage dev --event "dev applied: ${STACK}" --audit audit.json
python3 scripts/finalize_evidence.py --audit audit.json
- name: "Upload dev state artifacts (best-effort)"
uses: actions/upload-artifact@v3
with:
name: dev-state
path: |
audit.json
state.json
qa-gate:
name: "QA (manual approval)"
if: inputs.approve_qa == true && inputs.approve_prod != true
runs-on: ubuntu-latest
steps:
- name: "Checkout acdl (this repo, pinned to milestone/v1.0-initial)"
uses: actions/checkout@v4
with:
ref: milestone/v1.0-initial
- name: "Record QA approval in evidence"
run: |
python3 scripts/evidence_writer.py --stage qa --event "qa approved" --audit audit.json
python3 scripts/finalize_evidence.py --audit audit.json
prod-gate:
name: "Prod (manual approval)"
if: inputs.approve_prod == true
runs-on: ubuntu-latest
steps:
- name: "Checkout acdl (this repo, pinned to milestone/v1.0-initial)"
uses: actions/checkout@v4
with:
ref: milestone/v1.0-initial
- name: "Record Prod approval in evidence"
run: |
python3 scripts/evidence_writer.py --stage prod --event "prod approved" --audit audit.json
python3 scripts/finalize_evidence.py --audit audit.json
finalize:
name: "Finalize (publish evidence)"
needs: [prod-gate]
runs-on: ubuntu-latest
steps:
- name: "Checkout acdl (this repo, pinned to milestone/v1.0-initial)"
uses: actions/checkout@v4
with:
ref: milestone/v1.0-initial
- name: "Write finalize event + commit audit.json to acdl-evidence"
run: |
python3 scripts/evidence_writer.py --stage finalize --event "pipeline complete: audit.json committed to acdl-evidence" --audit audit.json
python3 scripts/finalize_evidence.py --audit audit.json