3ea36ef3ab
Squash merge of phase/03-l2-modules-and-core-scripts; 4 L2s + 5 core scripts; verify_phase03.sh green.
11 KiB
11 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:
- Act 1 — The Friction: the old manual 2-week deployment process.
- Act 2 — Developer Self-Service: commit a valid
contract.yamlforl2-commodity-price-feed, watch Dev auto-run, QA + Prod approval gates, then the evidence timeline. - Act 3 — Citizen Developer: open a GitHub Issue with natural-language intent; the Python keyword parser generates the same
contract.yamland triggers the identical pipeline. - Act 4 — The Safety Net: commit a malicious
contract.yaml(public-ingress: true) forl2-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-intelligenceGitea 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.yamltoacdl-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.jsonpublished via Pages + vanilla-JSindex.htmltimeline. - 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-contractsthat runs the L3B parser, commits a new branch, closes the issue, and triggers the main pipeline. - Evidence stream UI (
index.html) fetchingaudit.jsonand 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, orgcontinuous-intelligence. - The
acdlrepo already exists (empty) at org root and serves as the platform/meta repo. acdl-contractsandacdl-evidencewill 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 |