ship: phase-04 pipeline-and-approval-gates #4
Reference in New Issue
Block a user
Delete Branch "phase/04-pipeline-and-approval-gates"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 04 ship
Replaces the workflow skeletons with real implementations.
Pipeline (
.gitea/workflows/pipeline.yml): 3-dispatch approval-gate topology (D-027/D-028):workflow_dispatchinputs:contract-ref,approve_qa,approve_proddev(initial dispatch, runs policy_checker + confidence_signal + mock_executor + writes evidence),qa-gate(approve_qa=true),prod-gate(approve_prod=true),finalize(chained to prod-gate)acdl-evidenceviafinalize_evidence.pybetween dispatches (D-028)Issue trigger (
contracts-repo/.gitea/workflows/issue-to-contract.yml):on: issues [opened]→ checkoutacdl→ runl3b_agent_stub.py→ commitcontract.yamltocontract/<n>branch onacdl-contracts→ comment + close Issue → dispatch pipeline via API (D-014/D-030)Helper (
scripts/finalize_evidence.py): GET→sha + PUT/POST upload ofaudit.jsontoacdl-evidence, with cleanURLErrorhandling (P0-001 fixed during verify).Requirements covered: REQ-10, REQ-12
Verification: 4 layers passed, 1 P0 fixed inline (P0-002: shell injection in issue body interpolation → use env var).
Version target: v1.0.4 (feature milestone, phase 4 patch)
---ci--- phase: 4 milestone: v1.0 status: clarify decisions: - id: D-027 decision: Two workflows: pipeline.yml (workflow_call + workflow_dispatch) and issue-to-contract.yml (issues.opened); gates via workflow_dispatch approve_qa/approve_prod inputs rationale: Gitea has no environment reviewers, no repository_dispatch, no native approval UI; D-013 fallback confidence: 0.85 alternatives: [single long-running workflow that polls, external approval bot] - id: D-028 decision: All 4 stages in one workflow run; state passed via upload/download-artifact; gates re-dispatched with approve inputs rationale: Gitea supports upload/download-artifact; alternative is committing state between jobs (heavier) confidence: 0.80 alternatives: [commit state.json + audit.json to a state branch between jobs] - id: D-029 decision: Finalize commits audit.json to acdl-evidence main via the Gitea file-contents API (same mechanism as Phase 01 index.html push) rationale: D-012 raw-URL requires the file to be on main; the API is the only way to put it there from a step confidence: 0.90 alternatives: [clone acdl-evidence in the step, copy file, commit, push] - id: D-030 decision: Issue-to-contract workflow checks out l3b_agent_stub from acdl, parses Issue, commits contract.yaml to contract/<issue-number> branch on acdl-contracts, then dispatches pipeline via workflow_dispatch API rationale: Gitea Actions cannot trigger cross-repo without an explicit API call; the branch carries the contract ref confidence: 0.85 alternatives: [push contract to a sentinel branch on the acdl repo itself] ---/ci--- Phase 04 has real pipeline-topology ambiguities (Gitea's gaps vs the spec's approval-gate intent). Four decisions logged: D-027 two-workflow topology + dispatch-input gates, D-028 artifact-based state passing, D-029 finalize commits via file-contents API, D-030 issue workflow checks out l3b_agent_stub from acdl and dispatches the pipeline.---ci--- phase: 4 milestone: v1.0 status: research research: gitea_actions_capabilities: upload_artifact_v3: supported (v4 NOT supported by act_runner) artifacts_cross_dispatch: NOT preserved (re-dispatch starts a new run) workflow_dispatch_api: POST /actions/workflows/{filename}.yml/dispatches workflow_call: supported; uses: <owner>/<repo>/.gitea/workflows/<file>@<ref> checkout_cross_repo: actions/checkout@v4 with repository + ref + token secrets: ${{ secrets.GITEA_TOKEN }} must be a manually-created PAT (auto-token is current-repo only) approval_gate: workflow_dispatch input (approve_qa, approve_prod); no native pause-and-wait pipeline_design: topology: single pipeline.yml with workflow_dispatch inputs (contract-ref, approve_qa, approve_prod) stages: 3 separate dispatches (initial dev, qa-approve, prod-approve+finalize) state_persistence: each stage writes evidence to acdl-evidence via file-contents API (PUT audit.json) job_if_conditions: dev runs when !approve_qa && !approve_prod; qa-gate when approve_qa && !approve_prod; prod-gate+finalize when approve_prod personas: no change; backend-engineer owns the workflow YAML + finalize + issue trigger; lead-developer owns verify_phase04.sh ---/ci--- ARCHITECTURE.md gains a 'Phase 04 pipeline topology (research)' section: the artifact-vs-re-dispatch limitation forces 3-dispatch topology with evidence persisted to acdl-evidence (PUT audit.json) between dispatches. PERSONAS.md is unchanged for Phase 04 (backend-engineer + lead-developer active; infra-stub-engineer idle; frontend-engineer still off).---ci--- phase: 4 milestone: v1.0 status: plan plan: waves: 2 wave_1_backend: [T-4.1 finalize_evidence.py, T-4.2 pipeline.yml real, T-4.3 issue-to-contract.yml real] wave_2_coordination: [T-4.4 verify_phase04.sh, T-4.5 traceability] requirements_covered: [REQ-10, REQ-12] must_haves: 7 verification: typecheck: bash -n + py_compile + yaml load both workflows test: scripts/verify_phase04.sh (structure + syntax + finalize_evidence dry-run; full end-to-end run is Phase 05) risk: full pipeline cannot be exercised without an act_runner; verify_phase04 validates structure + syntax + dry-run only ---/ci--- PLAN.md replaced with Phase 04 plan. Wave 1 (backend) builds the pipeline real implementation + finalize_evidence.py helper + issue-to-contract.yml real implementation. Wave 2 (lead-developer) wires verify_phase04.sh + traceability. Full end-to-end run is deferred to Phase 05 (act_runner not registered in this environment).---ci--- phase: 4 milestone: v1.0 status: execute persona: lead-developer task: T-4.4 requirements: covered: [REQ-10, REQ-12] review: p0: 1 p1: 0 p0_items: - id: P0-001 location: scripts/finalize_evidence.py _request() issue: urllib.error.URLError (connection refused, DNS, timeout) was uncaught and stack-traced instead of returning clean JSON severity: P0 (the pipeline would crash on a Gitea outage instead of writing a clean failure event) fix: catch URLError in _request(); return (0, 'URLError: <reason>') so callers report cleanly ---/ci--- Wave 2, task T-4.4. scripts/verify_phase04.sh checks: (1) typecheck; (2) pipeline.yml structure (3 inputs + 4 jobs + correct if: conditions + finalize.needs=prod-gate); (3) pipeline.yml references all 5 core scripts + branch-pin doc; (4) issue-to-contract.yml structure (issues[opened] + parse-and-trigger); (5) issue-to-contract.yml references (l3b_agent_stub + dispatch endpoint + contract-ref + issue number + GITEA_TOKEN + new_branch); (6) finalize_evidence.py --help + missing file + missing token (all exit 1 clean, no stack trace); (7) finalize_evidence.py dead-host dry-run (exit 1, no stack trace). All 12 checks PASS. The dead-host check caught a P0: finalize_evidence.py did not catch urllib.error.URLError, so a connection-refused would stack-trace in the pipeline. Fixed by catching URLError in _request() and returning (0, 'URLError: <reason>') so callers report a clean JSON failure.---ci--- phase: 4 milestone: v1.0 status: verify requirements: covered: [REQ-10, REQ-12] partial: [] lessons: - 'on: <trigger>' YAML key must be quoted ('on':) because PyYAML interprets bare 'on:' as the boolean True (YAML 1.1 spec quirk). Structural validators using 'd.get("on")' fail without the quote. - Gitea Actions has no native approval-gate UI; the 3-dispatch topology (initial dev, qa-approve, prod-approve+finalize) is the documented fallback. State persists via audit.json on acdl-evidence between dispatches (D-028), NOT via artifacts (artifacts do not survive re-dispatch). - urllib.error.URLError is a separate exception from HTTPError and must be caught explicitly; otherwise a network failure (connection refused, DNS, timeout) stack-traces instead of returning a clean JSON failure. review: p0: 1 p1: 0 p0_items: - id: P0-002 location: contracts-repo/.gitea/workflows/issue-to-contract.yml 'Parse Issue body' step issue: gitea.event.issue.body was interpolated directly into a double-quoted shell argument ('"${{ gitea.event.issue.body }}"'), enabling shell injection from arbitrary Issue text severity: P0 (a malicious Issue could execute arbitrary commands in the workflow step) stride: Elevation of Privilege fix: pass the body via an env var (ISSUE_BODY: ${{ gitea.event.issue.body }}) and reference $ISSUE_BODY in the run: step; Gitea Actions escapes env var values, preventing shell metacharacter interpretation ---/ci--- Layer 1 STRUCTURAL: PASS (4/4 files; bash -n + py_compile + yaml load clean; no TODO/FIXME). Layer 2 BEHAVIORAL: PASS (verify_phase04.sh 12/12 checks). Layer 3 SECURITY: PASS with 1 P0 fixed in this commit (P0-002: shell injection in issue body interpolation -> use env var). Layer 4 QUALITY: PASS. Note: P0-001 (finalize_evidence URLError) was fixed inline during T-4.4 (commit7a93a92). Both P0s are now resolved.