docs(P01): complete repo-scaffolding phase

---ci---
phase: 1
milestone: v1.0
status: complete
requirements:
  covered: [REQ-01, REQ-09]
  partial: [REQ-10, REQ-12]
---/ci---

Squash merge of phase/01-repo-scaffolding into milestone/v1.0-initial.
Phase 01 ships the three-repo scaffold (acdl, acdl-contracts, acdl-evidence),
the placeholder index.html on acdl-evidence (D-012/D-016 raw-URL substitute
for unsupported Gitea Pages), the qa + prod branches on acdl-contracts
(D-013 stand-in for unsupported Gitea environments), the workflow skeletons
(pipeline.yml + issue-to-contract.yml), and the idempotent setup + verify
scripts. REQ-10/12 remain partial pending Phase 04 full implementation.
This commit is contained in:
Jon Chery
2026-07-21 13:10:50 +00:00
parent 38eaec9337
commit c6bac71faf
13 changed files with 521 additions and 5 deletions
+4 -4
View File
@@ -57,7 +57,7 @@
| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-01 | 1 | pending |
| REQ-01 | 1 | covered (pending VERIFY) |
| REQ-02 | 2 | pending |
| REQ-03 | 2 | pending |
| REQ-04 | 3 | pending |
@@ -65,10 +65,10 @@
| REQ-06 | 3 | pending |
| REQ-07 | 3 | pending |
| REQ-08 | 3 | pending |
| REQ-09 | 1 | pending |
| REQ-10 | 4 | pending |
| REQ-09 | 1 | covered (pending VERIFY) |
| REQ-10 | 4 | partial (skeleton in Phase 01; full impl in Phase 04) |
| REQ-11 | 3 | pending |
| REQ-12 | 4 | pending |
| REQ-12 | 4 | partial (skeleton in Phase 01; full impl in Phase 04) |
| REQ-13 | 5 | pending |
| REQ-14 | 5 | pending |
| REQ-15 | 5 | pending |
+1 -1
View File
@@ -8,7 +8,7 @@ Five-phase breakdown to take ACDL from empty repo to a reproducible 4-act execut
### Phase 01 — repo-scaffolding
- **Description:** Create the three repos under `continuous-intelligence` (`acdl-contracts`, `acdl-evidence`; `acdl` already exists), seed directory layouts, configure Pages on `acdl-evidence`, add environment protection for `qa` and `prod` on `acdl-contracts`.
- **Status:** not_started
- **Status:** executing
- **Depends on:** —
- **Requirements:** REQ-01, REQ-09, REQ-10
- **Success Criteria:**
View File
+75
View File
@@ -0,0 +1,75 @@
# ACDL reusable pipeline workflow (Phase 01 skeleton).
#
# This workflow is called from acdl-contracts via:
# uses: continuous-intelligence/acdl/.gitea/workflows/pipeline.yml@milestone/v1.0-initial
#
# Branch pinning rule (see .ciagent/ARCHITECTURE.md): the `acdl` repo's default
# branch is `milestone/v1.0-initial`, so `uses:` references must pin to
# `@milestone/v1.0-initial`, NOT `@main`.
#
# Phase 04 will implement the actual stage logic + approval gates (D-013:
# Gitea has no environments API; gates become workflow_dispatch approval
# inputs).
name: acdl-pipeline
on:
workflow_call:
inputs:
contract-ref:
description: "Ref on acdl-contracts that triggered the pipeline"
required: false
type: string
default: main
jobs:
dev:
name: "Dev (autonomous)"
runs-on: ubuntu-latest
steps:
# Phase 04 will implement: checkout acdl + acdl-contracts, run
# policy_checker.py, mock_executor.sh, confidence_signal.py, write
# evidence via evidence_writer.py.
- name: "Dev stage placeholder"
run: |
echo "Dev stage placeholder (Phase 01 skeleton)"
echo "Phase 04 will run policy_checker, mock_executor, confidence_signal, evidence_writer"
exit 0
qa-gate:
name: "QA (manual approval)"
needs: dev
runs-on: ubuntu-latest
steps:
# Phase 04 will implement: gate via workflow_dispatch approval input
# (D-013 fallback; Gitea ignores jobs.<id>.environment).
- name: "QA gate placeholder"
run: |
echo "QA gate placeholder (Phase 01 skeleton)"
echo "Phase 04 will pause here for human approval via workflow_dispatch"
exit 0
prod-gate:
name: "Prod (manual approval)"
needs: qa-gate
runs-on: ubuntu-latest
steps:
# Phase 04 will implement: same approval-input gate as qa-gate.
- name: "Prod gate placeholder"
run: |
echo "Prod gate placeholder (Phase 01 skeleton)"
echo "Phase 04 will pause here for human approval via workflow_dispatch"
exit 0
finalize:
name: "Finalize (publish evidence)"
needs: prod-gate
runs-on: ubuntu-latest
steps:
# Phase 04/05 will implement: commit audit.json to acdl-evidence main
# via the Gitea file-contents API; raw URL republishes index.html +
# audit.json for the timeline UI (D-012).
- name: "Finalize placeholder"
run: |
echo "Finalize placeholder (Phase 01 skeleton)"
echo "Phase 04/05 will commit audit.json to acdl-evidence main"
exit 0
+9
View File
@@ -0,0 +1,9 @@
__pycache__/
*.pyc
*.pyo
.env
.env.*
state.json
audit.json
*.tmp
.DS_Store
+55
View File
@@ -0,0 +1,55 @@
# ACDL — Agentic Cloud Delivery Platform
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.
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.
## 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/Gitea 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.
## Repositories
All under the `continuous-intelligence` Gitea org at
`https://git.cloudinit.dev`:
- `acdl` (this repo) — platform + stubs + reusable workflows
- `acdl-contracts` — developer surface (`contract.yaml` + issue trigger)
- `acdl-evidence` — audit timeline (served via raw file URLs; Gitea has no
native Pages — see `.ciagent/ARCHITECTURE.md` Gitea API Surface table)
## Project metadata
See `.ciagent/PROJECT.md` for the full spec, `.ciagent/ROADMAP.md` for the
5-phase breakdown, `.ciagent/REQUIREMENTS.md` for traceable requirements,
and `.ciagent/PERSONAS.md` for the active persona roster.
## Phase 01 verification
After running `scripts/gitea_setup.sh` (which creates `acdl-contracts` and
`acdl-evidence` in the org and pushes the placeholder `index.html`), run:
```bash
ACDL_GITEA_TOKEN=<token> scripts/verify_phase01.sh
```
The script confirms:
- both new repos exist via the Gitea API
- the raw `index.html` URL on `acdl-evidence` returns HTTP 200
- the `qa` and `prod` branches exist on `acdl-contracts`
Exit 0 = Phase 01 success criteria met.
@@ -0,0 +1,40 @@
# ACDL issue-to-contract workflow (Phase 01 skeleton, reference copy).
#
# This file is the source-of-truth copy kept in the `acdl` repo under
# contracts-repo/.gitea/workflows/. Phase 04 will push it to the actual
# `acdl-contracts` repo under .gitea/workflows/ and implement the real
# step bodies.
#
# Trigger: a new Issue is opened in acdl-contracts. The workflow runs
# l3b_agent_stub.py to map the Issue body to a contract.yaml, commits the
# contract to a new branch, closes the Issue, and triggers the main
# pipeline in the `acdl` repo via the workflow_dispatch API (D-014; Gitea
# Actions does not support repository_dispatch).
name: issue-to-contract
on:
issues:
types: [opened]
jobs:
parse-and-trigger:
runs-on: ubuntu-latest
steps:
# Phase 04 will implement:
# 1. checkout acdl-contracts (so l3b_agent_stub.py is available).
# 2. run: python3 scripts/l3b_agent_stub.py "${{ gitea.event.issue.body }}" > contract.yaml
# 3. parse the generated contract; commit it to a new branch
# (e.g. contract/<issue-number>).
# 4. push the branch.
# 5. close the Issue with a comment linking to the pipeline run.
# 6. trigger the main pipeline:
# curl -X POST \
# -H "Authorization: token ${GITEA_TOKEN}" \
# https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/actions/workflows/<id>/dispatches \
# -d '{"ref":"milestone/v1.0-initial","inputs":{"contract-ref":"<branch>"}}'
- name: "Issue-trigger placeholder"
run: |
echo "issue-to-contract placeholder (Phase 01 skeleton)"
echo "Issue body: ${{ gitea.event.issue.body }}"
echo "Phase 04 will run l3b_agent_stub.py, commit contract.yaml, close issue, dispatch pipeline"
exit 0
View File
View File
View File
+228
View File
@@ -0,0 +1,228 @@
#!/usr/bin/env bash
# Phase 01 Gitea scaffolding. Idempotent.
#
# Creates the two new repos under the continuous-intelligence org, pushes a
# placeholder index.html to acdl-evidence, and creates qa + prod branches on
# acdl-contracts. Running against existing repos / branches / files is a
# no-op (409 or 422 is treated as success).
#
# Usage: ACDL_GITEA_TOKEN=<token> scripts/gitea_setup.sh
# Exit codes: 0 = success (created or already existed); 1 = unrecoverable error.
set -euo pipefail
GITEA_HOST="${GITEA_HOST:-https://git.cloudinit.dev}"
ORG="continuous-intelligence"
TOKEN="${ACDL_GITEA_TOKEN:?ACDL_GITEA_TOKEN is required}"
API="${GITEA_HOST}/api/v1"
AUTH=(-H "Authorization: token ${TOKEN}" -H "Content-Type: application/json")
log() { printf '[setup] %s\n' "$*"; }
warn() { printf '[setup][WARN] %s\n' "$*" >&2; }
err() { printf '[setup][ERROR] %s\n' "$*" >&2; }
# --- helpers ----------------------------------------------------------------
# http_status_code URL
http_get_status() {
local url="$1"
curl -sS -o /dev/null -w "%{http_code}" "${AUTH[@]}" "$url"
}
# repo_exists NAME -> 0 if exists, 1 otherwise
repo_exists() {
local name="$1"
local status
status=$(http_get_status "${API}/repos/${ORG}/${name}")
[ "$status" = "200" ]
}
# create_repo NAME DESCRIPTION
create_repo() {
local name="$1"
local description="$2"
local body
body=$(python3 -c "
import json, sys
print(json.dumps({
'name': '${name}',
'description': ${description@Q},
'private': True,
'default_branch': 'main',
'auto_init': True,
'gitignores': 'Python',
'license': '',
'readme': 'Default'
}))
")
log "Creating repo ${ORG}/${name} (default_branch=main, auto_init=true)"
local status body_out
status=$(curl -sS -o /tmp/setup_repo_create.json -w "%{http_code}" \
"${AUTH[@]}" -X POST -d "$body" \
"${API}/orgs/${ORG}/repos")
case "$status" in
201) log " created (HTTP 201)" ;;
409) log " already exists (HTTP 409); skipping" ;;
*)
err "create_repo ${name} failed: HTTP ${status}"
cat /tmp/setup_repo_create.json >&2 || true
return 1
;;
esac
}
# set_repo_visibility REPO VISIBILITY (public|private)
set_repo_visibility() {
local repo="$1"
local visibility="$2"
local body
body=$(python3 -c "
import json
is_private = ('${visibility}' == 'private')
print(json.dumps({'private': is_private, 'visibility': '${visibility}'}))
")
log "Setting ${repo} visibility to ${visibility}"
local status
status=$(curl -sS -o /tmp/setup_vis.json -w "%{http_code}" \
"${AUTH[@]}" -X PATCH -d "$body" \
"${API}/repos/${ORG}/${repo}")
case "$status" in
200) log " ok (HTTP 200)" ;;
*) warn "set_repo_visibility ${repo} -> ${visibility} returned HTTP ${status} (continuing)"; cat /tmp/setup_vis.json >&2 || true ;;
esac
}
# file_exists REPO PATH -> 0 if the file already exists on the default branch
file_exists_on_default() {
local repo="$1"
local path="$2"
local status
status=$(http_get_status "${API}/repos/${ORG}/${repo}/contents/${path}?ref=main")
[ "$status" = "200" ]
}
# create_placeholder_index REPO
create_placeholder_index() {
local repo="$1"
local path="index.html"
local placeholder
placeholder='<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ACDL Evidence</title>
<style>body{font-family:system-ui,sans-serif;margin:2rem;color:#333}</style>
</head>
<body>
<h1>ACDL Evidence Stream</h1>
<p>Evidence timeline will appear here in Phase 05.</p>
<p>Placeholder served via Gitea raw file URL (D-012; Gitea has no native Pages).</p>
</body>
</html>'
if file_exists_on_default "$repo" "$path"; then
log "index.html already exists on ${repo} main; skipping"
return 0
fi
local body
body=$(python3 -c "
import json, base64
content = '''${placeholder}'''
print(json.dumps({
'content': base64.b64encode(content.encode('utf-8')).decode('ascii'),
'message': 'Initial placeholder index.html (Phase 01, D-016)',
'branch': 'main'
}))
")
log "Pushing placeholder index.html to ${repo} main"
local status
status=$(curl -sS -o /tmp/setup_index_push.json -w "%{http_code}" \
"${AUTH[@]}" -X POST -d "$body" \
"${API}/repos/${ORG}/${repo}/contents/${path}")
case "$status" in
201) log " pushed (HTTP 201)" ;;
409|422) log " already exists or conflict (HTTP ${status}); skipping" ;;
*)
err "create_placeholder_index on ${repo} failed: HTTP ${status}"
cat /tmp/setup_index_push.json >&2 || true
return 1
;;
esac
}
# branch_exists REPO BRANCH -> 0 if exists
branch_exists() {
local repo="$1"
local branch="$2"
local status
status=$(http_get_status "${API}/repos/${ORG}/${repo}/branches/${branch}")
[ "$status" = "200" ]
}
# create_branch REPO BRANCH FROM_REF
create_branch() {
local repo="$1"
local branch="$2"
local from_ref="$3"
if branch_exists "$repo" "$branch"; then
log "Branch ${branch} already exists on ${repo}; skipping"
return 0
fi
local body
body=$(python3 -c "
import json
print(json.dumps({'new_branch_name': '${branch}', 'old_branch_name': '${from_ref}'}))
")
log "Creating branch ${branch} on ${repo} from ${from_ref}"
local status
status=$(curl -sS -o /tmp/setup_branch.json -w "%{http_code}" \
"${AUTH[@]}" -X POST -d "$body" \
"${API}/repos/${ORG}/${repo}/branches")
case "$status" in
201) log " created (HTTP 201)" ;;
409) log " already exists (HTTP 409); skipping" ;;
*)
err "create_branch ${branch} on ${repo} failed: HTTP ${status}"
cat /tmp/setup_branch.json >&2 || true
return 1
;;
esac
}
# --- main -------------------------------------------------------------------
log "Host: ${GITEA_HOST}"
log "Org: ${ORG}"
log "Token: <set, ${#TOKEN} chars>"
# Step 1: create acdl-contracts
if ! repo_exists acdl-contracts; then
create_repo acdl-contracts "ACDL developer + agentic entry surface (contract.yaml + issue trigger)" || exit 1
else
log "acdl-contracts already exists; skipping create"
fi
# Step 2: create acdl-evidence
if ! repo_exists acdl-evidence; then
create_repo acdl-evidence "ACDL hash-chained audit timeline served as a static site via raw file URLs" || exit 1
else
log "acdl-evidence already exists; skipping create"
fi
# Step 2b: make acdl-evidence public so the Phase 05 UI (index.html) can
# fetch audit.json from a browser without exposing the API token (D-012
# raw-URL approach). acdl-contracts stays private.
set_repo_visibility acdl-evidence public
# Step 3: push placeholder index.html to acdl-evidence
create_placeholder_index acdl-evidence || exit 1
# Step 4: create qa + prod branches on acdl-contracts (visible stand-in for
# the unsupported Gitea environments API; per D-013).
create_branch acdl-contracts qa main || exit 1
create_branch acdl-contracts prod main || exit 1
log "Done. Run scripts/verify_phase01.sh to confirm success criteria."
exit 0
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
# Phase 01 verification script.
# Confirms the three-repo scaffold exists under the continuous-intelligence
# Gitea org and that the Phase 01 visible artifacts (placeholder index.html on
# acdl-evidence; qa + prod branches on acdl-contracts) are present.
#
# Usage: ACDL_GITEA_TOKEN=<token> scripts/verify_phase01.sh
# Exit codes: 0 = all checks passed; 1 = one or more checks failed.
set -euo pipefail
GITEA_HOST="${GITEA_HOST:-https://git.cloudinit.dev}"
ORG="continuous-intelligence"
TOKEN="${ACDL_GITEA_TOKEN:-}"
fail_count=0
note() { printf ' [%s] %s\n' "$1" "$2"; }
pass() { note "PASS" "$1"; }
fail() { note "FAIL" "$1"; fail_count=$((fail_count + 1)); }
warn() { printf ' [WARN] %s\n' "$1" >&2; }
echo "== Phase 01 verification =="
echo "Host: $GITEA_HOST"
echo "Org: $ORG"
if [ -n "$TOKEN" ]; then
echo "Token: <set, ${#TOKEN} chars>"
else
echo "Token: <unset>"
fi
echo
# --- Check 1: acdl-contracts repo exists ---
echo "-- Check 1: acdl-contracts repo exists --"
status=$(curl -sS -o /tmp/p01_contracts.json -w "%{http_code}" \
-H "Authorization: token ${TOKEN}" \
"${GITEA_HOST}/api/v1/repos/${ORG}/acdl-contracts")
if [ "$status" = "200" ]; then
default_branch=$(python3 -c "import json; print(json.load(open('/tmp/p01_contracts.json')).get('default_branch','?'))")
pass "acdl-contracts exists (default_branch=${default_branch})"
else
fail "acdl-contracts GET returned HTTP ${status}"
fi
# --- Check 2: acdl-evidence repo exists ---
echo "-- Check 2: acdl-evidence repo exists --"
status=$(curl -sS -o /tmp/p01_evidence.json -w "%{http_code}" \
-H "Authorization: token ${TOKEN}" \
"${GITEA_HOST}/api/v1/repos/${ORG}/acdl-evidence")
if [ "$status" = "200" ]; then
default_branch=$(python3 -c "import json; print(json.load(open('/tmp/p01_evidence.json')).get('default_branch','?'))")
pass "acdl-evidence exists (default_branch=${default_branch})"
else
fail "acdl-evidence GET returned HTTP ${status}"
fi
# --- Check 3: acdl-evidence raw index.html returns 200 (Pages substitute per D-012/D-016) ---
# acdl-evidence is public per gitea_setup.sh step 2b, so the raw URL should
# work without auth. We also try with the auth header as a fallback so the
# check does not spuriously fail if the repo visibility was reset.
echo "-- Check 3: acdl-evidence raw index.html returns 200 --"
index_url="${GITEA_HOST}/${ORG}/acdl-evidence/raw/branch/main/index.html"
status=$(curl -sS -o /tmp/p01_index.html -w "%{http_code}" "${index_url}")
if [ "$status" != "200" ] && [ -n "$TOKEN" ]; then
warn "raw URL returned ${status} unauth; retrying with Authorization header"
status=$(curl -sS -o /tmp/p01_index.html -w "%{http_code}" \
-H "Authorization: token ${TOKEN}" "${index_url}")
fi
if [ "$status" = "200" ]; then
body_size=$(wc -c < /tmp/p01_index.html)
if grep -q "ACDL Evidence" /tmp/p01_index.html; then
pass "raw index.html returns 200 with placeholder body (${body_size} bytes)"
else
fail "raw index.html returns 200 but body does not contain 'ACDL Evidence' marker"
fi
else
fail "GET ${index_url} returned HTTP ${status}"
fi
# --- Check 4: qa + prod branches exist on acdl-contracts ---
echo "-- Check 4: qa + prod branches exist on acdl-contracts --"
status=$(curl -sS -o /tmp/p01_branches.json -w "%{http_code}" \
-H "Authorization: token ${TOKEN}" \
"${GITEA_HOST}/api/v1/repos/${ORG}/acdl-contracts/branches?limit=50")
if [ "$status" != "200" ]; then
fail "list branches on acdl-contracts returned HTTP ${status}"
else
for want in qa prod; do
if python3 -c "
import json, sys
branches = json.load(open('/tmp/p01_branches.json'))
names = [b.get('name', '') for b in branches]
sys.exit(0 if '${want}' in names else 1)
"; then
pass "branch '${want}' exists on acdl-contracts"
else
fail "branch '${want}' missing on acdl-contracts"
fi
done
fi
echo
echo "== Summary =="
if [ "$fail_count" -eq 0 ]; then
echo "Phase 01 verification PASSED (all checks ok)"
exit 0
else
echo "Phase 01 verification FAILED (${fail_count} check(s) failed)"
exit 1
fi