docs(P05): create 3-wave phase plan (REQ-13/14/15)

---ci---
phase: 5
milestone: v1.0
status: plan
plan:
  waves: 3
  wave_1_frontend: [T-5.1 evidence-ui/index.html]
  wave_2_backend: [T-5.2 scripts/run_demo.sh 4-act simulation]
  wave_3_coordination: [T-5.3 verify_phase05.sh, T-5.4 traceability]
  requirements_covered: [REQ-13, REQ-14, REQ-15]
  must_haves: 6
  verification:
    typecheck: bash -n + py_compile
    test: scripts/verify_phase05.sh (with + without Gitea token)
---/ci---

PLAN.md replaced with Phase 05 plan. Wave 1 (frontend-engineer
REACTIVATED) builds evidence-ui/index.html. Wave 2 (backend-engineer)
builds scripts/run_demo.sh (4-act local simulation + upload). Wave 3
(lead-developer) verifies + traceability. Phase 05 is the milestone
capstone; COMPLETE gate follows.
This commit is contained in:
Jon Chery
2026-07-21 13:45:07 +00:00
parent d95c4015e3
commit 0a1ed28c50
+64 -52
View File
@@ -1,88 +1,100 @@
---
phase: 04
name: pipeline-and-approval-gates
phase: 05
name: evidence-ui-and-demo-dry-run
milestone: v1.0
milestone_type: feature
status: planned
requirements: [REQ-10, REQ-12]
requirements: [REQ-13, REQ-14, REQ-15]
must_haves:
- ".gitea/workflows/pipeline.yml is a real workflow (no placeholders) with workflow_dispatch inputs (contract-ref, approve_qa, approve_prod) and 4 jobs (dev, qa-gate, prod-gate, finalize) with correct if: conditions per D-027/D-028"
- "The dev job checks out acdl + acdl-contracts@contract-ref, runs policy_checker.py + confidence_signal.py + mock_executor.sh, and writes a dev_applied or dev_rejected evidence event to acdl-evidence via the file-contents API"
- "The qa-gate job (approve_qa=true) writes a qa_approved evidence event"
- "The prod-gate + finalize jobs (approve_prod=true) write prod_approved + finalize evidence events, committing the final audit.json to acdl-evidence main"
- "contracts-repo/.gitea/workflows/issue-to-contract.yml is a real workflow (no placeholders) that checks out l3b_agent_stub.py from acdl, parses the Issue body, commits contract.yaml to contract/<issue-number> on acdl-contracts, and dispatches pipeline.yml with contract-ref=contract/<issue-number>"
- "scripts/finalize_evidence.py helper exists: given an audit.json file path, GETs the current file sha from acdl-evidence, PUTs the new content via the file-contents API. Used by the workflow steps to commit evidence updates."
- "scripts/verify_phase04.sh passes: validates the workflow YAML syntax, confirms job if: conditions, confirms issue-to-contract.yml references the dispatch API, runs a dry-run of finalize_evidence.py against a temp audit.json (mock API)"
- "evidence-ui/index.html exists: single HTML file with inline CSS + JS, no external resources, no frameworks (D-032, REQ-14)"
- "index.html fetches ./audit.json (relative URL) and renders events as a vertical timeline with stage color-coding (dev/qa/prod/finalize/genesis) + 12-char hash preview"
- "scripts/run_demo.sh exists: simulates all 4 acts (Friction, Dev Self-Service, Citizen Developer, Safety Net) by calling the Phase 03 core scripts + evidence_writer.py + finalize_evidence.py; writes a final audit.json; uploads audit.json + evidence-ui/index.html to acdl-evidence main via the Gitea file-contents API (D-031/D-033)"
- "scripts/run_demo.sh is idempotent: re-running overwrites both files on acdl-evidence and produces the same audit.json (deterministic hash chain)"
- "Act 4 produces a 'dev rejected: POLICY_VIOLATION:PUBLIC_INGRESS' evidence event with score 0.40 (the Safety Net)"
- "scripts/verify_phase05.sh passes: validates index.html structure (single file, inline, fetch call), runs run_demo.sh, fetches the raw audit.json + index.html URLs from acdl-evidence and confirms HTTP 200"
verification:
typecheck: "bash -n scripts/*.sh && python3 -m py_compile scripts/*.py && python3 -c 'import yaml; yaml.safe_load(open(\".gitea/workflows/pipeline.yml\")); yaml.safe_load(open(\"contracts-repo/.gitea/workflows/issue-to-contract.yml\"))'"
test: "scripts/verify_phase04.sh"
typecheck: "bash -n scripts/*.sh && python3 -m py_compile scripts/*.py"
test: "scripts/verify_phase05.sh"
build: no-op
---
# Phase 04pipeline-and-approval-gates PLAN
# Phase 05evidence-ui-and-demo-dry-run PLAN
## Goal
Replace the Phase 01 workflow skeletons with real implementations. Wire
the Dev → QA → Prod → Finalize pipeline with the 3-dispatch approval-gate
pattern (D-027/D-028), and the issue-to-contract trigger (D-030).
Build the vanilla-JS timeline UI and run the 4 demo acts as a local dry
run that produces a real `audit.json` and publishes it (plus the UI) to
`acdl-evidence` main. This is the milestone's capstone phase.
## Requirements covered
- REQ-10: reusable pipeline runs Dev (autonomous), QA (approval), Prod (approval), Finalize (commits audit.json to acdl-evidence)
- REQ-12: opening an Issue in acdl-contracts runs l3b_agent_stub.py, commits a contract.yaml to a new branch, closes the Issue, and triggers the main pipeline
- REQ-13: `acdl-evidence` is Pages-enabled and serves `audit.json` plus `index.html` (substituted by D-012: raw-URL 200 on both files)
- REQ-14: `index.html` uses vanilla JS to fetch `audit.json` and render events as a timeline
- REQ-15: All four demo acts reproduce deterministically in a dry run
## Waves (vertical slices, domain priority order)
### Wave 1 — backend-engineer (pipeline + finalize helper + issue trigger)
### Wave 1 — frontend-engineer (the UI)
**Tasks:**
- **T-4.1** Create `scripts/finalize_evidence.py` — a helper that takes `--audit <path>` `--owner <org>` `--repo <name>` `--branch <name>` (defaults: continuous-intelligence, acdl-evidence, main) and `--token-env <name>` (default ACDL_GITEA_TOKEN). It GETs the current `audit.json` from the repo (to get the sha if it exists), reads the local `<audit>` file, PUTs (or POSTs if 404) the new content via the Gitea file-contents API with base64 + commit message "finalize: update audit.json (run <gitea.run_id>)". Exit 0 on success, 1 on API failure. Used by the pipeline finalize + each stage to persist evidence to acdl-evidence.
- **T-4.2** Replace `.gitea/workflows/pipeline.yml` with the real implementation:
- `on: workflow_dispatch` with inputs `contract-ref` (string, default `main`), `approve_qa` (boolean, default `false`), `approve_prod` (boolean, default `false`).
- Job `dev`: `if: inputs.approve_qa != true && inputs.approve_prod != true`. Steps: checkout `acdl` (current repo) at `milestone/v1.0-initial`; checkout `acdl-contracts` at `inputs.contract-ref` into a sibling dir using `actions/checkout@v4` with `repository: continuous-intelligence/acdl-contracts`, `ref: ${{ inputs.contract-ref }}`, `token: ${{ secrets.GITEA_TOKEN }}`; run `python3 scripts/policy_checker.py ../acdl-contracts/contract.yaml`; run `python3 scripts/confidence_signal.py ../acdl-contracts/contract.yaml` and capture the score; if score < 0.50, run `python3 scripts/evidence_writer.py --stage dev --event "dev rejected: confidence < 0.50"` and `python3 scripts/finalize_evidence.py --audit audit.json` and `exit 1`; else run `bash scripts/mock_executor.sh ../acdl-contracts/contract.yaml`, then `python3 scripts/evidence_writer.py --stage dev --event "dev applied: <stack>"`, then `python3 scripts/finalize_evidence.py --audit audit.json`.
- Job `qa-gate`: `if: inputs.approve_qa == true && inputs.approve_prod != true`. Steps: checkout acdl; run `python3 scripts/evidence_writer.py --stage qa --event "qa approved"`; `python3 scripts/finalize_evidence.py --audit audit.json`.
- Job `prod-gate`: `if: inputs.approve_prod == true`. Steps: checkout acdl; run `python3 scripts/evidence_writer.py --stage prod --event "prod approved"`; `python3 scripts/finalize_evidence.py --audit audit.json`.
- Job `finalize`: `needs: prod-gate`. Steps: checkout acdl; run `python3 scripts/evidence_writer.py --stage finalize --event "pipeline complete: audit.json committed to acdl-evidence"`; `python3 scripts/finalize_evidence.py --audit audit.json`.
- All jobs `runs-on: ubuntu-latest`. Use `gitea.*` context where relevant.
- **T-4.3** Replace `contracts-repo/.gitea/workflows/issue-to-contract.yml` with the real implementation:
- `on: issues` with `types: [opened]`.
- Job `parse-and-trigger`: `runs-on: ubuntu-latest`. Steps: checkout `acdl` (to get `l3b_agent_stub.py`) at `milestone/v1.0-initial`; run `python3 scripts/l3b_agent_stub.py "${{ gitea.event.issue.body }}" -o contract.yaml`; parse the contract to confirm a stack was set; commit `contract.yaml` to a new branch `contract/${{ gitea.event.issue.number }}` on `acdl-contracts` via the file-contents API (POST); comment on the Issue with the contract summary; close the Issue via the issues API; dispatch the pipeline workflow via `POST /api/v1/repos/continuous-intelligence/acdl/actions/workflows/pipeline.yml/dispatches` with body `{ "ref": "milestone/v1.0-initial", "inputs": { "contract-ref": "contract/${{ gitea.event.issue.number }}" } }` using `secrets.GITEA_TOKEN`.
- **T-5.1** Create `evidence-ui/index.html` — single HTML file with inline `<style>` + `<script>`. The JS fetches `./audit.json` (relative URL), parses the JSON array, and renders a vertical timeline. Each event card shows: `seq` (badge), `ts` (timestamp), `stage` (color-coded chip: dev=blue, qa=yellow, prod=orange, finalize=green, genesis=gray, rejected=red), `event` text, and `hash.slice(0, 12) + "…"` (a 12-char preview). Includes a refresh button that re-fetches. Handles fetch failure with a "No audit data yet" message. No external resources (no CDN, no fetch libraries); vanilla JS only. The file is self-contained.
**Files owned:** `scripts/finalize_evidence.py`, `.gitea/workflows/pipeline.yml`, `contracts-repo/.gitea/workflows/issue-to-contract.yml`
**Files owned:** `evidence-ui/index.html`
**Commits:** one per task, `phase: 4, status: plan-as-execute, persona: backend-engineer, task: T-4.x, requirements.covered: [REQ-10 or REQ-12]`.
**Commits:** one commit, `phase: 5, status: plan-as-execute, persona: frontend-engineer, task: T-5.1, requirements.covered: [REQ-14]`.
### Wave 2 — lead-developer (verify script + traceability)
### Wave 2 — backend-engineer (the dry-run script)
**Tasks:**
- **T-4.4** Create `scripts/verify_phase04.sh`. Checks:
1. `.gitea/workflows/pipeline.yml` parses as YAML; has `on.workflow_dispatch` with inputs `contract-ref`, `approve_qa`, `approve_prod`; has 4 jobs `dev`, `qa-gate`, `prod-gate`, `finalize`; `dev.if` excludes approve_qa/approve_prod; `qa-gate.if` requires approve_qa; `prod-gate.if` requires approve_prod; `finalize.needs == prod-gate`.
2. `contracts-repo/.gitea/workflows/issue-to-contract.yml` parses as YAML; has `on.issues` with `[opened]`; the job references `l3b_agent_stub.py` and the dispatch API endpoint `/actions/workflows/pipeline.yml/dispatches`.
3. `scripts/finalize_evidence.py` is py_compile clean; `--help` exits 0; called with a non-existent token env fails cleanly (exit 1, not a stack trace).
4. `bash -n` + `py_compile` + `yaml.safe_load` on all phase files.
5. End-to-end mini-test: run `evidence_writer.py` to produce a temp `audit.json`, then call `finalize_evidence.py` against a mock Gitea API (set `GITEA_HOST=http://127.0.0.1:0` so the connection fails; verify the script exits 1 cleanly without crashing).
- **T-4.5** Update `.ciagent/REQUIREMENTS.md` (REQ-10/12 → covered pending VERIFY) and `.ciagent/ROADMAP.md` (Phase 04 → executing).
- **T-5.2** Create `scripts/run_demo.sh` — the Phase 05 dry-run simulation. It:
1. Accepts an optional `--no-upload` flag (for testing without hitting Gitea).
2. Creates a clean working directory under `/tmp/acdl_demo_run/`; sets `AUDIT=/tmp/acdl_demo_run/audit.json`.
3. Initializes the audit log: `python3 scripts/evidence_writer.py --stage genesis --event "audit log initialized" --audit "$AUDIT"`.
4. **Act 1 — Friction:** write a single event `--stage dev --event "Act 1 Friction: manual 2-week deployment (legacy process)"`.
5. **Act 2 — Developer Self-Service:** write `contracts/act2.yaml` with `stack: l2-commodity-price-feed`, `public-ingress: false`. Run `policy_checker.py` + `confidence_signal.py` + `mock_executor.sh`. Write events: `dev applied: l2-commodity-price-feed`, `qa approved`, `prod approved`, `finalize: audit.json committed`.
6. **Act 3 — Citizen Developer:** feed an Issue body ("We need to ingest natural gas prices from Platts...") to `l3b_agent_stub.py -o contracts/act3.yaml`. Run the same pipeline as Act 2 against the generated contract. Write 4 events.
7. **Act 4 — Safety Net:** write `contracts/act4.yaml` with `stack: l2-regulatory-reporting`, `public-ingress: true`. Run `policy_checker.py` (fails) + `confidence_signal.py` (score 0.40). Since score < 0.50, write `dev rejected: POLICY_VIOLATION:PUBLIC_INGRESS` and skip QA/Prod/Finalize.
8. Print a summary of all events.
9. If `--no-upload` is NOT set: call `python3 scripts/finalize_evidence.py --audit "$AUDIT"` to upload `audit.json` to `acdl-evidence`, then call `finalize_evidence.py --audit evidence-ui/index.html --path index.html --message "chore(ui): update index.html"` to upload the UI. (Reuses `finalize_evidence.py` with `--path` override for `index.html`.)
10. Exit 0 if all 4 acts produced the expected evidence events; non-zero otherwise.
**Files owned:** `scripts/verify_phase04.sh`, `.ciagent/REQUIREMENTS.md`, `.ciagent/ROADMAP.md`
Cleanup: write contracts under `contracts/` (gitignored) so the working tree stays clean.
**Commits:** one per task, `phase: 4, status: plan-as-execute, persona: lead-developer, task: T-4.4/4.5`.
**Files owned:** `scripts/run_demo.sh`
**Commits:** one commit, `phase: 5, status: plan-as-execute, persona: backend-engineer, task: T-5.2, requirements.covered: [REQ-13, REQ-15]`.
### Wave 3 — lead-developer (verify script + traceability)
**Tasks:**
- **T-5.3** Create `scripts/verify_phase05.sh`. Checks:
1. `evidence-ui/index.html` exists, is a single file, contains `<style>` and `<script>` inline tags, contains `fetch('./audit.json'` (relative URL), no `https://` external resource references (no CDN).
2. `scripts/run_demo.sh` is `bash -n` clean.
3. Run `scripts/run_demo.sh --no-upload` and confirm:
- It exits 0.
- It produces a non-empty `audit.json` with at least 14 events (genesis + act1 + act2[4] + act3[4] + act4[1] = 11 minimum, but with markers it may be more — use `>= 11`).
- The audit chain is valid (re-run the hash check).
- The Act 4 event contains "POLICY_VIOLATION:PUBLIC_INGRESS".
4. If `ACDL_GITEA_TOKEN` is set: run `scripts/run_demo.sh` (with upload), then curl the raw URLs for `audit.json` and `index.html` on `acdl-evidence` and confirm HTTP 200 + that the audit.json matches the local one (or at least parses as JSON with the expected number of events) + that index.html contains "ACDL Evidence" or "audit.json" reference.
5. If `ACDL_GITEA_TOKEN` is NOT set: skip the upload check with a clear "SKIP (no token)" message; the dry-run + structural checks are sufficient.
- **T-5.4** Update `.ciagent/REQUIREMENTS.md` (REQ-13/14/15 → covered pending VERIFY) and `.ciagent/ROADMAP.md` (Phase 05 → executing).
**Files owned:** `scripts/verify_phase05.sh`, `.ciagent/REQUIREMENTS.md`, `.ciagent/ROADMAP.md`
**Commits:** one per task, `phase: 5, status: plan-as-execute, persona: lead-developer, task: T-5.3/5.4`.
## Wave ordering
- Wave 1 (backend) builds the pipeline + helper + issue trigger.
- Wave 2 (lead-developer) wires the verify script after the workflows exist.
- Wave 1 (frontend) builds the UI.
- Wave 2 (backend) builds the dry-run script.
- Wave 3 (lead-developer) verifies + traceability.
`infra-stub-engineer`, `data-engineer`, `frontend-engineer` have 0 tasks.
Wave 1 and Wave 2 can run in parallel (no file overlap), but per execute.md the domain priority is `coordination → backend → frontend → custom`. Here `frontend-engineer` and `backend-engineer` have no territory overlap, so they can run sequentially in priority order: backend first (Wave 2), then frontend (Wave 1) — but for clarity I'll keep Wave 1 = frontend, Wave 2 = backend (the UI is the visible artifact; the script needs to upload it). Actually, `run_demo.sh` references `evidence-ui/index.html`, so the UI must exist before the script is tested end-to-end. Order: Wave 1 (UI) → Wave 2 (script) → Wave 3 (verify).
## Dependencies
- Depends on Phase 03 (all 5 core scripts must exist for the workflow to invoke them).
- Phase 05 depends on this phase for the finalize step to produce `audit.json` on `acdl-evidence` that the UI fetches.
## Risk notes
- The full pipeline cannot be exercised end-to-end without an act_runner registered to the `acdl` repo (Phase 04 ships the workflow but cannot trigger a real run in this environment). verify_phase04.sh validates structure + syntax + a dry-run of finalize_evidence against a dead host; the end-to-end Act 2/3/4 dry run is Phase 05.
- `actions/checkout@v4` cross-repo requires the `GITEA_TOKEN` secret to be set on the `acdl` repo (out-of-band Gitea UI step). verify_phase04 cannot test this; documented in the workflow YAML comments.
- Depends on Phases 01-04 (all core scripts, workflows, and the acdl-evidence repo must exist).
- This is the last phase in the milestone. After Phase 05 ships, the COMPLETE gate runs: review → ship(milestone v1.1.0) → audit.