Files
acdl/.ciagent/PROJECT.md
T
grimacing 0672edfc3f ship: phase-05 evidence-ui-and-demo-dry-run (v1.0.5)
Squash merge of phase/05-evidence-ui-and-demo-dry-run; evidence-ui/index.html + run_demo.sh 4-act simulation + verify_phase05.sh; demo live at acdl-evidence raw URL.
2026-07-21 13:53:54 +00:00

16 KiB

ACDL — Agentic Cloud Delivery Platform

Vision / Core Value

A 30-minute executive demo proving that infrastructure can be delivered automatically, safely, and with a complete audit trail — without the usual weeks of manual tickets, reviews, and copy-pasted configuration. Because the demo runs entirely on local stubs (no AWS/GCP/Azure, no external LLM APIs), it shows intent and safety behavior rather than provisioning real cloud resources.

Objective

Build a runnable demo (Linux + GitHub/Gitea Actions) that walks executives through four acts:

  1. Act 1 — The Friction: the old manual 2-week deployment process.
  2. Act 2 — Developer Self-Service: commit a valid contract.yaml for l2-commodity-price-feed, watch Dev auto-run, QA + Prod approval gates, then the evidence timeline.
  3. Act 3 — Citizen Developer: open a GitHub Issue with natural-language intent; the Python keyword parser generates the same contract.yaml and triggers the identical pipeline.
  4. Act 4 — The Safety Net: commit a malicious contract.yaml (public-ingress: true) for l2-regulatory-reporting; the pipeline halts in Dev because the confidence signal drops below 0.50, and the rejection is visible on the evidence stream.

Requirements

Validated

  • Three repos under the continuous-intelligence Gitea org: acdl (platform + stubs + reusable workflows), acdl-contracts (developer surface), acdl-evidence (GitHub Pages audit timeline).
  • L1 modules (single-purpose, substrate-agnostic, max-depth-1 primitives) as folders with manifest.yaml + mock_apply.sh.
  • L2 modules (composed stacks, max-depth-5) grouping L1s into deployable service shapes.
  • L3A developer surface: commit contract.yaml to acdl-contracts.
  • L3B agentic surface: Python keyword parser turning an Issue body into contract.yaml.
  • Confidence signal: base 0.90, drops to 0.40 on policy violation; gate threshold ≥ 0.50.
  • Evidence stream: hash-chained audit.json published via Pages + vanilla-JS index.html timeline.
  • Reusable CI workflow: Dev (autonomous) → QA (manual approval) → Prod (manual approval) → finalize.

Active

  • 8 L1 modules (serverless/container focus): l1-eks-fargate, l1-iam-role, l1-lambda, l1-api-gateway, l1-eventbridge, l1-sqs, l1-s3, l1-cloudwatch.
  • 4 L2 modules mirroring S&P Global Energy / Platts use cases: l2-invoice-service, l2-commodity-price-feed, l2-energy-analytics-api, l2-regulatory-reporting.
  • 5 core scripts: mock_executor.sh, policy_checker.py, confidence_signal.py, evidence_writer.py, l3b_agent_stub.py.
  • Issue-triggered workflow in acdl-contracts that runs the L3B parser, commits a new branch, closes the issue, and triggers the main pipeline.
  • Evidence stream UI (index.html) fetching audit.json and rendering events as a timeline.

Out of Scope

  • Real cloud provisioning (AWS/GCP/Azure).
  • Real LLM inference / external AI APIs.
  • Production-grade infrastructure or multi-tenant isolation.
  • Real cryptographic tamper-proofing (the hash chain is demonstrative, not adversarially secure).

Constraints

  • Environment: local Linux OS.
  • CI/CD: GitHub/Gitea Actions + Environments (QA, Prod approval gates).
  • No cloud — absolutely no AWS, GCP, or Azure resources.
  • No AI — no OpenAI or external LLM APIs; the "Agentic" part is a keyword parser.
  • All state in flat JSON files or CI artifacts.
  • Compute strategy: EKS Fargate + serverless primitives (no VPC module).
  • L1 modules are single-purpose, substrate-agnostic, do not compose with other L1s.
  • L2 modules combine L1 primitives into deployable shapes, max depth 5.

Context

  • Forge: Gitea at https://git.cloudinit.dev, org continuous-intelligence.
  • The acdl repo already exists (empty) at org root and serves as the platform/meta repo.
  • acdl-contracts and acdl-evidence will be created as additional repos in the same org.
  • act_runner / Gitea Actions is the CI runtime; "GitHub Actions" workflow YAML is reused as-is.

Key Decisions

ID Decision Rationale Outcome
D-001 Use Gitea org continuous-intelligence for all repos User-specified target org; already exists Single source of truth for the demo
D-002 Map "GitHub Actions" to Gitea Actions (act_runner) Environment is Gitea; same workflow YAML syntax Demo runs on the actual forge
D-003 Collapse acdl-platform into the existing acdl repo acdl already exists empty at org root 3 repos total: acdl, acdl-contracts, acdl-evidence
D-004 Use Gitea environment blocks + required reviewers for QA/Prod; fallback to manual workflow_dispatch with approval input Approval gates required by spec; forge supports environment protection Frictionless approval gates
D-005 Hash-chained ledger (prev_hash + own hash) for evidence; declared demonstrative Spec asks for simple JSON; chain gives visible tamper-evidence Visible audit timeline without overengineering
D-006 Confidence gate threshold = 0.50 exactly Explicit in spec Acts 2/4 behave as scripted
D-007 Each mock_apply.sh echoes [L1: <name>] applying... + OK, sleeps 1s, exits 0 Spec literal; uniformity aids timeline parsing Predictable evidence events
D-008 Keyword→stack mapping for L3B: gas/price/ingest/data-lake → commodity-price-feed; invoice/billing → invoice-service; analytics/historical/query → energy-analytics-api; regulatory/compliance/reporting/trading → regulatory-reporting; fallback → invoice-service Mirrors the 4 L2 modules + Act 3 example issue Act 3 reproduces deterministic behavior
D-009 Init milestone = v1.0, branch milestone/v1.0-initial init.md Step 5 mandate Branching strategy follows convention
D-010 Single-project mode for the acdl checkout User chose standalone single-project ---ci--- blocks omit project: field
D-011 Single-project mode explicitly enforced via config.json mode: "single" overriding projects[] length signal run.md Step 0 reads projects[] length as multi-project trigger; explicit flag disambiguates No project: prefix in commits or branches
D-012 Gitea has no native Pages — serve acdl-evidence via raw file URLs (/raw/branch/main/...) and a CORS note in ARCHITECTURE.md Research confirms Gitea has no [pages] section Demo can render index.html via raw URL without server-side Pages config
D-013 Gitea has no environments API and ignores jobs.<id>.environment — model QA/Prod gates as workflow_dispatch approval inputs (D-004 fallback) Research confirms environment: blocks are ignored by act_runner Approval gates become dispatch inputs; "environments" become workflow job names + optional branch protection on qa/prod branches
D-014 Cross-repo triggering uses the workflow_dispatch Gitea API (POST /actions/workflows/{id}/dispatches) from inside a step instead of repository_dispatch Gitea Actions does not support repository_dispatch Issue-trigger workflow calls the main pipeline via authenticated dispatch from a step
D-015 New repos acdl-contracts and acdl-evidence use default_branch: "main" with auto_init: true Matches Gitea DEFAULT_BRANCH=main; required for the default branch to exist before any push Reusable-workflow uses: references still pin acdl workflows to @milestone/v1.0-initial
D-016 Pages placeholder for Phase 01 is a minimal HTML stub (<title>ACDL Evidence</title> + "evidence stream coming soon"); full UI deferred to Phase 05 Phase 01 success criterion is "Pages returns 200 with placeholder index.html" but Gitea has no Pages Raw-URL HTTP 200 against index.html substitutes for the Pages check; full timeline UI built in Phase 05
D-017 Each L1 manifest.yaml declares a single inputs: map of named string keys with descriptions; no nested types (substrate-agnostic, max-depth-1) REQ-02/03 say "declared inputs"; spec forbids composition and cloud-specific types Uniform, parseable schema that Phase 03's mock_executor.sh can read with python+yaml
D-018 L1 mock_apply.sh reads its own manifest.yaml for self-identification but ignores the input values (uniform stub per D-007) D-007 mandates a literal echo + 1s sleep + exit 0; inputs are declared for traceability, not consumed Predictable evidence events + clean separation from Phase 03 where L2s pass inputs to L1s
D-019 The 8 L1 names are fixed per REQ-02: l1-eks-fargate, l1-iam-role, l1-lambda, l1-api-gateway, l1-eventbridge, l1-sqs, l1-s3, l1-cloudwatch REQ-02 literal Phase 02 enumerates them exactly; no naming freedom
D-020 L2 manifest.yaml schema: name, kind: l2, description, l1s: (list of {name, inputs: map} entries). L2 references L1s by name (no path); inputs are string maps per L1 manifest declarations REQ-04 says L2 "composes L1s"; REQ-05 caps depth at 5 (L2→L1 is depth 1) mock_executor.sh reads l1s: and invokes each L1's mock_apply.sh
D-021 contract.yaml schema: stack (L2 name), inputs (string map for the L2's top-level params), optional public-ingress: bool (the policy violation key per REQ-07) REQ-07 cites public-ingress: true as the forbidden key; REQ-08's confidence signal keys off policy pass/fail Single flat schema drives both policy_checker and the mock_executor
D-022 mock_executor.sh writes state.json with shape {"l2": "<name>", "l1s": [{"name":"...","applied":true,"exit_code":0}], "contract": <contract.yaml parsed>} to the cwd; idempotent (overwrites) REQ-06 says "writes state.json" but does not specify shape Deterministic, parseable; Phase 05's evidence UI can include it in the audit trail
D-023 evidence_writer.py appends events to audit.json (a JSON array of event objects). Each event: `{"seq": N, "ts": , "stage": "dev qa prod
D-024 confidence_signal.py reads contract.yaml, calls policy_checker.py (as a subprocess or import), returns base 0.90 on pass and 0.40 with reason code on policy failure; prints `{"score": 0.90 0.40, "reason": "<POLICY_VIOLATION:... >"}` to stdout; exit 0 always
D-025 policy_checker.py reads contract.yaml, fails with exit code 1 and stdout POLICY_VIOLATION:PUBLIC_INGRESS if public-ingress: true; otherwise exits 0 with stdout POLICY_PASS REQ-07 literal Single source of policy truth; called by confidence_signal and the pipeline directly
D-026 l3b_agent_stub.py reads Issue body text from argv[1] (or stdin if no argv), applies the D-008 keyword map, writes a contract.yaml to stdout (or to -o <path>). Output contract uses the D-021 schema with stack: set to the mapped L2 name and a fixed inputs: map per L2 D-008 + Act 3 example; L3B must produce the same contract format as L3A Deterministic keyword parser; no external APIs
D-027 Phase 04 models the pipeline as TWO Gitea Actions workflows: (1) acdl/.gitea/workflows/pipeline.ymlon: workflow_call + on: workflow_dispatch (so it can be both called by the contracts-repo trigger AND manually re-dispatched for approvals); (2) acdl-contracts/.gitea/workflows/issue-to-contract.ymlon: issues [opened]. Approval gates are implemented as separate workflow_dispatch inputs (approve_qa: bool, approve_prod: bool) on the pipeline workflow, since Gitea ignores environment: blocks (D-013) Gitea Actions has no environment reviewers, no repository_dispatch, no native approval UI Pipeline can be re-dispatched by a human at each gate; the workflow_dispatch API call from a step (D-014) drives cross-repo triggering
D-028 The pipeline workflow runs all 4 stages (dev, qa-gate, prod-gate, finalize) in a single workflow run, with each gate job checking a workflow_dispatch input (approve_qa/approve_prod). When the input is false (the default), the gate job fails with a clear "awaiting approval" message; the human re-dispatches with approve_qa=true to advance. State (state.json, audit.json, contract ref) is passed via workflow artifacts (upload/download between jobs) because Gitea Actions artifacts work the same as GitHub Actions Gitea Actions supports actions/upload-artifact and actions/download-artifact; the alternative is committing state between jobs, which is heavier Deterministic, observable pipeline; artifacts keep the audit trail within one run
D-029 The finalize step commits audit.json to acdl-evidence main via the Gitea file-contents API (POST /repos/{owner}/{repo}/contents/{path} with the base64 content + a commit message referencing the pipeline run id), exactly like Phase 01's gitea_setup.sh does for index.html. It uses ${GITEA_TOKEN} (a repo secret) for auth D-012 raw-URL approach requires the file to be on main; the API is the only way to put it there from a workflow step The evidence timeline (Phase 05 UI) fetches the raw URL after finalize completes
D-030 The issue-to-contract workflow in acdl-contracts checks out l3b_agent_stub.py from the acdl repo (pinned to @milestone/v1.0-initial per the branch-pin rule), parses the Issue body, commits contract.yaml to a new branch contract/<issue-number> on acdl-contracts, then dispatches the pipeline workflow on the acdl repo via curl POST /actions/workflows/<id>/dispatches with inputs: {contract-ref: contract/<issue-number>} (D-014). The pipeline workflow checks out acdl-contracts at that ref to read the contract Gitea Actions cannot trigger across repos without an explicit API call; the branch carries the contract ref Reproducible Act 3: Issue → contract.yaml → pipeline run with the same contract as Act 2
D-031 Phase 05 "dry run" = a local bash script (scripts/run_demo.sh) that simulates the full pipeline by calling the core scripts in sequence + writing evidence events via evidence_writer.py + uploading audit.json to acdl-evidence via finalize_evidence.py. It does NOT use act_runner (no runner is registered in this environment). It exercises all 4 acts: (1) Friction — a static "manual 2-week" log entry; (2) Developer Self-Service — a valid contract.yaml for l2-commodity-price-feed, full pipeline (dev→qa→prod→finalize), all evidence events; (3) Citizen Developer — an Issue body, l3b_agent_stub.py produces the contract, identical pipeline; (4) Safety Net — a malicious public-ingress: true contract for l2-regulatory-reporting, dev rejects with confidence < 0.50, rejection visible in the timeline The spec says "4 scripted acts reproduce deterministically in a dry run"; without a runner, the bash simulation IS the deterministic reproduction The same audit.json shape is produced as a real pipeline run would produce, so the index.html UI renders the timeline identically
D-032 index.html (vanilla JS) is committed to the acdl repo at evidence-ui/index.html as the source of truth, and pushed to acdl-evidence main by scripts/run_demo.sh (via the file-contents API) alongside audit.json. The UI fetches audit.json from the same raw-URL origin (D-012). It renders events as a vertical timeline with seq, ts, stage (color-coded), event text, and a truncated hash per event. No frameworks; one HTML file with inline CSS + JS D-012 raw-URL approach; the UI must be a single file (no separate JS/CSS) for simplicity; vanilla JS per REQ-14 The UI loads in any browser by visiting the raw URL; it fetches the sibling audit.json and renders
D-033 The audit.json raw URL on acdl-evidence main is the single source of truth for the timeline. run_demo.sh writes the final audit.json (after all 4 acts) and the final index.html in two API calls. Re-running run_demo.sh overwrites both (idempotent). The UI's fetch URL is relative (./audit.json) so the same index.html works against any raw-URL origin D-012 + D-029; relative URL avoids hardcoding the host Deterministic re-run; UI always reflects the latest audit