diff --git a/docs/presentations/README.md b/docs/presentations/README.md index 658c30f..9b18fed 100644 --- a/docs/presentations/README.md +++ b/docs/presentations/README.md @@ -115,9 +115,9 @@ Distill the source of truth (Step 1) into presenter-ready cues, indexed by the Marp deck (Step 2) slide structure: - **One section per Marp slide** — `## Slide N — Title`, matching the Marp - deck's 10-slide structure exactly. The Marp deck provides the indexing and - context (what the audience sees); the source markdown provides the content - (the speaker notes, the detail, the nuance). + deck's 10 main + 6 appendix slide structure exactly. The Marp deck + provides the indexing and context (what the audience sees); the source + markdown provides the content (the speaker notes, the detail, the nuance). - **3-6 talking point bullets per slide** — punchy, actionable cues distilled from the source markdown's speaker notes. NOT the speaker notes verbatim (those are too long and too contextual). These are prompts: "Land this @@ -145,31 +145,57 @@ and re-distill. docs/presentations/ ├── README.md ← this file ├── how-the-platform-works.md ← Step 1: full source of truth -├── how-the-platform-works-marp.md ← Step 2: Marp deck (10 slides) +├── how-the-platform-works-marp.md ← Step 2: Marp deck (10 main + 6 appendix) ├── how-the-platform-works.html ← Step 3: rendered HTML (committed) -├── how-the-platform-works-talking-points.md ← Step 4: presenter cues (10 sections) +├── how-the-platform-works-talking-points.md ← Step 4: presenter cues (16 sections) ├── the-developer-experience.md ← Step 1: full source of truth -├── the-developer-experience-marp.md ← Step 2: Marp deck (10 slides) +├── the-developer-experience-marp.md ← Step 2: Marp deck (10 main + 6 appendix) ├── the-developer-experience.html ← Step 3: rendered HTML (committed) -├── the-developer-experience-talking-points.md ← Step 4: presenter cues (10 sections) +├── the-developer-experience-talking-points.md ← Step 4: presenter cues (16 sections) └── assets/ ├── puppeteer-config.json ← no-sandbox config for mmdc ├── mmd/ ← mermaid source files (Step 2 input) │ ├── platform-works-01-contract-driven.mmd │ ├── platform-works-02-end-to-end-flow.mmd - │ ├── developer-experience-01-two-surfaces.mmd + │ ├── platform-works-03-scope-boundary.mmd + │ ├── platform-works-04-confidence-signal.mmd + │ ├── platform-works-05-attestation-flow.mmd + │ ├── developer-experience-01b-scope-boundary.mmd │ ├── developer-experience-02-what-dev-does.mmd - │ └── developer-experience-03-no-cloning.mmd + │ ├── developer-experience-03-no-cloning.mmd + │ ├── developer-experience-04-promotion-journey.mmd + │ └── road-to-north-star.mmd └── png/ ← rendered PNGs (embedded in Marp) ├── platform-works-01-contract-driven.png ├── platform-works-02-end-to-end-flow.png - ├── developer-experience-01-two-surfaces.png + ├── platform-works-03-scope-boundary.png + ├── platform-works-04-confidence-signal.png + ├── platform-works-05-attestation-flow.png + ├── developer-experience-01b-scope-boundary.png ├── developer-experience-02-what-dev-does.png - └── developer-experience-03-no-cloning.png + ├── developer-experience-03-no-cloning.png + ├── developer-experience-04-promotion-journey.png + └── road-to-north-star.png ``` ## Conventions +### Appendix structure + +Each Marp deck has **10 main slides + 6 appendix slides** (16 total). The +main 10 are the presentation; the appendix is for deep dives and Q&A backup. + +- **Main slides** (1-10): the story arc, high-impact, minimal text, + visual-heavy. These are what the audience sees during the talk. +- **Appendix slides** (A1-A5 + TOC): detail-heavy slides moved out of the + main 10 to preserve the narrative flow. The appendix starts with a TOC + slide listing the contents, followed by detail slides and a glossary. +- **The Road to the North Star** is a required appendix slide in both decks + — a phased timeline from v1.0 demo to the North Star, annotated as + "proposed phasing, not formally planned." +- **The Glossary** is a required appendix slide in both decks — defines + acronyms (OIDC, ABAC, CMK, CMDB, RPO, HITL, VCS, NFR) for the audience. + ### Maturity framing Every capability claim in a deck is tagged with one of three badges: @@ -296,7 +322,7 @@ attachments to the Gitea release. ## Current decks -| Deck | Source of truth (Step 1) | Marp deck (Step 2) | Rendered HTML (Step 3) | Talking points (Step 4) | Audience | -|---|---|---|---|---|---| -| How the Platform Works | `how-the-platform-works.md` | `how-the-platform-works-marp.md` | `how-the-platform-works.html` | `how-the-platform-works-talking-points.md` | CTO, Head of Cloud, Head of Infra, Head of DevOps | -| The Developer Experience | `the-developer-experience.md` | `the-developer-experience-marp.md` | `the-developer-experience.html` | `the-developer-experience-talking-points.md` | CTO, Head of Cloud, Head of Infra, Head of DevOps | \ No newline at end of file +| Deck | Source of truth (Step 1) | Marp deck (Step 2) | Rendered HTML (Step 3) | Talking points (Step 4) | Slides | Audience | +|---|---|---|---|---|---|---| +| How the Platform Works | `how-the-platform-works.md` | `how-the-platform-works-marp.md` | `how-the-platform-works.html` | `how-the-platform-works-talking-points.md` | 10 main + 6 appendix | CTO, Head of Cloud, Head of Infra, Head of DevOps | +| The Developer Experience | `the-developer-experience.md` | `the-developer-experience-marp.md` | `the-developer-experience.html` | `the-developer-experience-talking-points.md` | 10 main + 6 appendix | CTO, Head of Cloud, Head of Infra, Head of DevOps | \ No newline at end of file diff --git a/docs/presentations/assets/mmd/developer-experience-01-two-surfaces.mmd b/docs/presentations/assets/mmd/developer-experience-01-two-surfaces.mmd deleted file mode 100644 index c82745a..0000000 --- a/docs/presentations/assets/mmd/developer-experience-01-two-surfaces.mmd +++ /dev/null @@ -1,7 +0,0 @@ -flowchart LR - A["Technical developer"] --> C["Contract YAML"] - B["Citizen developer
(non-technical)"] --> D["Declares intent
in natural language"] - D --> E["Agent produces
the contract"] - C --> F["Same platform:
resolve → check → plan →
policy → confidence → apply"] - E --> F - F --> G["Same safety guarantees,
same audit trail"] \ No newline at end of file diff --git a/docs/presentations/assets/mmd/developer-experience-01b-scope-boundary.mmd b/docs/presentations/assets/mmd/developer-experience-01b-scope-boundary.mmd new file mode 100644 index 0000000..f11f8e5 --- /dev/null +++ b/docs/presentations/assets/mmd/developer-experience-01b-scope-boundary.mmd @@ -0,0 +1,21 @@ +flowchart LR + subgraph UP ["Upstream — anything"] + direction TB + A["Technical dev\n(app code + contract)"] + B["Citizen dev\n(intent → AI agent\n→ contract)"] + end + subgraph ACDL ["ACDL — infrastructure only"] + C["Same contract\nSame pipeline\nSame safety"] + D["Provision\nAWS resources"] + E["Evidence\nhash-chained"] + end + subgraph DOWN ["Downstream"] + F["AWS resources\nrunning"] + G["Consumer pipeline\ndeploys image"] + end + A --> C + B --> C + C --> D + C --> E + D --> F + F --> G \ No newline at end of file diff --git a/docs/presentations/assets/mmd/developer-experience-04-promotion-journey.mmd b/docs/presentations/assets/mmd/developer-experience-04-promotion-journey.mmd new file mode 100644 index 0000000..4981816 --- /dev/null +++ b/docs/presentations/assets/mmd/developer-experience-04-promotion-journey.mmd @@ -0,0 +1,8 @@ +flowchart LR + A["dev\n≥ 0.50\nautonomous"] -->|promotion| B["qa\n≥ 0.75\nQA attests"] + B -->|promotion| C["prod\n≥ 0.90\nSRE attests"] + C -->|promotion| D["dr\n≥ 0.95\nSRE + DR drill"] + A -.->|"Testing\n(pilot-ready)"| A + B -.->|"Planned"| B + C -.->|"Planned"| C + D -.->|"Planned"| D \ No newline at end of file diff --git a/docs/presentations/assets/mmd/platform-works-03-scope-boundary.mmd b/docs/presentations/assets/mmd/platform-works-03-scope-boundary.mmd new file mode 100644 index 0000000..e4fa375 --- /dev/null +++ b/docs/presentations/assets/mmd/platform-works-03-scope-boundary.mmd @@ -0,0 +1,25 @@ +flowchart LR + subgraph UP ["Upstream — anything"] + direction TB + A["IDE / IDE + AI\n(dev writes contract)"] + B["Agentic SDLC\n(agent writes contract)"] + C["Citizen dev\n(vibe codes → AI agent\n→ contract)"] + end + subgraph ACDL ["ACDL — infrastructure only"] + D["Contract\nvalidated"] + E["Resolve → Plan\nSecurity + Policy checks\nConfidence signal"] + F["Provision\nAWS resources"] + G["Evidence\nhash-chained"] + end + subgraph DOWN ["Downstream"] + H["AWS resources\nrunning"] + I["Consumer pipeline\ndeploys image"] + end + A --> D + B --> D + C --> D + D --> E + E --> F + E --> G + F --> H + H --> I \ No newline at end of file diff --git a/docs/presentations/assets/mmd/platform-works-04-confidence-signal.mmd b/docs/presentations/assets/mmd/platform-works-04-confidence-signal.mmd new file mode 100644 index 0000000..8a9d8b2 --- /dev/null +++ b/docs/presentations/assets/mmd/platform-works-04-confidence-signal.mmd @@ -0,0 +1,15 @@ +flowchart LR + subgraph IN ["6 weighted inputs"] + direction TB + A["Policy\nconformance"] + B["Validation"] + C["Freshness"] + D["Source\nprovenance"] + E["History"] + F["NFRs"] + end + IN --> G["Weighted sum\n→ Confidence score"] + G --> H{"Threshold\ngate"} + H -->|Meets threshold| I["Proceed"] + H -->|Below threshold| J["Halt +\nexplainable reason"] + H -->|Critical finding| J \ No newline at end of file diff --git a/docs/presentations/assets/mmd/platform-works-05-attestation-flow.mmd b/docs/presentations/assets/mmd/platform-works-05-attestation-flow.mmd new file mode 100644 index 0000000..446c750 --- /dev/null +++ b/docs/presentations/assets/mmd/platform-works-05-attestation-flow.mmd @@ -0,0 +1,13 @@ +flowchart LR + A["Deployment arrives\nat env gate"] --> B["Confidence signal\ncomputed"] + B --> C{"Meets\nthreshold?"} + C -->|No / Critical| D["Halt —\nexplainable reason"] + C -->|Yes| E{"Human attestation\nrequired?"} + E -->|No — dev| F["Autonomous\nproceed"] + E -->|Yes — qa/prod/dr| G["Approver reviews:\ncontract + plan + evidence"] + G --> H{"Approver\ndecides"} + H -->|Approve| I["Attestation recorded\n(identity + state)"] + H -->|Reject| J["Halt — rejection\nextends audit chain"] + I --> K["Deployment\nproceeds"] + F --> K + K --> L["Evidence written\nRPO=0"] \ No newline at end of file diff --git a/docs/presentations/assets/mmd/road-to-north-star.mmd b/docs/presentations/assets/mmd/road-to-north-star.mmd new file mode 100644 index 0000000..b2faead --- /dev/null +++ b/docs/presentations/assets/mmd/road-to-north-star.mmd @@ -0,0 +1,11 @@ +flowchart LR + A["v1.0\nDEMO\ncomplete"] --> B["v1.1–v1.8\nPLATFORM BUILD\ncomplete"] + B --> C["v1.9\nPRESENTATIONS + PATCHES\ncomplete"] + C --> D["v1.10\nNEXT\nHITL wiring\nall-runner OIDC\nregulatory ledger"] + D --> E["v2.0\nFUTURE\ncompliance milestone\nself-service\ndynamic modules\nengine adapters"] + E --> F["North Star\nREALIZED\nfull autonomy (lower)\nattested (higher)\ncitizen dev live\nevidence regulatory-grade"] + A -.->|"stub-driven proof"| A + B -.->|"IR + OIDC + ABAC +\nmodule catalog +\nencryption + decommission"| B + C -.->|"10-slide decks +\ntalking points +\nS&P theme"| C + D -.->|"proposed phasing\nnot formally planned"| D + E -.->|"proposed phasing\nnot formally planned"| E \ No newline at end of file diff --git a/docs/presentations/assets/png/developer-experience-01-two-surfaces.png b/docs/presentations/assets/png/developer-experience-01-two-surfaces.png deleted file mode 100644 index 58c4eeb..0000000 Binary files a/docs/presentations/assets/png/developer-experience-01-two-surfaces.png and /dev/null differ diff --git a/docs/presentations/assets/png/developer-experience-01b-scope-boundary.png b/docs/presentations/assets/png/developer-experience-01b-scope-boundary.png new file mode 100644 index 0000000..ca26e43 Binary files /dev/null and b/docs/presentations/assets/png/developer-experience-01b-scope-boundary.png differ diff --git a/docs/presentations/assets/png/developer-experience-04-promotion-journey.png b/docs/presentations/assets/png/developer-experience-04-promotion-journey.png new file mode 100644 index 0000000..152b2f9 Binary files /dev/null and b/docs/presentations/assets/png/developer-experience-04-promotion-journey.png differ diff --git a/docs/presentations/assets/png/platform-works-03-scope-boundary.png b/docs/presentations/assets/png/platform-works-03-scope-boundary.png new file mode 100644 index 0000000..218fe6d Binary files /dev/null and b/docs/presentations/assets/png/platform-works-03-scope-boundary.png differ diff --git a/docs/presentations/assets/png/platform-works-04-confidence-signal.png b/docs/presentations/assets/png/platform-works-04-confidence-signal.png new file mode 100644 index 0000000..56832ba Binary files /dev/null and b/docs/presentations/assets/png/platform-works-04-confidence-signal.png differ diff --git a/docs/presentations/assets/png/platform-works-05-attestation-flow.png b/docs/presentations/assets/png/platform-works-05-attestation-flow.png new file mode 100644 index 0000000..15d210f Binary files /dev/null and b/docs/presentations/assets/png/platform-works-05-attestation-flow.png differ diff --git a/docs/presentations/assets/png/road-to-north-star.png b/docs/presentations/assets/png/road-to-north-star.png new file mode 100644 index 0000000..e143424 Binary files /dev/null and b/docs/presentations/assets/png/road-to-north-star.png differ diff --git a/docs/presentations/how-the-platform-works-marp.md b/docs/presentations/how-the-platform-works-marp.md index be73e79..9844538 100644 --- a/docs/presentations/how-the-platform-works-marp.md +++ b/docs/presentations/how-the-platform-works-marp.md @@ -18,7 +18,8 @@ style: | table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } - img { display: block; margin: 0 auto; max-height: 320px; } + img { display: block; margin: 0 auto; max-height: 300px; } + em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -44,6 +45,8 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top # The Problem & The North Star +Story beat: Here's the problem we're solving and where we're going. + Four frictions slow every team: - **Cognitive load** — authoring infrastructure correctly; the long tail of services inconsistent in security and observability @@ -56,6 +59,17 @@ Four frictions slow every team: - A merged change progresses **without a platform engineer joining a thread or approving a ticket** - A **non-technical consumer** ships by declaring intent — no workflow, no config file, no infrastructure module - Every production change is **traceable to a human attestation and an immutable evidence stream** + +--- + +# Where ACDL Sits in Your World + +Story beat: Now that we know the problem, here's where ACDL fits — and where it doesn't. + +![w:1100](assets/png/platform-works-03-scope-boundary.png) + +- **Upstream is anything** — your IDE, an agentic SDLC, or a citizen developer vibe coding on a laptop. ACDL doesn't care how the contract was produced. +- **ACDL is infrastructure only** — it provisions and governs AWS resources. It does not build, test, or deploy your application code. That's upstream. - **Not a general-purpose AI** — autonomy is narrow, scoped to delivery, bounded by strict policy - **Not a permissive delivery highway** — no escape hatches to bypass the confidence framework @@ -63,19 +77,23 @@ Four frictions slow every team: # The Contract-Driven Model -A single YAML contract is all a consumer writes — **module, environment, inputs**. The platform owns everything else. +Story beat: The contract is the boundary between upstream and ACDL. It's all a consumer writes. + +A single YAML contract — **module, environment, inputs**. The platform owns everything else. ![w:850](assets/png/platform-works-01-contract-driven.png) - **Which module** — a catalog of pre-built, security-reviewed building blocks - **Which environment** — the platform raises the safety bar automatically as sensitivity rises -- **Which inputs** — the handful of values that vary per deployment -- The consumer provides **no AWS account, no VPC, no state backend, no runner key** — the platform owns the blast radius +- **Which inputs** — infrastructure values that vary per deployment (cpu, memory, port, desired_count) +- The consumer provides **no AWS account, no VPC, no state backend** — the platform owns the blast radius --- # The End-to-End Flow +Story beat: Once the contract is written, here's what the platform does with it — every time. + Every deployment runs the same stages, in the same order, with the same checks — no team-specific pipelines, no tribal runbooks. ![w:1100](assets/png/platform-works-02-end-to-end-flow.png) @@ -87,6 +105,8 @@ Every deployment runs the same stages, in the same order, with the same checks # Zero-Trust by Default +Story beat: Before any infrastructure is created, here's how access is scoped. + Consumer repositories hold **no long-lived cloud credentials.** Ever. - **Authentication — OIDC federation.** Each job mints a short-lived token; no credential is stored in the consumer repo or in a runner secret. Testing (GitHub Actions) Planned: all runners @@ -100,25 +120,29 @@ Consumer repositories hold **no long-lived cloud credentials.** Ever. # Safety is Computed, Not Assumed -Every delivery action produces a **measurable, explainable confidence signal** — the platform's certified answer to *"is this safe to proceed?"* Agentic +Story beat: Now let's look at how the platform decides whether a deployment is safe. -- **Six weighted inputs:** policy conformance, validation, freshness, source provenance, history, NFRs +Every delivery action produces a **measurable, explainable confidence signal** — a weighted sum of observable facts, not a black box. Agentic + +![w:1100](assets/png/platform-works-04-confidence-signal.png) + +- **Six weighted inputs** — manually tuned, auditable. If a consumer asks "why 0.62?", the platform answers with a per-input breakdown - **Per-environment thresholds** that rise with sensitivity: | Environment | Threshold | Attester | |---|---|---| -| dev | ≥ 0.50 | No one — autonomous | -| qa | ≥ 0.75 | QA | -| prod | ≥ 0.90 | SRE | -| dr | ≥ 0.95 | SRE + DR drill | +| dev | ≥ 0.50 | No one — autonomous Testing | +| qa | ≥ 0.75 | QA Planned | +| prod | ≥ 0.90 | SRE Planned | -- **A single critical finding hard-blocks the deployment** — critical findings are not averaged away -- **When the platform halts, it gives a measured reason** — never an opaque debugging exercise +- **A single critical finding hard-blocks** — critical findings are not averaged away --- # Security by Construction +Story beat: Beyond the confidence signal, security defaults are on by construction — not by opt-in. + Security defaults that **do not require a team to opt in.** Checks run on **every** deployment, normalized to a single schema. Testing - **Policy checks** (Checkov, Wiz, Kyverno) — secrets in plaintext, public ingress, IAM wildcards, **required tagging standards** — all run *before* infra is created @@ -130,13 +154,15 @@ Security defaults that **do not require a team to opt in.** Checks run on **ever # Accountability & Audit -Autonomy and accountability are **not in tension** — they apply at different environments. +Story beat: Computed safety handles the gate. But humans still matter — here's how accountability works. -- **Dev is fully autonomous.** The confidence signal (≥ 0.50) is the only gate. Queue-based handoffs are eliminated. Agentic -- **qa, prod, dr require deliberate human attestation** — policy-mandated acts of accountability, not rubber stamps -- **Separation of duties is enforced** — the QA approver **cannot** be the prod approver. The platform **blocks on a match.** Design tested Wiring: planned -- **Every deployment writes a hash-chained evidence event** — tampering breaks the chain. **RPO = 0** — the evidence write is synchronous Testing -- **Every production change is traceable to a human attestation** — the only durable record outside the VCS's audit log +![w:1100](assets/png/platform-works-05-attestation-flow.png) + +- **Dev is fully autonomous.** The confidence signal (≥ 0.50) is the only gate. Testing Agentic +- **qa, prod, dr require human attestation** — the approver reviews the contract, the planned Terraform changes, and the accumulated evidence Planned +- **QA attests to infrastructure readiness, not application code** — the contract, the plan, and the evidence. Application code review is upstream +- **Separation of duties** — the QA approver **cannot** be the prod approver. The platform **blocks on a match.** Planned +- **Every deployment writes a hash-chained evidence event** — tampering breaks the chain. **RPO = 0** Testing --- @@ -145,9 +171,112 @@ Autonomy and accountability are **not in tension** — they apply at different e # Testing vs. Planned +Story beat: Let's be honest about what works today and what's on the roadmap. + + +**11 capabilities testing today** (dev pilot-ready): + +- Contract-driven deploys · Module catalog · Zero-trust OIDC + ABAC +- Security + policy checks before infra creation · Confidence signal gating +- Hash-chained evidence outbox (RPO = 0) · Encryption by default + per-stack CMKs +- Deletion protection + safe decommission · Uptime monitoring +- Platform-managed environments · Engine-agnostic core + VCS-agnostic ingestion + +**9 planned** (production path): + +- HITL wiring for qa/prod/dr · All-runner OIDC · Full regulatory ledger +- Compliance milestone (GDPR, SOX, SOC2, DORA) · Environment self-service +- Dynamic module creation Agentic · Pattern recognition Agentic +- Additional engine adapters · Deeper observability bootstrap + +*Full inventory + phased roadmap in the appendix.* + +--- + + + + +# The Vision Realized + +Story beat: Here's what success looks like when the North Star is reached. + +- **Velocity without sacrificing safety.** Speed is in the ergonomics (a simple contract, a one-line `uses:`); safety is in the gates the consumer cannot bypass. +- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation. +- **Auditability as a byproduct, not a project.** Every production change is traceable to a human attestation and a tamper-evident evidence event. +- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources. +- **Infrastructure as a utility, not a craft.** Teams consume infrastructure, they don't maintain it. +- **A path to the citizen developer.** The same safety envelope that serves a senior engineer will serve a non-technical consumer. Agentic + +--- + + + + +# Appendix + +For deep dives — these slides cover details omitted from the main 10. + +**Contents:** + +1. Platform-Managed Environments (detail) +2. Observability Built In (detail) +3. The Road to the North Star (phased roadmap) +4. Testing vs. Planned (full inventory) +5. Glossary + +--- + +# A1 — Platform-Managed Environments + +A consumer provides **no AWS account, no VPC, no subnet, no state backend, no runner key.** The platform owns the blast radius. + +A named environment is a platform-owned bundle of: + +- An AWS account (or a scoped partition of one) +- A network (VPC + subnets) +- A state backend (S3 + DynamoDB for state + locking) +- An IAM role surfaced via ABAC, scoped to the consumer's identity and resource tags + +The consumer selects an environment **by name** in their contract. The platform resolves the name to the underlying resources at run time. **The consumer never sees raw credentials.** + +**Friendly onboarding:** the first run detects no environment and emits a guided prompt (not an opaque failure). Testing Self-service: planned + +--- + +# A2 — Observability Built In + +Monitoring is **a platform default, not a per-team project.** Testing + +- **Uptime monitoring deployed automatically with every stack** — a dedicated monitoring instance is provisioned after any module deploy, in a separate state, with a feature flag to disable +- **Monitored endpoints passed from the deployment's own outputs** — no manual endpoint registration +- **Alert channels:** Microsoft Teams webhook, email, SMS, and GitHub issues +- **The uptime URL is published to the developer** via a PR comment — they don't hunt for it +- **Roadmap:** deeper observability bootstrap (dashboards, runbooks, on-call bindings) as first-class contract fields Planned + +--- + + + + +# A3 — The Road to the North Star + +*Proposed phasing — not formally planned.* + +![w:1100](assets/png/road-to-north-star.png) + +--- + + + + +# A4 — Testing vs. Planned (Full Inventory) + + @@ -156,7 +285,7 @@ li { margin-bottom: 2px; } -**Testing** (works internally, not yet released to consumers) +**Testing** (works internally, dev pilot-ready) - Contract-driven deploys with a versioned reusable workflow - Module catalog (primitives + modules) with validated examples @@ -191,14 +320,16 @@ li { margin-bottom: 2px; } --- - - +# A5 — Glossary -# The Vision Realized - -- **Velocity without sacrificing safety.** Speed is in the ergonomics (a simple contract, a one-line `uses:`); safety is in the gates the consumer cannot bypass. -- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation. Encryption, deletion protection, uptime monitoring, policy checks, and evidence are on by construction. -- **Auditability as a byproduct, not a project.** Every production change is traceable to a human attestation and a tamper-evident evidence event. -- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources. -- **Infrastructure as a utility, not a craft.** The platform abstracts compute, networking, and state. Teams consume infrastructure, they don't maintain it. -- **A path to the citizen developer.** The same safety envelope that serves a senior engineer will serve a non-technical consumer — expanding who can ship safely without lowering the bar. Agentic \ No newline at end of file +| Term | Meaning | +|---|---| +| **OIDC** | OpenID Connect — federation protocol for short-lived tokens, no long-lived credentials | +| **ABAC** | Attribute-Based Access Control — access scoped by resource tags + repo identity, not roles | +| **CMK** | Customer-Managed Key — per-stack encryption key, 90-day rotation, no shared keys | +| **CMDB** | Configuration Management Database — validates change requests for decommission | +| **RPO** | Recovery Point Objective — RPO = 0 means evidence is written synchronously, no data loss | +| **HITL** | Human-in-the-Loop — deliberate human attestation required for qa/prod/dr environments | +| **VCS** | Version Control System — the git hosting platform (GitHub, Gitea, GitLab) | +| **NFR** | Non-Functional Requirement — encryption, tagging, observability standards | +| **IR** | Intermediate Representation — the engine-agnostic stack definition between contract and Terraform | \ No newline at end of file diff --git a/docs/presentations/how-the-platform-works.html b/docs/presentations/how-the-platform-works.html index 9a3c21f..60ceed4 100644 --- a/docs/presentations/how-the-platform-works.html +++ b/docs/presentations/how-the-platform-works.html @@ -7,7 +7,7 @@ * @auto-scaling true * @size 16:9 1280px 720px * @size 4:3 960px 720px - */div#\:\$p > svg > foreignObject > section [data-theme=light],div#\:\$p > svg > foreignObject > section{color-scheme:light}div#\:\$p > svg > foreignObject > section [data-theme=dark],div#\:\$p > svg > foreignObject > section:where(.invert){color-scheme:dark}div#\:\$p > svg > foreignObject > section{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;font-weight:var(--base-text-weight-normal, 400);color:var(--fgColor-default);background-color:var(--bgColor-default);font-family:var(--fontStack-sansSerif, -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji");font-size:16px;line-height:1.5;word-wrap:break-word}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size:16px}div#\:\$p > svg > foreignObject > section a{text-decoration:underline;text-underline-offset:calc(var(--marpit-root-font-size, 1rem) * .2)}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6):hover .anchor .octicon-link:before{width:16px;height:16px;content:" ";display:inline-block;background-color:currentColor;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,')}div#\:\$p > svg > foreignObject > section details,div#\:\$p > svg > foreignObject > section figcaption,div#\:\$p > svg > foreignObject > section figure{display:block}div#\:\$p > svg > foreignObject > section summary{display:list-item}div#\:\$p > svg > foreignObject > section [hidden]{display:none!important}div#\:\$p > svg > foreignObject > section a{background-color:transparent;color:var(--fgColor-accent);text-decoration:none}div#\:\$p > svg > foreignObject > section abbr[title]{border-bottom:none;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}div#\:\$p > svg > foreignObject > section b,div#\:\$p > svg > foreignObject > section strong{font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section dfn{font-style:italic}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1){margin:.67em 0;font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:2em;border-bottom:1px solid var(--borderColor-muted)}div#\:\$p > svg > foreignObject > section mark{background-color:var(--bgColor-attention-muted);color:var(--fgColor-default)}div#\:\$p > svg > foreignObject > section small{font-size:90%}div#\:\$p > svg > foreignObject > section sub,div#\:\$p > svg > foreignObject > section sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}div#\:\$p > svg > foreignObject > section sub{bottom:-.25em}div#\:\$p > svg > foreignObject > section sup{top:-.5em}div#\:\$p > svg > foreignObject > section img{border-style:none;max-width:100%;box-sizing:content-box}div#\:\$p > svg > foreignObject > section code,div#\:\$p > svg > foreignObject > section kbd,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre),div#\:\$p > svg > foreignObject > section samp{font-family:monospace;font-size:1em}div#\:\$p > svg > foreignObject > section figure{margin:1em var(--base-size-40)}div#\:\$p > svg > foreignObject > section hr{box-sizing:content-box;overflow:hidden;background:transparent;height:.25em;padding:0;margin:var(--base-size-24) 0;background-color:var(--borderColor-default);border:0}div#\:\$p > svg > foreignObject > section input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}div#\:\$p > svg > foreignObject > section [type=button],div#\:\$p > svg > foreignObject > section [type=reset],div#\:\$p > svg > foreignObject > section [type=submit]{-webkit-appearance:button;-moz-appearance:button;appearance:button}div#\:\$p > svg > foreignObject > section [type=checkbox],div#\:\$p > svg > foreignObject > section [type=radio]{box-sizing:border-box;padding:0}div#\:\$p > svg > foreignObject > section [type=number]::-webkit-inner-spin-button,div#\:\$p > svg > foreignObject > section [type=number]::-webkit-outer-spin-button{height:auto}div#\:\$p > svg > foreignObject > section [type=search]::-webkit-search-cancel-button,div#\:\$p > svg > foreignObject > section [type=search]::-webkit-search-decoration{-webkit-appearance:none;appearance:none}div#\:\$p > svg > foreignObject > section ::-webkit-input-placeholder{color:inherit;opacity:.54}div#\:\$p > svg > foreignObject > section ::-webkit-file-upload-button{-webkit-appearance:button;appearance:button;font:inherit}div#\:\$p > svg > foreignObject > section a:hover{text-decoration:underline}div#\:\$p > svg > foreignObject > section ::-moz-placeholder{color:var(--fgColor-muted);opacity:1}div#\:\$p > svg > foreignObject > section ::placeholder{color:var(--fgColor-muted);opacity:1}div#\:\$p > svg > foreignObject > section hr:after,div#\:\$p > svg > foreignObject > section hr:before{display:table;content:""}div#\:\$p > svg > foreignObject > section hr:after{clear:both}div#\:\$p > svg > foreignObject > section table{border-spacing:0;border-collapse:collapse;display:block;width:-moz-max-content;width:max-content;max-width:100%;overflow:auto;font-variant:tabular-nums}div#\:\$p > svg > foreignObject > section td,div#\:\$p > svg > foreignObject > section th{padding:0}div#\:\$p > svg > foreignObject > section details summary{cursor:pointer}div#\:\$p > svg > foreignObject > section [role=button]:focus,div#\:\$p > svg > foreignObject > section a:focus,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus,div#\:\$p > svg > foreignObject > section input[type=radio]:focus{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}div#\:\$p > svg > foreignObject > section [role=button]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section a:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section input[type=radio]:focus:not(:focus-visible){outline:1px solid transparent}div#\:\$p > svg > foreignObject > section [role=button]:focus-visible,div#\:\$p > svg > foreignObject > section a:focus-visible,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus-visible,div#\:\$p > svg > foreignObject > section input[type=radio]:focus-visible{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}div#\:\$p > svg > foreignObject > section a:not([class]):focus,div#\:\$p > svg > foreignObject > section a:not([class]):focus-visible,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus-visible,div#\:\$p > svg > foreignObject > section input[type=radio]:focus,div#\:\$p > svg > foreignObject > section input[type=radio]:focus-visible{outline-offset:0}div#\:\$p > svg > foreignObject > section kbd{display:inline-block;padding:var(--base-size-4);font:11px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);line-height:10px;color:var(--fgColor-default);vertical-align:middle;background-color:var(--bgColor-muted);border-bottom-color:var(--borderColor-neutral-muted);border:1px solid var(--borderColor-neutral-muted);border-radius:6px;box-shadow:inset 0 -1px 0 var(--borderColor-neutral-muted)}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section :is(h3, marp-h3),div#\:\$p > svg > foreignObject > section :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section :is(h5, marp-h5),div#\:\$p > svg > foreignObject > section :is(h6, marp-h6){margin-top:var(--base-size-24);margin-bottom:var(--base-size-16);font-weight:var(--base-text-weight-semibold, 600);line-height:1.25}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--borderColor-muted)}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section :is(h3, marp-h3){font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section :is(h3, marp-h3){font-size:1.25em}div#\:\$p > svg > foreignObject > section :is(h4, marp-h4){font-size:1em}div#\:\$p > svg > foreignObject > section :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section :is(h5, marp-h5){font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section :is(h5, marp-h5){font-size:.875em}div#\:\$p > svg > foreignObject > section :is(h6, marp-h6){font-weight:var(--base-text-weight-semibold, 600);font-size:.85em;color:var(--fgColor-muted)}div#\:\$p > svg > foreignObject > section p{margin-top:0;margin-bottom:10px}div#\:\$p > svg > foreignObject > section blockquote{margin:0;padding:0 1em;color:var(--fgColor-muted);border-left:.25em solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section ol,div#\:\$p > svg > foreignObject > section ul{margin-top:0;margin-bottom:0;padding-left:2em}div#\:\$p > svg > foreignObject > section ol ol,div#\:\$p > svg > foreignObject > section ul ol{list-style-type:lower-roman}div#\:\$p > svg > foreignObject > section ol ol ol,div#\:\$p > svg > foreignObject > section ol ul ol,div#\:\$p > svg > foreignObject > section ul ol ol,div#\:\$p > svg > foreignObject > section ul ul ol{list-style-type:lower-alpha}div#\:\$p > svg > foreignObject > section dd{margin-left:0}div#\:\$p > svg > foreignObject > section code,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre),div#\:\$p > svg > foreignObject > section samp,div#\:\$p > svg > foreignObject > section tt{font-family:var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);font-size:12px}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre){margin-top:0;margin-bottom:0;word-wrap:normal}div#\:\$p > svg > foreignObject > section .octicon{display:inline-block;overflow:visible!important;vertical-align:text-bottom;fill:currentColor}div#\:\$p > svg > foreignObject > section input::-webkit-inner-spin-button,div#\:\$p > svg > foreignObject > section input::-webkit-outer-spin-button{margin:0;-webkit-appearance:none;appearance:none}div#\:\$p > svg > foreignObject > section .mr-2{margin-right:var(--base-size-8, 8px)!important}div#\:\$p > svg > foreignObject > section:after,div#\:\$p > svg > foreignObject > section:before{display:table}div#\:\$p > svg > foreignObject > section:after{clear:both}div#\:\$p > svg > foreignObject > section>:first-child{margin-top:0!important}div#\:\$p > svg > foreignObject > section>:last-child{margin-bottom:0!important}div#\:\$p > svg > foreignObject > section a:not([href]){color:inherit;text-decoration:none}div#\:\$p > svg > foreignObject > section .absent{color:var(--fgColor-danger)}div#\:\$p > svg > foreignObject > section .anchor{float:left;padding-right:var(--base-size-4);margin-left:-20px;line-height:1}div#\:\$p > svg > foreignObject > section .anchor:focus{outline:none}div#\:\$p > svg > foreignObject > section blockquote,div#\:\$p > svg > foreignObject > section details,div#\:\$p > svg > foreignObject > section dl,div#\:\$p > svg > foreignObject > section ol,div#\:\$p > svg > foreignObject > section p,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre),div#\:\$p > svg > foreignObject > section table,div#\:\$p > svg > foreignObject > section ul{margin-top:0;margin-bottom:var(--base-size-16)}div#\:\$p > svg > foreignObject > section blockquote>:first-child{margin-top:0}div#\:\$p > svg > foreignObject > section blockquote>:last-child{margin-bottom:0}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) .octicon-link{color:var(--fgColor-default);vertical-align:middle;visibility:hidden}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6):hover .anchor{text-decoration:none}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6):hover .anchor .octicon-link{visibility:visible}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) code,div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) tt,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) code,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) tt,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) code,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) tt,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) code,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) tt,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) code,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) tt,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) code,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) tt{padding:0 .2em;font-size:inherit}div#\:\$p > svg > foreignObject > section summary :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section summary :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section summary :is(h3, marp-h3),div#\:\$p > svg > foreignObject > section summary :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section summary :is(h5, marp-h5),div#\:\$p > svg > foreignObject > section summary :is(h6, marp-h6){display:inline-block}div#\:\$p > svg > foreignObject > section summary :is(h1, marp-h1) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h2, marp-h2) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h3, marp-h3) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h4, marp-h4) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h5, marp-h5) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h6, marp-h6) .anchor{margin-left:-40px}div#\:\$p > svg > foreignObject > section summary :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section summary :is(h2, marp-h2){padding-bottom:0;border-bottom:0}div#\:\$p > svg > foreignObject > section ol.no-list,div#\:\$p > svg > foreignObject > section ul.no-list{padding:0;list-style-type:none}div#\:\$p > svg > foreignObject > section ol[type="a s"]{list-style-type:lower-alpha}div#\:\$p > svg > foreignObject > section ol[type="A s"]{list-style-type:upper-alpha}div#\:\$p > svg > foreignObject > section ol[type="i s"]{list-style-type:lower-roman}div#\:\$p > svg > foreignObject > section ol[type="I s"]{list-style-type:upper-roman}div#\:\$p > svg > foreignObject > section div>ol:not([type]),div#\:\$p > svg > foreignObject > section ol[type="1"]{list-style-type:decimal}div#\:\$p > svg > foreignObject > section ol ol,div#\:\$p > svg > foreignObject > section ol ul,div#\:\$p > svg > foreignObject > section ul ol,div#\:\$p > svg > foreignObject > section ul ul{margin-top:0;margin-bottom:0}div#\:\$p > svg > foreignObject > section li>p{margin-top:var(--base-size-16)}div#\:\$p > svg > foreignObject > section li+li{margin-top:.25em}div#\:\$p > svg > foreignObject > section dl{padding:0}div#\:\$p > svg > foreignObject > section dl dt{padding:0;margin-top:var(--base-size-16);font-size:1em;font-style:italic;font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section dl dd{padding:0 var(--base-size-16);margin-bottom:var(--base-size-16)}div#\:\$p > svg > foreignObject > section table th{font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section table td,div#\:\$p > svg > foreignObject > section table th{padding:6px 13px;border:1px solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section table td>:last-child{margin-bottom:0}div#\:\$p > svg > foreignObject > section table tr{background-color:var(--bgColor-default);border-top:1px solid var(--borderColor-muted)}div#\:\$p > svg > foreignObject > section table tr:nth-child(2n){background-color:var(--bgColor-muted)}div#\:\$p > svg > foreignObject > section table img{background-color:transparent}div#\:\$p > svg > foreignObject > section img[align=right]{padding-left:20px}div#\:\$p > svg > foreignObject > section img[align=left]{padding-right:20px}div#\:\$p > svg > foreignObject > section .emoji{max-width:none;vertical-align:text-top;background-color:transparent}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame,div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame>:is(span, marp-span){display:block;overflow:hidden}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame>:is(span, marp-span){float:left;width:auto;padding:7px;margin:13px 0 0;border:1px solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame :is(span, marp-span) img{display:block;float:left}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame :is(span, marp-span) :is(span, marp-span){display:block;padding:5px 0 0;clear:both;color:var(--fgColor-default)}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-center{display:block;overflow:hidden;clear:both}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-center>:is(span, marp-span){display:block;margin:13px auto 0;overflow:hidden;text-align:center}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-center :is(span, marp-span) img{margin:0 auto;text-align:center}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-right{display:block;overflow:hidden;clear:both}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-right>:is(span, marp-span){display:block;margin:13px 0 0;overflow:hidden;text-align:right}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-right :is(span, marp-span) img{margin:0;text-align:right}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-left{display:block;float:left;margin-right:13px;overflow:hidden}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-left :is(span, marp-span){margin:13px 0 0}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-right{display:block;float:right;margin-left:13px;overflow:hidden}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-right>:is(span, marp-span){display:block;margin:13px auto 0;overflow:hidden;text-align:right}div#\:\$p > svg > foreignObject > section code,div#\:\$p > svg > foreignObject > section tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--bgColor-neutral-muted);border-radius:6px}div#\:\$p > svg > foreignObject > section code br,div#\:\$p > svg > foreignObject > section tt br{display:none}div#\:\$p > svg > foreignObject > section del code{text-decoration:inherit}div#\:\$p > svg > foreignObject > section samp{font-size:85%}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) code{font-size:100%}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre)>code{padding:0;margin:0;word-break:normal;white-space:pre;background:transparent;border:0}div#\:\$p > svg > foreignObject > section .highlight{margin-bottom:var(--base-size-16)}div#\:\$p > svg > foreignObject > section .highlight :is(pre, marp-pre){margin-bottom:0;word-break:normal}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre){padding:var(--base-size-16);overflow:auto;font-size:85%;line-height:1.45;color:var(--fgColor-default);background-color:var(--bgColor-muted);border-radius:6px}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) code,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) tt{display:inline;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}div#\:\$p > svg > foreignObject > section .csv-data td,div#\:\$p > svg > foreignObject > section .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}div#\:\$p > svg > foreignObject > section .csv-data .blob-num{padding:10px var(--base-size-8) 9px;text-align:right;background:var(--bgColor-default);border:0}div#\:\$p > svg > foreignObject > section .csv-data tr{border-top:0}div#\:\$p > svg > foreignObject > section .csv-data th{font-weight:var(--base-text-weight-semibold, 600);background:var(--bgColor-muted);border-top:0}div#\:\$p > svg > foreignObject > section [data-footnote-ref]:before{content:"["}div#\:\$p > svg > foreignObject > section [data-footnote-ref]:after{content:"]"}div#\:\$p > svg > foreignObject > section .footnotes{font-size:12px;color:var(--fgColor-muted);border-top:1px solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section div#\:\$p > svg > foreignObject > section section.footnotes{--marpit-root-font-size:12px}div#\:\$p > svg > foreignObject > section .footnotes ol,div#\:\$p > svg > foreignObject > section .footnotes ol ul{padding-left:var(--base-size-16)}div#\:\$p > svg > foreignObject > section .footnotes ol ul{display:inline-block;margin-top:var(--base-size-16)}div#\:\$p > svg > foreignObject > section .footnotes li{position:relative}div#\:\$p > svg > foreignObject > section .footnotes li:target:before{position:absolute;top:calc(var(--base-size-8)*-1);right:calc(var(--base-size-8)*-1);bottom:calc(var(--base-size-8)*-1);left:calc(var(--base-size-24)*-1);pointer-events:none;content:"";border:2px solid var(--borderColor-accent-emphasis);border-radius:6px}div#\:\$p > svg > foreignObject > section .footnotes li:target{color:var(--fgColor-default)}div#\:\$p > svg > foreignObject > section .footnotes .data-footnote-backref g-emoji{font-family:monospace}div#\:\$p > svg > foreignObject > section .pl-c{color:var(--color-prettylights-syntax-comment)}div#\:\$p > svg > foreignObject > section .pl-c1,div#\:\$p > svg > foreignObject > section .pl-s .pl-v{color:var(--color-prettylights-syntax-constant)}div#\:\$p > svg > foreignObject > section .pl-e,div#\:\$p > svg > foreignObject > section .pl-en{color:var(--color-prettylights-syntax-entity)}div#\:\$p > svg > foreignObject > section .pl-s .pl-s1,div#\:\$p > svg > foreignObject > section .pl-smi{color:var(--color-prettylights-syntax-storage-modifier-import)}div#\:\$p > svg > foreignObject > section .pl-ent{color:var(--color-prettylights-syntax-entity-tag)}div#\:\$p > svg > foreignObject > section .pl-k{color:var(--color-prettylights-syntax-keyword)}div#\:\$p > svg > foreignObject > section .pl-pds,div#\:\$p > svg > foreignObject > section .pl-s,div#\:\$p > svg > foreignObject > section .pl-s .pl-pse .pl-s1,div#\:\$p > svg > foreignObject > section .pl-sr,div#\:\$p > svg > foreignObject > section .pl-sr .pl-cce,div#\:\$p > svg > foreignObject > section .pl-sr .pl-sra,div#\:\$p > svg > foreignObject > section .pl-sr .pl-sre{color:var(--color-prettylights-syntax-string)}div#\:\$p > svg > foreignObject > section .pl-smw,div#\:\$p > svg > foreignObject > section .pl-v{color:var(--color-prettylights-syntax-variable)}div#\:\$p > svg > foreignObject > section .pl-bu{color:var(--color-prettylights-syntax-brackethighlighter-unmatched)}div#\:\$p > svg > foreignObject > section .pl-ii{color:var(--color-prettylights-syntax-invalid-illegal-text);background-color:var(--color-prettylights-syntax-invalid-illegal-bg)}div#\:\$p > svg > foreignObject > section .pl-c2{color:var(--color-prettylights-syntax-carriage-return-text);background-color:var(--color-prettylights-syntax-carriage-return-bg)}div#\:\$p > svg > foreignObject > section .pl-sr .pl-cce{font-weight:700;color:var(--color-prettylights-syntax-string-regexp)}div#\:\$p > svg > foreignObject > section .pl-ml{color:var(--color-prettylights-syntax-markup-list)}div#\:\$p > svg > foreignObject > section .pl-mh,div#\:\$p > svg > foreignObject > section .pl-mh .pl-en,div#\:\$p > svg > foreignObject > section .pl-ms{font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}div#\:\$p > svg > foreignObject > section .pl-mi{font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}div#\:\$p > svg > foreignObject > section .pl-mb{font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}div#\:\$p > svg > foreignObject > section .pl-md{color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}div#\:\$p > svg > foreignObject > section .pl-mi1{color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}div#\:\$p > svg > foreignObject > section .pl-mc{color:var(--color-prettylights-syntax-markup-changed-text);background-color:var(--color-prettylights-syntax-markup-changed-bg)}div#\:\$p > svg > foreignObject > section .pl-mi2{color:var(--color-prettylights-syntax-markup-ignored-text);background-color:var(--color-prettylights-syntax-markup-ignored-bg)}div#\:\$p > svg > foreignObject > section .pl-mdr{font-weight:700;color:var(--color-prettylights-syntax-meta-diff-range)}div#\:\$p > svg > foreignObject > section .pl-ba{color:var(--color-prettylights-syntax-brackethighlighter-angle)}div#\:\$p > svg > foreignObject > section .pl-sg{color:var(--color-prettylights-syntax-sublimelinter-gutter-mark)}div#\:\$p > svg > foreignObject > section .pl-corl{text-decoration:underline;color:var(--color-prettylights-syntax-constant-other-reference-link)}div#\:\$p > svg > foreignObject > section [role=button]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section [role=tabpanel][tabindex="0"]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section a:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section button:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section summary:focus:not(:focus-visible){outline:none;box-shadow:none}div#\:\$p > svg > foreignObject > section [tabindex="0"]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section details-dialog:focus:not(:focus-visible){outline:none}div#\:\$p > svg > foreignObject > section g-emoji{display:inline-block;min-width:1ch;font-family:Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-size:1em;font-style:normal!important;font-weight:var(--base-text-weight-normal, 400);line-height:1;vertical-align:-.075em}div#\:\$p > svg > foreignObject > section g-emoji img{width:1em;height:1em}div#\:\$p > svg > foreignObject > section a:has(>p,>div,>:is(pre, marp-pre),>blockquote){display:block}div#\:\$p > svg > foreignObject > section a:has(>p,>div,>:is(pre, marp-pre),>blockquote):not(:has(.snippet-clipboard-content,>:is(pre, marp-pre))){width:-moz-fit-content;width:fit-content}div#\:\$p > svg > foreignObject > section a:has(>p,>div,>:is(pre, marp-pre),>blockquote):has(.snippet-clipboard-content,>:is(pre, marp-pre)):focus-visible{outline:2px solid var(--focus-outlineColor);outline-offset:2px}div#\:\$p > svg > foreignObject > section .task-list-item{list-style-type:none}div#\:\$p > svg > foreignObject > section .task-list-item label{font-weight:var(--base-text-weight-normal, 400)}div#\:\$p > svg > foreignObject > section .task-list-item.enabled label{cursor:pointer}div#\:\$p > svg > foreignObject > section .task-list-item+.task-list-item{margin-top:var(--base-size-4)}div#\:\$p > svg > foreignObject > section .task-list-item .handle{display:none}div#\:\$p > svg > foreignObject > section .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}div#\:\$p > svg > foreignObject > section ul:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}div#\:\$p > svg > foreignObject > section ol:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}div#\:\$p > svg > foreignObject > section .contains-task-list:focus-within .task-list-item-convert-container,div#\:\$p > svg > foreignObject > section .contains-task-list:hover .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip-path:none}div#\:\$p > svg > foreignObject > section ::-webkit-calendar-picker-indicator{filter:invert(50%)}div#\:\$p > svg > foreignObject > section .markdown-alert{padding:var(--base-size-8) var(--base-size-16);margin-bottom:var(--base-size-16);color:inherit;border-left:.25em solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section .markdown-alert>:first-child{margin-top:0}div#\:\$p > svg > foreignObject > section .markdown-alert>:last-child{margin-bottom:0}div#\:\$p > svg > foreignObject > section .markdown-alert .markdown-alert-title{display:flex;font-weight:var(--base-text-weight-medium, 500);align-items:center;line-height:1}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-note{border-left-color:var(--borderColor-accent-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-note .markdown-alert-title{color:var(--fgColor-accent)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-important{border-left-color:var(--borderColor-done-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-important .markdown-alert-title{color:var(--fgColor-done)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-warning{border-left-color:var(--borderColor-attention-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-warning .markdown-alert-title{color:var(--fgColor-attention)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-tip{border-left-color:var(--borderColor-success-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-tip .markdown-alert-title{color:var(--fgColor-success)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-caution{border-left-color:var(--borderColor-danger-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-caution .markdown-alert-title{color:var(--fgColor-danger)}div#\:\$p > svg > foreignObject > section>:first-child>.heading-element:first-child{margin-top:0!important}div#\:\$p > svg > foreignObject > section .highlight :is(pre, marp-pre):has(+.zeroclipboard-container){min-height:52px}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1){color:var(--h1-color);font-size:1.6em}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){border-bottom:none}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){font-size:1.3em}div#\:\$p > svg > foreignObject > section :is(h3, marp-h3){font-size:1.1em}div#\:\$p > svg > foreignObject > section :is(h4, marp-h4){font-size:1.05em}div#\:\$p > svg > foreignObject > section :is(h5, marp-h5){font-size:1em}div#\:\$p > svg > foreignObject > section :is(h6, marp-h6){font-size:.9em}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) strong,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) strong,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) strong,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) strong,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) strong,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) strong{font-weight:inherit;color:var(--heading-strong-color)}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h2, marp-h2)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h3, marp-h3)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h4, marp-h4)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h5, marp-h5)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h6, marp-h6)::part(auto-scaling){max-height:563px}div#\:\$p > svg > foreignObject > section hr{height:0;padding-top:.25em}div#\:\$p > svg > foreignObject > section img{background-color:transparent}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre){border:1px solid var(--borderColor-default);line-height:1.15;overflow:visible}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre)::part(auto-scaling){max-height:529px}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs){color:var(--color-prettylights-syntax-storage-modifier-import)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-doctag),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-keyword),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-meta .hljs-keyword),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-template-tag),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-template-variable),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-type),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-variable.language_){color:var(--color-prettylights-syntax-keyword)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title.class_),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title.class_.inherited__),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title.function_){color:var(--color-prettylights-syntax-entity)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-attr),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-attribute),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-literal),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-meta),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-number),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-operator),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-attr),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-class),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-id),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-variable){color:var(--color-prettylights-syntax-constant)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-meta .hljs-string),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-regexp),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-string){color:var(--color-prettylights-syntax-string)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-built_in),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-symbol){color:var(--color-prettylights-syntax-variable)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-code),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-comment),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-formula){color:var(--color-prettylights-syntax-comment)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-name),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-quote),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-pseudo),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-tag){color:var(--color-prettylights-syntax-entity-tag)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-subst){color:var(--color-prettylights-syntax-storage-modifier-import)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-section){font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-bullet){color:var(--color-prettylights-syntax-markup-list)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-emphasis){font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-strong){font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-addition){color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-deletion){color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}div#\:\$p > svg > foreignObject > section footer,div#\:\$p > svg > foreignObject > section header{margin:0;position:absolute;left:30px;color:var(--header-footer-color);font-size:18px}div#\:\$p > svg > foreignObject > section header{top:21px}div#\:\$p > svg > foreignObject > section footer{bottom:21px}div#\:\$p > svg > foreignObject > section{--h1-color:light-dark(#246, #cee7ff);--header-footer-color:light-dark(hsla(0,0%,40%,.75), hsla(0,0%,60%,.75));--heading-strong-color:light-dark(#48c, #7bf);--paginate-color:light-dark(#777, #999);--base-size-4:4px;--base-size-8:8px;--base-size-16:16px;--base-size-24:24px;--base-size-40:40px;display:block;font-size:29px;height:720px;padding:78.5px;place-content:safe center center;width:1280px;flex-flow:column nowrap;align-items:stretch}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size:29px}div#\:\$p > svg > foreignObject > section>:last-child,div#\:\$p > svg > foreignObject > section[data-footer]>:nth-last-child(2){margin-bottom:0}div#\:\$p > svg > foreignObject > section>:first-child,div#\:\$p > svg > foreignObject > section>header:first-child+*{margin-top:0}div#\:\$p > svg > foreignObject > section:after{position:absolute;padding:0;right:30px;bottom:21px;font-size:24px;color:var(--paginate-color)}div#\:\$p > svg > foreignObject > section:after{--marpit-root-font-size:24px}div#\:\$p > svg > foreignObject > section[data-color] :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section[data-color] :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section[data-color] :is(h3, marp-h3),div#\:\$p > svg > foreignObject > section[data-color] :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section[data-color] :is(h5, marp-h5),div#\:\$p > svg > foreignObject > section[data-color] :is(h6, marp-h6){color:currentcolor}div#\:\$p > svg > foreignObject > section{font-family:"Akkurat Pro", "Helvetica Neue", "Arial", sans-serif;font-size:22px;color:#1B1B1B}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size: 22px}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1){color:#D6002A;font-size:34px;margin-bottom:0.3em}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){color:#D6002A;font-size:26px;margin-bottom:0.2em}div#\:\$p > svg > foreignObject > section.title{background:#1B1B1B;color:#fff;border-top:8px solid #D6002A}div#\:\$p > svg > foreignObject > section.title :is(h1, marp-h1){color:#fff}div#\:\$p > svg > foreignObject > section table{font-size:18px;width:100%}div#\:\$p > svg > foreignObject > section th{background:#F0F0F0}div#\:\$p > svg > foreignObject > section blockquote{border-left:4px solid #D6002A;color:#2E2E2E;font-size:20px}div#\:\$p > svg > foreignObject > section img{display:block;margin:0 auto;max-height:320px}div#\:\$p > svg > foreignObject > section .badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:14px;font-weight:600}div#\:\$p > svg > foreignObject > section section.badge{--marpit-root-font-size: 14px}div#\:\$p > svg > foreignObject > section .testing{background:#DBEAFE;color:#1E3A5F}div#\:\$p > svg > foreignObject > section .planned{background:#fef3c7;color:#78350f}div#\:\$p > svg > foreignObject > section .agentic{background:#EDE9FE;color:#4C1D95}div#\:\$p > svg > foreignObject > section.title :is(h1, marp-h1){font-size:44px;margin-bottom:0.1em}div#\:\$p > svg > foreignObject > section.title :is(h3, marp-h3){color:#F0F0F0;font-weight:400;font-size:22px;margin-top:0}div#\:\$p > svg > foreignObject > section{font-size:16px}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size: 16px}div#\:\$p > svg > foreignObject > section td{font-size:15px;vertical-align:top}div#\:\$p > svg > foreignObject > section ul{margin:0;padding-left:1.2em}div#\:\$p > svg > foreignObject > section li{margin-bottom:2px}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"]::before, div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"]::after, div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="content"]::before, div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="content"]::after{display:none!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction="vertical"]{flex-direction:column}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"][data-marpit-advanced-background-split] > div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split, 50%)}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"][data-marpit-advanced-background-split="right"] > div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container] > figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container] > figure > figcaption{position:absolute;border:0;clip:rect(0, 0, 0, 0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="content"], div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="pseudo"]{background:transparent!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="pseudo"], div#\:\$p > svg[data-marpit-svg] > foreignObject[data-marpit-advanced-background="pseudo"]{pointer-events:none!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background-split]{width:100%;height:100%} + */div#\:\$p > svg > foreignObject > section [data-theme=light],div#\:\$p > svg > foreignObject > section{color-scheme:light}div#\:\$p > svg > foreignObject > section [data-theme=dark],div#\:\$p > svg > foreignObject > section:where(.invert){color-scheme:dark}div#\:\$p > svg > foreignObject > section{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;font-weight:var(--base-text-weight-normal, 400);color:var(--fgColor-default);background-color:var(--bgColor-default);font-family:var(--fontStack-sansSerif, -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji");font-size:16px;line-height:1.5;word-wrap:break-word}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size:16px}div#\:\$p > svg > foreignObject > section a{text-decoration:underline;text-underline-offset:calc(var(--marpit-root-font-size, 1rem) * .2)}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5):hover .anchor .octicon-link:before,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6):hover .anchor .octicon-link:before{width:16px;height:16px;content:" ";display:inline-block;background-color:currentColor;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,')}div#\:\$p > svg > foreignObject > section details,div#\:\$p > svg > foreignObject > section figcaption,div#\:\$p > svg > foreignObject > section figure{display:block}div#\:\$p > svg > foreignObject > section summary{display:list-item}div#\:\$p > svg > foreignObject > section [hidden]{display:none!important}div#\:\$p > svg > foreignObject > section a{background-color:transparent;color:var(--fgColor-accent);text-decoration:none}div#\:\$p > svg > foreignObject > section abbr[title]{border-bottom:none;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}div#\:\$p > svg > foreignObject > section b,div#\:\$p > svg > foreignObject > section strong{font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section dfn{font-style:italic}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1){margin:.67em 0;font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:2em;border-bottom:1px solid var(--borderColor-muted)}div#\:\$p > svg > foreignObject > section mark{background-color:var(--bgColor-attention-muted);color:var(--fgColor-default)}div#\:\$p > svg > foreignObject > section small{font-size:90%}div#\:\$p > svg > foreignObject > section sub,div#\:\$p > svg > foreignObject > section sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}div#\:\$p > svg > foreignObject > section sub{bottom:-.25em}div#\:\$p > svg > foreignObject > section sup{top:-.5em}div#\:\$p > svg > foreignObject > section img{border-style:none;max-width:100%;box-sizing:content-box}div#\:\$p > svg > foreignObject > section code,div#\:\$p > svg > foreignObject > section kbd,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre),div#\:\$p > svg > foreignObject > section samp{font-family:monospace;font-size:1em}div#\:\$p > svg > foreignObject > section figure{margin:1em var(--base-size-40)}div#\:\$p > svg > foreignObject > section hr{box-sizing:content-box;overflow:hidden;background:transparent;height:.25em;padding:0;margin:var(--base-size-24) 0;background-color:var(--borderColor-default);border:0}div#\:\$p > svg > foreignObject > section input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}div#\:\$p > svg > foreignObject > section [type=button],div#\:\$p > svg > foreignObject > section [type=reset],div#\:\$p > svg > foreignObject > section [type=submit]{-webkit-appearance:button;-moz-appearance:button;appearance:button}div#\:\$p > svg > foreignObject > section [type=checkbox],div#\:\$p > svg > foreignObject > section [type=radio]{box-sizing:border-box;padding:0}div#\:\$p > svg > foreignObject > section [type=number]::-webkit-inner-spin-button,div#\:\$p > svg > foreignObject > section [type=number]::-webkit-outer-spin-button{height:auto}div#\:\$p > svg > foreignObject > section [type=search]::-webkit-search-cancel-button,div#\:\$p > svg > foreignObject > section [type=search]::-webkit-search-decoration{-webkit-appearance:none;appearance:none}div#\:\$p > svg > foreignObject > section ::-webkit-input-placeholder{color:inherit;opacity:.54}div#\:\$p > svg > foreignObject > section ::-webkit-file-upload-button{-webkit-appearance:button;appearance:button;font:inherit}div#\:\$p > svg > foreignObject > section a:hover{text-decoration:underline}div#\:\$p > svg > foreignObject > section ::-moz-placeholder{color:var(--fgColor-muted);opacity:1}div#\:\$p > svg > foreignObject > section ::placeholder{color:var(--fgColor-muted);opacity:1}div#\:\$p > svg > foreignObject > section hr:after,div#\:\$p > svg > foreignObject > section hr:before{display:table;content:""}div#\:\$p > svg > foreignObject > section hr:after{clear:both}div#\:\$p > svg > foreignObject > section table{border-spacing:0;border-collapse:collapse;display:block;width:-moz-max-content;width:max-content;max-width:100%;overflow:auto;font-variant:tabular-nums}div#\:\$p > svg > foreignObject > section td,div#\:\$p > svg > foreignObject > section th{padding:0}div#\:\$p > svg > foreignObject > section details summary{cursor:pointer}div#\:\$p > svg > foreignObject > section [role=button]:focus,div#\:\$p > svg > foreignObject > section a:focus,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus,div#\:\$p > svg > foreignObject > section input[type=radio]:focus{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}div#\:\$p > svg > foreignObject > section [role=button]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section a:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section input[type=radio]:focus:not(:focus-visible){outline:1px solid transparent}div#\:\$p > svg > foreignObject > section [role=button]:focus-visible,div#\:\$p > svg > foreignObject > section a:focus-visible,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus-visible,div#\:\$p > svg > foreignObject > section input[type=radio]:focus-visible{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}div#\:\$p > svg > foreignObject > section a:not([class]):focus,div#\:\$p > svg > foreignObject > section a:not([class]):focus-visible,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus,div#\:\$p > svg > foreignObject > section input[type=checkbox]:focus-visible,div#\:\$p > svg > foreignObject > section input[type=radio]:focus,div#\:\$p > svg > foreignObject > section input[type=radio]:focus-visible{outline-offset:0}div#\:\$p > svg > foreignObject > section kbd{display:inline-block;padding:var(--base-size-4);font:11px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);line-height:10px;color:var(--fgColor-default);vertical-align:middle;background-color:var(--bgColor-muted);border-bottom-color:var(--borderColor-neutral-muted);border:1px solid var(--borderColor-neutral-muted);border-radius:6px;box-shadow:inset 0 -1px 0 var(--borderColor-neutral-muted)}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section :is(h3, marp-h3),div#\:\$p > svg > foreignObject > section :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section :is(h5, marp-h5),div#\:\$p > svg > foreignObject > section :is(h6, marp-h6){margin-top:var(--base-size-24);margin-bottom:var(--base-size-16);font-weight:var(--base-text-weight-semibold, 600);line-height:1.25}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--borderColor-muted)}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section :is(h3, marp-h3){font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section :is(h3, marp-h3){font-size:1.25em}div#\:\$p > svg > foreignObject > section :is(h4, marp-h4){font-size:1em}div#\:\$p > svg > foreignObject > section :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section :is(h5, marp-h5){font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section :is(h5, marp-h5){font-size:.875em}div#\:\$p > svg > foreignObject > section :is(h6, marp-h6){font-weight:var(--base-text-weight-semibold, 600);font-size:.85em;color:var(--fgColor-muted)}div#\:\$p > svg > foreignObject > section p{margin-top:0;margin-bottom:10px}div#\:\$p > svg > foreignObject > section blockquote{margin:0;padding:0 1em;color:var(--fgColor-muted);border-left:.25em solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section ol,div#\:\$p > svg > foreignObject > section ul{margin-top:0;margin-bottom:0;padding-left:2em}div#\:\$p > svg > foreignObject > section ol ol,div#\:\$p > svg > foreignObject > section ul ol{list-style-type:lower-roman}div#\:\$p > svg > foreignObject > section ol ol ol,div#\:\$p > svg > foreignObject > section ol ul ol,div#\:\$p > svg > foreignObject > section ul ol ol,div#\:\$p > svg > foreignObject > section ul ul ol{list-style-type:lower-alpha}div#\:\$p > svg > foreignObject > section dd{margin-left:0}div#\:\$p > svg > foreignObject > section code,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre),div#\:\$p > svg > foreignObject > section samp,div#\:\$p > svg > foreignObject > section tt{font-family:var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);font-size:12px}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre){margin-top:0;margin-bottom:0;word-wrap:normal}div#\:\$p > svg > foreignObject > section .octicon{display:inline-block;overflow:visible!important;vertical-align:text-bottom;fill:currentColor}div#\:\$p > svg > foreignObject > section input::-webkit-inner-spin-button,div#\:\$p > svg > foreignObject > section input::-webkit-outer-spin-button{margin:0;-webkit-appearance:none;appearance:none}div#\:\$p > svg > foreignObject > section .mr-2{margin-right:var(--base-size-8, 8px)!important}div#\:\$p > svg > foreignObject > section:after,div#\:\$p > svg > foreignObject > section:before{display:table}div#\:\$p > svg > foreignObject > section:after{clear:both}div#\:\$p > svg > foreignObject > section>:first-child{margin-top:0!important}div#\:\$p > svg > foreignObject > section>:last-child{margin-bottom:0!important}div#\:\$p > svg > foreignObject > section a:not([href]){color:inherit;text-decoration:none}div#\:\$p > svg > foreignObject > section .absent{color:var(--fgColor-danger)}div#\:\$p > svg > foreignObject > section .anchor{float:left;padding-right:var(--base-size-4);margin-left:-20px;line-height:1}div#\:\$p > svg > foreignObject > section .anchor:focus{outline:none}div#\:\$p > svg > foreignObject > section blockquote,div#\:\$p > svg > foreignObject > section details,div#\:\$p > svg > foreignObject > section dl,div#\:\$p > svg > foreignObject > section ol,div#\:\$p > svg > foreignObject > section p,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre),div#\:\$p > svg > foreignObject > section table,div#\:\$p > svg > foreignObject > section ul{margin-top:0;margin-bottom:var(--base-size-16)}div#\:\$p > svg > foreignObject > section blockquote>:first-child{margin-top:0}div#\:\$p > svg > foreignObject > section blockquote>:last-child{margin-bottom:0}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) .octicon-link,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) .octicon-link{color:var(--fgColor-default);vertical-align:middle;visibility:hidden}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5):hover .anchor,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6):hover .anchor{text-decoration:none}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5):hover .anchor .octicon-link,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6):hover .anchor .octicon-link{visibility:visible}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) code,div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) tt,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) code,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) tt,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) code,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) tt,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) code,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) tt,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) code,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) tt,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) code,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) tt{padding:0 .2em;font-size:inherit}div#\:\$p > svg > foreignObject > section summary :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section summary :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section summary :is(h3, marp-h3),div#\:\$p > svg > foreignObject > section summary :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section summary :is(h5, marp-h5),div#\:\$p > svg > foreignObject > section summary :is(h6, marp-h6){display:inline-block}div#\:\$p > svg > foreignObject > section summary :is(h1, marp-h1) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h2, marp-h2) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h3, marp-h3) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h4, marp-h4) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h5, marp-h5) .anchor,div#\:\$p > svg > foreignObject > section summary :is(h6, marp-h6) .anchor{margin-left:-40px}div#\:\$p > svg > foreignObject > section summary :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section summary :is(h2, marp-h2){padding-bottom:0;border-bottom:0}div#\:\$p > svg > foreignObject > section ol.no-list,div#\:\$p > svg > foreignObject > section ul.no-list{padding:0;list-style-type:none}div#\:\$p > svg > foreignObject > section ol[type="a s"]{list-style-type:lower-alpha}div#\:\$p > svg > foreignObject > section ol[type="A s"]{list-style-type:upper-alpha}div#\:\$p > svg > foreignObject > section ol[type="i s"]{list-style-type:lower-roman}div#\:\$p > svg > foreignObject > section ol[type="I s"]{list-style-type:upper-roman}div#\:\$p > svg > foreignObject > section div>ol:not([type]),div#\:\$p > svg > foreignObject > section ol[type="1"]{list-style-type:decimal}div#\:\$p > svg > foreignObject > section ol ol,div#\:\$p > svg > foreignObject > section ol ul,div#\:\$p > svg > foreignObject > section ul ol,div#\:\$p > svg > foreignObject > section ul ul{margin-top:0;margin-bottom:0}div#\:\$p > svg > foreignObject > section li>p{margin-top:var(--base-size-16)}div#\:\$p > svg > foreignObject > section li+li{margin-top:.25em}div#\:\$p > svg > foreignObject > section dl{padding:0}div#\:\$p > svg > foreignObject > section dl dt{padding:0;margin-top:var(--base-size-16);font-size:1em;font-style:italic;font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section dl dd{padding:0 var(--base-size-16);margin-bottom:var(--base-size-16)}div#\:\$p > svg > foreignObject > section table th{font-weight:var(--base-text-weight-semibold, 600)}div#\:\$p > svg > foreignObject > section table td,div#\:\$p > svg > foreignObject > section table th{padding:6px 13px;border:1px solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section table td>:last-child{margin-bottom:0}div#\:\$p > svg > foreignObject > section table tr{background-color:var(--bgColor-default);border-top:1px solid var(--borderColor-muted)}div#\:\$p > svg > foreignObject > section table tr:nth-child(2n){background-color:var(--bgColor-muted)}div#\:\$p > svg > foreignObject > section table img{background-color:transparent}div#\:\$p > svg > foreignObject > section img[align=right]{padding-left:20px}div#\:\$p > svg > foreignObject > section img[align=left]{padding-right:20px}div#\:\$p > svg > foreignObject > section .emoji{max-width:none;vertical-align:text-top;background-color:transparent}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame,div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame>:is(span, marp-span){display:block;overflow:hidden}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame>:is(span, marp-span){float:left;width:auto;padding:7px;margin:13px 0 0;border:1px solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame :is(span, marp-span) img{display:block;float:left}div#\:\$p > svg > foreignObject > section :is(span, marp-span).frame :is(span, marp-span) :is(span, marp-span){display:block;padding:5px 0 0;clear:both;color:var(--fgColor-default)}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-center{display:block;overflow:hidden;clear:both}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-center>:is(span, marp-span){display:block;margin:13px auto 0;overflow:hidden;text-align:center}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-center :is(span, marp-span) img{margin:0 auto;text-align:center}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-right{display:block;overflow:hidden;clear:both}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-right>:is(span, marp-span){display:block;margin:13px 0 0;overflow:hidden;text-align:right}div#\:\$p > svg > foreignObject > section :is(span, marp-span).align-right :is(span, marp-span) img{margin:0;text-align:right}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-left{display:block;float:left;margin-right:13px;overflow:hidden}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-left :is(span, marp-span){margin:13px 0 0}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-right{display:block;float:right;margin-left:13px;overflow:hidden}div#\:\$p > svg > foreignObject > section :is(span, marp-span).float-right>:is(span, marp-span){display:block;margin:13px auto 0;overflow:hidden;text-align:right}div#\:\$p > svg > foreignObject > section code,div#\:\$p > svg > foreignObject > section tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--bgColor-neutral-muted);border-radius:6px}div#\:\$p > svg > foreignObject > section code br,div#\:\$p > svg > foreignObject > section tt br{display:none}div#\:\$p > svg > foreignObject > section del code{text-decoration:inherit}div#\:\$p > svg > foreignObject > section samp{font-size:85%}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) code{font-size:100%}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre)>code{padding:0;margin:0;word-break:normal;white-space:pre;background:transparent;border:0}div#\:\$p > svg > foreignObject > section .highlight{margin-bottom:var(--base-size-16)}div#\:\$p > svg > foreignObject > section .highlight :is(pre, marp-pre){margin-bottom:0;word-break:normal}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre){padding:var(--base-size-16);overflow:auto;font-size:85%;line-height:1.45;color:var(--fgColor-default);background-color:var(--bgColor-muted);border-radius:6px}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) code,div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) tt{display:inline;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}div#\:\$p > svg > foreignObject > section .csv-data td,div#\:\$p > svg > foreignObject > section .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}div#\:\$p > svg > foreignObject > section .csv-data .blob-num{padding:10px var(--base-size-8) 9px;text-align:right;background:var(--bgColor-default);border:0}div#\:\$p > svg > foreignObject > section .csv-data tr{border-top:0}div#\:\$p > svg > foreignObject > section .csv-data th{font-weight:var(--base-text-weight-semibold, 600);background:var(--bgColor-muted);border-top:0}div#\:\$p > svg > foreignObject > section [data-footnote-ref]:before{content:"["}div#\:\$p > svg > foreignObject > section [data-footnote-ref]:after{content:"]"}div#\:\$p > svg > foreignObject > section .footnotes{font-size:12px;color:var(--fgColor-muted);border-top:1px solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section div#\:\$p > svg > foreignObject > section section.footnotes{--marpit-root-font-size:12px}div#\:\$p > svg > foreignObject > section .footnotes ol,div#\:\$p > svg > foreignObject > section .footnotes ol ul{padding-left:var(--base-size-16)}div#\:\$p > svg > foreignObject > section .footnotes ol ul{display:inline-block;margin-top:var(--base-size-16)}div#\:\$p > svg > foreignObject > section .footnotes li{position:relative}div#\:\$p > svg > foreignObject > section .footnotes li:target:before{position:absolute;top:calc(var(--base-size-8)*-1);right:calc(var(--base-size-8)*-1);bottom:calc(var(--base-size-8)*-1);left:calc(var(--base-size-24)*-1);pointer-events:none;content:"";border:2px solid var(--borderColor-accent-emphasis);border-radius:6px}div#\:\$p > svg > foreignObject > section .footnotes li:target{color:var(--fgColor-default)}div#\:\$p > svg > foreignObject > section .footnotes .data-footnote-backref g-emoji{font-family:monospace}div#\:\$p > svg > foreignObject > section .pl-c{color:var(--color-prettylights-syntax-comment)}div#\:\$p > svg > foreignObject > section .pl-c1,div#\:\$p > svg > foreignObject > section .pl-s .pl-v{color:var(--color-prettylights-syntax-constant)}div#\:\$p > svg > foreignObject > section .pl-e,div#\:\$p > svg > foreignObject > section .pl-en{color:var(--color-prettylights-syntax-entity)}div#\:\$p > svg > foreignObject > section .pl-s .pl-s1,div#\:\$p > svg > foreignObject > section .pl-smi{color:var(--color-prettylights-syntax-storage-modifier-import)}div#\:\$p > svg > foreignObject > section .pl-ent{color:var(--color-prettylights-syntax-entity-tag)}div#\:\$p > svg > foreignObject > section .pl-k{color:var(--color-prettylights-syntax-keyword)}div#\:\$p > svg > foreignObject > section .pl-pds,div#\:\$p > svg > foreignObject > section .pl-s,div#\:\$p > svg > foreignObject > section .pl-s .pl-pse .pl-s1,div#\:\$p > svg > foreignObject > section .pl-sr,div#\:\$p > svg > foreignObject > section .pl-sr .pl-cce,div#\:\$p > svg > foreignObject > section .pl-sr .pl-sra,div#\:\$p > svg > foreignObject > section .pl-sr .pl-sre{color:var(--color-prettylights-syntax-string)}div#\:\$p > svg > foreignObject > section .pl-smw,div#\:\$p > svg > foreignObject > section .pl-v{color:var(--color-prettylights-syntax-variable)}div#\:\$p > svg > foreignObject > section .pl-bu{color:var(--color-prettylights-syntax-brackethighlighter-unmatched)}div#\:\$p > svg > foreignObject > section .pl-ii{color:var(--color-prettylights-syntax-invalid-illegal-text);background-color:var(--color-prettylights-syntax-invalid-illegal-bg)}div#\:\$p > svg > foreignObject > section .pl-c2{color:var(--color-prettylights-syntax-carriage-return-text);background-color:var(--color-prettylights-syntax-carriage-return-bg)}div#\:\$p > svg > foreignObject > section .pl-sr .pl-cce{font-weight:700;color:var(--color-prettylights-syntax-string-regexp)}div#\:\$p > svg > foreignObject > section .pl-ml{color:var(--color-prettylights-syntax-markup-list)}div#\:\$p > svg > foreignObject > section .pl-mh,div#\:\$p > svg > foreignObject > section .pl-mh .pl-en,div#\:\$p > svg > foreignObject > section .pl-ms{font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}div#\:\$p > svg > foreignObject > section .pl-mi{font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}div#\:\$p > svg > foreignObject > section .pl-mb{font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}div#\:\$p > svg > foreignObject > section .pl-md{color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}div#\:\$p > svg > foreignObject > section .pl-mi1{color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}div#\:\$p > svg > foreignObject > section .pl-mc{color:var(--color-prettylights-syntax-markup-changed-text);background-color:var(--color-prettylights-syntax-markup-changed-bg)}div#\:\$p > svg > foreignObject > section .pl-mi2{color:var(--color-prettylights-syntax-markup-ignored-text);background-color:var(--color-prettylights-syntax-markup-ignored-bg)}div#\:\$p > svg > foreignObject > section .pl-mdr{font-weight:700;color:var(--color-prettylights-syntax-meta-diff-range)}div#\:\$p > svg > foreignObject > section .pl-ba{color:var(--color-prettylights-syntax-brackethighlighter-angle)}div#\:\$p > svg > foreignObject > section .pl-sg{color:var(--color-prettylights-syntax-sublimelinter-gutter-mark)}div#\:\$p > svg > foreignObject > section .pl-corl{text-decoration:underline;color:var(--color-prettylights-syntax-constant-other-reference-link)}div#\:\$p > svg > foreignObject > section [role=button]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section [role=tabpanel][tabindex="0"]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section a:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section button:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section summary:focus:not(:focus-visible){outline:none;box-shadow:none}div#\:\$p > svg > foreignObject > section [tabindex="0"]:focus:not(:focus-visible),div#\:\$p > svg > foreignObject > section details-dialog:focus:not(:focus-visible){outline:none}div#\:\$p > svg > foreignObject > section g-emoji{display:inline-block;min-width:1ch;font-family:Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-size:1em;font-style:normal!important;font-weight:var(--base-text-weight-normal, 400);line-height:1;vertical-align:-.075em}div#\:\$p > svg > foreignObject > section g-emoji img{width:1em;height:1em}div#\:\$p > svg > foreignObject > section a:has(>p,>div,>:is(pre, marp-pre),>blockquote){display:block}div#\:\$p > svg > foreignObject > section a:has(>p,>div,>:is(pre, marp-pre),>blockquote):not(:has(.snippet-clipboard-content,>:is(pre, marp-pre))){width:-moz-fit-content;width:fit-content}div#\:\$p > svg > foreignObject > section a:has(>p,>div,>:is(pre, marp-pre),>blockquote):has(.snippet-clipboard-content,>:is(pre, marp-pre)):focus-visible{outline:2px solid var(--focus-outlineColor);outline-offset:2px}div#\:\$p > svg > foreignObject > section .task-list-item{list-style-type:none}div#\:\$p > svg > foreignObject > section .task-list-item label{font-weight:var(--base-text-weight-normal, 400)}div#\:\$p > svg > foreignObject > section .task-list-item.enabled label{cursor:pointer}div#\:\$p > svg > foreignObject > section .task-list-item+.task-list-item{margin-top:var(--base-size-4)}div#\:\$p > svg > foreignObject > section .task-list-item .handle{display:none}div#\:\$p > svg > foreignObject > section .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}div#\:\$p > svg > foreignObject > section ul:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}div#\:\$p > svg > foreignObject > section ol:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}div#\:\$p > svg > foreignObject > section .contains-task-list:focus-within .task-list-item-convert-container,div#\:\$p > svg > foreignObject > section .contains-task-list:hover .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip-path:none}div#\:\$p > svg > foreignObject > section ::-webkit-calendar-picker-indicator{filter:invert(50%)}div#\:\$p > svg > foreignObject > section .markdown-alert{padding:var(--base-size-8) var(--base-size-16);margin-bottom:var(--base-size-16);color:inherit;border-left:.25em solid var(--borderColor-default)}div#\:\$p > svg > foreignObject > section .markdown-alert>:first-child{margin-top:0}div#\:\$p > svg > foreignObject > section .markdown-alert>:last-child{margin-bottom:0}div#\:\$p > svg > foreignObject > section .markdown-alert .markdown-alert-title{display:flex;font-weight:var(--base-text-weight-medium, 500);align-items:center;line-height:1}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-note{border-left-color:var(--borderColor-accent-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-note .markdown-alert-title{color:var(--fgColor-accent)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-important{border-left-color:var(--borderColor-done-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-important .markdown-alert-title{color:var(--fgColor-done)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-warning{border-left-color:var(--borderColor-attention-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-warning .markdown-alert-title{color:var(--fgColor-attention)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-tip{border-left-color:var(--borderColor-success-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-tip .markdown-alert-title{color:var(--fgColor-success)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-caution{border-left-color:var(--borderColor-danger-emphasis)}div#\:\$p > svg > foreignObject > section .markdown-alert.markdown-alert-caution .markdown-alert-title{color:var(--fgColor-danger)}div#\:\$p > svg > foreignObject > section>:first-child>.heading-element:first-child{margin-top:0!important}div#\:\$p > svg > foreignObject > section .highlight :is(pre, marp-pre):has(+.zeroclipboard-container){min-height:52px}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1){color:var(--h1-color);font-size:1.6em}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){border-bottom:none}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){font-size:1.3em}div#\:\$p > svg > foreignObject > section :is(h3, marp-h3){font-size:1.1em}div#\:\$p > svg > foreignObject > section :is(h4, marp-h4){font-size:1.05em}div#\:\$p > svg > foreignObject > section :is(h5, marp-h5){font-size:1em}div#\:\$p > svg > foreignObject > section :is(h6, marp-h6){font-size:.9em}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1) strong,div#\:\$p > svg > foreignObject > section :is(h2, marp-h2) strong,div#\:\$p > svg > foreignObject > section :is(h3, marp-h3) strong,div#\:\$p > svg > foreignObject > section :is(h4, marp-h4) strong,div#\:\$p > svg > foreignObject > section :is(h5, marp-h5) strong,div#\:\$p > svg > foreignObject > section :is(h6, marp-h6) strong{font-weight:inherit;color:var(--heading-strong-color)}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h2, marp-h2)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h3, marp-h3)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h4, marp-h4)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h5, marp-h5)::part(auto-scaling),div#\:\$p > svg > foreignObject > section :is(h6, marp-h6)::part(auto-scaling){max-height:563px}div#\:\$p > svg > foreignObject > section hr{height:0;padding-top:.25em}div#\:\$p > svg > foreignObject > section img{background-color:transparent}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre){border:1px solid var(--borderColor-default);line-height:1.15;overflow:visible}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre)::part(auto-scaling){max-height:529px}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs){color:var(--color-prettylights-syntax-storage-modifier-import)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-doctag),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-keyword),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-meta .hljs-keyword),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-template-tag),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-template-variable),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-type),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-variable.language_){color:var(--color-prettylights-syntax-keyword)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title.class_),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title.class_.inherited__),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-title.function_){color:var(--color-prettylights-syntax-entity)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-attr),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-attribute),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-literal),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-meta),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-number),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-operator),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-attr),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-class),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-id),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-variable){color:var(--color-prettylights-syntax-constant)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-meta .hljs-string),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-regexp),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-string){color:var(--color-prettylights-syntax-string)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-built_in),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-symbol){color:var(--color-prettylights-syntax-variable)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-code),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-comment),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-formula){color:var(--color-prettylights-syntax-comment)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-name),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-quote),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-pseudo),div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-selector-tag){color:var(--color-prettylights-syntax-entity-tag)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-subst){color:var(--color-prettylights-syntax-storage-modifier-import)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-section){font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-bullet){color:var(--color-prettylights-syntax-markup-list)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-emphasis){font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-strong){font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-addition){color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}div#\:\$p > svg > foreignObject > section :is(pre, marp-pre) :where(.hljs-deletion){color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}div#\:\$p > svg > foreignObject > section footer,div#\:\$p > svg > foreignObject > section header{margin:0;position:absolute;left:30px;color:var(--header-footer-color);font-size:18px}div#\:\$p > svg > foreignObject > section header{top:21px}div#\:\$p > svg > foreignObject > section footer{bottom:21px}div#\:\$p > svg > foreignObject > section{--h1-color:light-dark(#246, #cee7ff);--header-footer-color:light-dark(hsla(0,0%,40%,.75), hsla(0,0%,60%,.75));--heading-strong-color:light-dark(#48c, #7bf);--paginate-color:light-dark(#777, #999);--base-size-4:4px;--base-size-8:8px;--base-size-16:16px;--base-size-24:24px;--base-size-40:40px;display:block;font-size:29px;height:720px;padding:78.5px;place-content:safe center center;width:1280px;flex-flow:column nowrap;align-items:stretch}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size:29px}div#\:\$p > svg > foreignObject > section>:last-child,div#\:\$p > svg > foreignObject > section[data-footer]>:nth-last-child(2){margin-bottom:0}div#\:\$p > svg > foreignObject > section>:first-child,div#\:\$p > svg > foreignObject > section>header:first-child+*{margin-top:0}div#\:\$p > svg > foreignObject > section:after{position:absolute;padding:0;right:30px;bottom:21px;font-size:24px;color:var(--paginate-color)}div#\:\$p > svg > foreignObject > section:after{--marpit-root-font-size:24px}div#\:\$p > svg > foreignObject > section[data-color] :is(h1, marp-h1),div#\:\$p > svg > foreignObject > section[data-color] :is(h2, marp-h2),div#\:\$p > svg > foreignObject > section[data-color] :is(h3, marp-h3),div#\:\$p > svg > foreignObject > section[data-color] :is(h4, marp-h4),div#\:\$p > svg > foreignObject > section[data-color] :is(h5, marp-h5),div#\:\$p > svg > foreignObject > section[data-color] :is(h6, marp-h6){color:currentcolor}div#\:\$p > svg > foreignObject > section{font-family:"Akkurat Pro", "Helvetica Neue", "Arial", sans-serif;font-size:22px;color:#1B1B1B}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size: 22px}div#\:\$p > svg > foreignObject > section :is(h1, marp-h1){color:#D6002A;font-size:34px;margin-bottom:0.3em}div#\:\$p > svg > foreignObject > section :is(h2, marp-h2){color:#D6002A;font-size:26px;margin-bottom:0.2em}div#\:\$p > svg > foreignObject > section.title{background:#1B1B1B;color:#fff;border-top:8px solid #D6002A}div#\:\$p > svg > foreignObject > section.title :is(h1, marp-h1){color:#fff}div#\:\$p > svg > foreignObject > section table{font-size:18px;width:100%}div#\:\$p > svg > foreignObject > section th{background:#F0F0F0}div#\:\$p > svg > foreignObject > section blockquote{border-left:4px solid #D6002A;color:#2E2E2E;font-size:20px}div#\:\$p > svg > foreignObject > section img{display:block;margin:0 auto;max-height:300px}div#\:\$p > svg > foreignObject > section em.story{color:#6B7280;font-size:16px;font-style:italic}div#\:\$p > svg > foreignObject > section .badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:14px;font-weight:600}div#\:\$p > svg > foreignObject > section section.badge{--marpit-root-font-size: 14px}div#\:\$p > svg > foreignObject > section .testing{background:#DBEAFE;color:#1E3A5F}div#\:\$p > svg > foreignObject > section .planned{background:#fef3c7;color:#78350f}div#\:\$p > svg > foreignObject > section .agentic{background:#EDE9FE;color:#4C1D95}div#\:\$p > svg > foreignObject > section.title :is(h1, marp-h1){font-size:44px;margin-bottom:0.1em}div#\:\$p > svg > foreignObject > section.title :is(h3, marp-h3){color:#F0F0F0;font-weight:400;font-size:22px;margin-top:0}div#\:\$p > svg > foreignObject > section{font-size:20px}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size: 20px}div#\:\$p > svg > foreignObject > section{font-size:15px}div#\:\$p > svg > foreignObject > section{--marpit-root-font-size: 15px}div#\:\$p > svg > foreignObject > section td{font-size:14px;vertical-align:top}div#\:\$p > svg > foreignObject > section ul{margin:0;padding-left:1.2em}div#\:\$p > svg > foreignObject > section li{margin-bottom:2px}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"]::before, div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"]::after, div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="content"]::before, div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="content"]::after{display:none!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction="vertical"]{flex-direction:column}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"][data-marpit-advanced-background-split] > div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split, 50%)}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"][data-marpit-advanced-background-split="right"] > div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container] > figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="background"] > div[data-marpit-advanced-background-container] > figure > figcaption{position:absolute;border:0;clip:rect(0, 0, 0, 0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="content"], div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="pseudo"]{background:transparent!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background="pseudo"], div#\:\$p > svg[data-marpit-svg] > foreignObject[data-marpit-advanced-background="pseudo"]{pointer-events:none!important}div#\:\$p > svg > foreignObject > section[data-marpit-advanced-background-split]{width:100%;height:100%}
+;" data-marpit-pagination-total="17">
How The Platform Works

The Problem & The North Star

+

Story beat: Here's the problem we're solving and where we're going.

Four frictions slow every team:

  • Cognitive load — authoring infrastructure correctly; the long tail of services inconsistent in security and observability
  • @@ -112,8 +117,6 @@ img { display: block; margin: 0 auto; max-height: 320px; }
  • A merged change progresses without a platform engineer joining a thread or approving a ticket
  • A non-technical consumer ships by declaring intent — no workflow, no config file, no infrastructure module
  • Every production change is traceable to a human attestation and an immutable evidence stream
  • -
  • Not a general-purpose AI — autonomy is narrow, scoped to delivery, bounded by strict policy
  • -
  • Not a permissive delivery highway — no escape hatches to bypass the confidence framework
Internal
@@ -129,7 +132,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -149,7 +153,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -157,16 +162,16 @@ img { display: block; margin: 0 auto; max-height: 320px; } .testing { background: #DBEAFE; color: #1E3A5F; } .planned { background: #fef3c7; color: #78350f; } .agentic { background: #EDE9FE; color: #4C1D95; } -;" data-marpit-pagination-total="10"> +;" data-marpit-pagination-total="17">
How The Platform Works
-

The Contract-Driven Model

-

A single YAML contract is all a consumer writes — module, environment, inputs. The platform owns everything else.

-

+

Where ACDL Sits in Your World

+

Story beat: Now that we know the problem, here's where ACDL fits — and where it doesn't.

+

    -
  • Which module — a catalog of pre-built, security-reviewed building blocks
  • -
  • Which environment — the platform raises the safety bar automatically as sensitivity rises
  • -
  • Which inputs — the handful of values that vary per deployment
  • -
  • The consumer provides no AWS account, no VPC, no state backend, no runner key — the platform owns the blast radius
  • +
  • Upstream is anything — your IDE, an agentic SDLC, or a citizen developer vibe coding on a laptop. ACDL doesn't care how the contract was produced.
  • +
  • ACDL is infrastructure only — it provisions and governs AWS resources. It does not build, test, or deploy your application code. That's upstream.
  • +
  • Not a general-purpose AI — autonomy is narrow, scoped to delivery, bounded by strict policy
  • +
  • Not a permissive delivery highway — no escape hatches to bypass the confidence framework
Internal
@@ -182,7 +187,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -202,7 +208,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -210,14 +217,17 @@ img { display: block; margin: 0 auto; max-height: 320px; } .testing { background: #DBEAFE; color: #1E3A5F; } .planned { background: #fef3c7; color: #78350f; } .agentic { background: #EDE9FE; color: #4C1D95; } -;" data-marpit-pagination-total="10"> +;" data-marpit-pagination-total="17">
How The Platform Works
-

The End-to-End Flow

-

Every deployment runs the same stages, in the same order, with the same checks — no team-specific pipelines, no tribal runbooks.

-

+

The Contract-Driven Model

+

Story beat: The contract is the boundary between upstream and ACDL. It's all a consumer writes.

+

A single YAML contract — module, environment, inputs. The platform owns everything else.

+

    -
  • Security and policy checks run before any infrastructure is created
  • -
  • Every stage produces a record that feeds the confidence signal and the evidence stream — there is no "unchecked" path
  • +
  • Which module — a catalog of pre-built, security-reviewed building blocks
  • +
  • Which environment — the platform raises the safety bar automatically as sensitivity rises
  • +
  • Which inputs — infrastructure values that vary per deployment (cpu, memory, port, desired_count)
  • +
  • The consumer provides no AWS account, no VPC, no state backend — the platform owns the blast radius
Internal
@@ -233,7 +243,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -253,7 +264,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -261,20 +273,16 @@ img { display: block; margin: 0 auto; max-height: 320px; } .testing { background: #DBEAFE; color: #1E3A5F; } .planned { background: #fef3c7; color: #78350f; } .agentic { background: #EDE9FE; color: #4C1D95; } -;" data-marpit-pagination-total="10"> +;" data-marpit-pagination-total="17">
How The Platform Works
-

Zero-Trust by Default

-

Consumer repositories hold no long-lived cloud credentials. Ever.

+

The End-to-End Flow

+

Story beat: Once the contract is written, here's what the platform does with it — every time.

+

Every deployment runs the same stages, in the same order, with the same checks — no team-specific pipelines, no tribal runbooks.

+

    -
  • Authentication — OIDC federation. Each job mints a short-lived token; no credential is stored in the consumer repo or in a runner secret. Testing (GitHub Actions) Planned: all runners
  • -
  • Authorization — attribute-based (ABAC), not role-based. Two attribute classes scope every action: -
      -
    • Repository identity — the role's trust policy binds to the exact consumer repo + branch
    • -
    • Resource tags — every resource is tagged acdl:owner + acdl:contract; the session policy grants access only to matching tags
    • +
    • Security and policy checks run before any infrastructure is created
    • +
    • Every stage produces a record that feeds the confidence signal and the evidence stream — there is no "unchecked" path
    -
  • -
-

The effect: a consumer can only touch the resources it created. Blast radius is contained. One consumer can never affect another.

Internal
+;" data-marpit-pagination-total="17">
How The Platform Works
-

Safety is Computed, Not Assumed

-

Every delivery action produces a measurable, explainable confidence signal — the platform's certified answer to "is this safe to proceed?" Agentic

+

Zero-Trust by Default

+

Story beat: Before any infrastructure is created, here's how access is scoped.

+

Consumer repositories hold no long-lived cloud credentials. Ever.

    -
  • Six weighted inputs: policy conformance, validation, freshness, source provenance, history, NFRs
  • -
  • Per-environment thresholds that rise with sensitivity:
  • -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EnvironmentThresholdAttester
dev≥ 0.50No one — autonomous
qa≥ 0.75QA
prod≥ 0.90SRE
dr≥ 0.95SRE + DR drill
+
  • Authentication — OIDC federation. Each job mints a short-lived token; no credential is stored in the consumer repo or in a runner secret. Testing (GitHub Actions) Planned: all runners
  • +
  • Authorization — attribute-based (ABAC), not role-based. Two attribute classes scope every action:
      -
    • A single critical finding hard-blocks the deployment — critical findings are not averaged away
    • -
    • When the platform halts, it gives a measured reason — never an opaque debugging exercise
    • +
    • Repository identity — the role's trust policy binds to the exact consumer repo + branch
    • +
    • Resource tags — every resource is tagged acdl:owner + acdl:contract; the session policy grants access only to matching tags
    +
  • + +

    The effect: a consumer can only touch the resources it created. Blast radius is contained. One consumer can never affect another.

    Internal
    +;" data-marpit-pagination-total="17">
    How The Platform Works
    -

    Security by Construction

    -

    Security defaults that do not require a team to opt in. Checks run on every deployment, normalized to a single schema. Testing

    +

    Safety is Computed, Not Assumed

    +

    Story beat: Now let's look at how the platform decides whether a deployment is safe.

    +

    Every delivery action produces a measurable, explainable confidence signal — a weighted sum of observable facts, not a black box. Agentic

    +

      -
    • Policy checks (Checkov, Wiz, Kyverno) — secrets in plaintext, public ingress, IAM wildcards, required tagging standards — all run before infra is created
    • -
    • Encryption on every resource — at-rest encryption on by default; per-stack customer-managed keys with 90-day rotation, no shared keys across stacks
    • -
    • Deletion protection on by defaultprevent_destroy on unless explicitly disabled via a documented flag
    • -
    • Safe decommission — a 2-step pipeline with two SRE attestation gates and a change-request validated against the CMDB
    • +
    • Six weighted inputs — manually tuned, auditable. If a consumer asks "why 0.62?", the platform answers with a per-input breakdown
    • +
    • Per-environment thresholds that rise with sensitivity:
    • +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentThresholdAttester
    dev≥ 0.50No one — autonomous Testing
    qa≥ 0.75QA Planned
    prod≥ 0.90SRE Planned
    +
      +
    • A single critical finding hard-blocks — critical findings are not averaged away
    Internal
    @@ -426,7 +439,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -446,7 +460,8 @@ section.title h1 { color: #fff; } table { font-size: 18px; width: 100%; } th { background: #F0F0F0; } blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; } -img { display: block; margin: 0 auto; max-height: 320px; } +img { display: block; margin: 0 auto; max-height: 300px; } +em.story { color: #6B7280; font-size: 16px; font-style: italic; } .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; @@ -454,20 +469,20 @@ img { display: block; margin: 0 auto; max-height: 320px; } .testing { background: #DBEAFE; color: #1E3A5F; } .planned { background: #fef3c7; color: #78350f; } .agentic { background: #EDE9FE; color: #4C1D95; } -;" data-marpit-pagination-total="10"> +;" data-marpit-pagination-total="17">
    How The Platform Works
    -

    Accountability & Audit

    -

    Autonomy and accountability are not in tension — they apply at different environments.

    +

    Security by Construction

    +

    Story beat: Beyond the confidence signal, security defaults are on by construction — not by opt-in.

    +

    Security defaults that do not require a team to opt in. Checks run on every deployment, normalized to a single schema. Testing

      -
    • Dev is fully autonomous. The confidence signal (≥ 0.50) is the only gate. Queue-based handoffs are eliminated. Agentic
    • -
    • qa, prod, dr require deliberate human attestation — policy-mandated acts of accountability, not rubber stamps
    • -
    • Separation of duties is enforced — the QA approver cannot be the prod approver. The platform blocks on a match. Design tested Wiring: planned
    • -
    • Every deployment writes a hash-chained evidence event — tampering breaks the chain. RPO = 0 — the evidence write is synchronous Testing
    • -
    • Every production change is traceable to a human attestation — the only durable record outside the VCS's audit log
    • +
    • Policy checks (Checkov, Wiz, Kyverno) — secrets in plaintext, public ingress, IAM wildcards, required tagging standards — all run before infra is created
    • +
    • Encryption on every resource — at-rest encryption on by default; per-stack customer-managed keys with 90-day rotation, no shared keys across stacks
    • +
    • Deletion protection on by defaultprevent_destroy on unless explicitly disabled via a documented flag
    • +
    • Safe decommission — a 2-step pipeline with two SRE attestation gates and a change-request validated against the CMDB
    Internal
    -
    +
    How The Platform Works
    +

    Accountability & Audit

    +

    Story beat: Computed safety handles the gate. But humans still matter — here's how accountability works.

    +

    +
      +
    • Dev is fully autonomous. The confidence signal (≥ 0.50) is the only gate. Testing Agentic
    • +
    • qa, prod, dr require human attestation — the approver reviews the contract, the planned Terraform changes, and the accumulated evidence Planned
    • +
    • QA attests to infrastructure readiness, not application code — the contract, the plan, and the evidence. Application code review is upstream
    • +
    • Separation of duties — the QA approver cannot be the prod approver. The platform blocks on a match. Planned
    • +
    • Every deployment writes a hash-chained evidence event — tampering breaks the chain. RPO = 0 Testing
    • +
    +
    Internal
    +
    +
    Testing vs. Planned +

    Story beat: Let's be honest about what works today and what's on the roadmap.

    + +

    11 capabilities testing today (dev pilot-ready):

    +
      +
    • Contract-driven deploys · Module catalog · Zero-trust OIDC + ABAC
    • +
    • Security + policy checks before infra creation · Confidence signal gating
    • +
    • Hash-chained evidence outbox (RPO = 0) · Encryption by default + per-stack CMKs
    • +
    • Deletion protection + safe decommission · Uptime monitoring
    • +
    • Platform-managed environments · Engine-agnostic core + VCS-agnostic ingestion
    • +
    +

    9 planned (production path):

    +
      +
    • HITL wiring for qa/prod/dr · All-runner OIDC · Full regulatory ledger
    • +
    • Compliance milestone (GDPR, SOX, SOC2, DORA) · Environment self-service
    • +
    • Dynamic module creation Agentic · Pattern recognition Agentic
    • +
    • Additional engine adapters · Deeper observability bootstrap
    • +
    +

    Full inventory + phased roadmap in the appendix.

    +
    Internal
    +
    +
    +
    How The Platform Works
    + +

    The Vision Realized

    +

    Story beat: Here's what success looks like when the North Star is reached.

    +
      +
    • Velocity without sacrificing safety. Speed is in the ergonomics (a simple contract, a one-line uses:); safety is in the gates the consumer cannot bypass.
    • +
    • Security, observability, and compliance as platform defaults — not per-team effort, not post-hoc remediation.
    • +
    • Auditability as a byproduct, not a project. Every production change is traceable to a human attestation and a tamper-evident evidence event.
    • +
    • Blast radius contained by design. Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources.
    • +
    • Infrastructure as a utility, not a craft. Teams consume infrastructure, they don't maintain it.
    • +
    • A path to the citizen developer. The same safety envelope that serves a senior engineer will serve a non-technical consumer. Agentic
    • +
    +
    Internal
    +
    +
    +
    How The Platform Works
    + +

    Appendix

    +

    For deep dives — these slides cover details omitted from the main 10.

    +

    Contents:

    +
      +
    1. Platform-Managed Environments (detail)
    2. +
    3. Observability Built In (detail)
    4. +
    5. The Road to the North Star (phased roadmap)
    6. +
    7. Testing vs. Planned (full inventory)
    8. +
    9. Glossary
    10. +
    +
    Internal
    +
    +
    +
    How The Platform Works
    +

    A1 — Platform-Managed Environments

    +

    A consumer provides no AWS account, no VPC, no subnet, no state backend, no runner key. The platform owns the blast radius.

    +

    A named environment is a platform-owned bundle of:

    +
      +
    • An AWS account (or a scoped partition of one)
    • +
    • A network (VPC + subnets)
    • +
    • A state backend (S3 + DynamoDB for state + locking)
    • +
    • An IAM role surfaced via ABAC, scoped to the consumer's identity and resource tags
    • +
    +

    The consumer selects an environment by name in their contract. The platform resolves the name to the underlying resources at run time. The consumer never sees raw credentials.

    +

    Friendly onboarding: the first run detects no environment and emits a guided prompt (not an opaque failure). Testing Self-service: planned

    +
    Internal
    +
    +
    +
    How The Platform Works
    +

    A2 — Observability Built In

    +

    Monitoring is a platform default, not a per-team project. Testing

    +
      +
    • Uptime monitoring deployed automatically with every stack — a dedicated monitoring instance is provisioned after any module deploy, in a separate state, with a feature flag to disable
    • +
    • Monitored endpoints passed from the deployment's own outputs — no manual endpoint registration
    • +
    • Alert channels: Microsoft Teams webhook, email, SMS, and GitHub issues
    • +
    • The uptime URL is published to the developer via a PR comment — they don't hunt for it
    • +
    • Roadmap: deeper observability bootstrap (dashboards, runbooks, on-call bindings) as first-class contract fields Planned
    • +
    +
    Internal
    +
    +
    +
    How The Platform Works
    + +

    A3 — The Road to the North Star

    +

    Proposed phasing — not formally planned.

    +

    +
    Internal
    +
    +
    +
    How The Platform Works
    + +

    A4 — Testing vs. Planned (Full Inventory)

    -

    Testing (works internally, not yet released to consumers)

    +

    Testing (works internally, dev pilot-ready)

    • Contract-driven deploys with a versioned reusable workflow
    • Module catalog (primitives + modules) with validated examples
    • @@ -547,7 +962,7 @@ img { display: block; margin: 0 auto; max-height: 320px; }
    Internal
    -
    +;" data-marpit-pagination-total="17">
    How The Platform Works
    - -

    The Vision Realized

    -
      -
    • Velocity without sacrificing safety. Speed is in the ergonomics (a simple contract, a one-line uses:); safety is in the gates the consumer cannot bypass.
    • -
    • Security, observability, and compliance as platform defaults — not per-team effort, not post-hoc remediation. Encryption, deletion protection, uptime monitoring, policy checks, and evidence are on by construction.
    • -
    • Auditability as a byproduct, not a project. Every production change is traceable to a human attestation and a tamper-evident evidence event.
    • -
    • Blast radius contained by design. Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources.
    • -
    • Infrastructure as a utility, not a craft. The platform abstracts compute, networking, and state. Teams consume infrastructure, they don't maintain it.
    • -
    • A path to the citizen developer. The same safety envelope that serves a senior engineer will serve a non-technical consumer — expanding who can ship safely without lowering the bar. Agentic
    • -
    +

    A5 — Glossary

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TermMeaning
    OIDCOpenID Connect — federation protocol for short-lived tokens, no long-lived credentials
    ABACAttribute-Based Access Control — access scoped by resource tags + repo identity, not roles
    CMKCustomer-Managed Key — per-stack encryption key, 90-day rotation, no shared keys
    CMDBConfiguration Management Database — validates change requests for decommission
    RPORecovery Point Objective — RPO = 0 means evidence is written synchronously, no data loss
    HITLHuman-in-the-Loop — deliberate human attestation required for qa/prod/dr environments
    VCSVersion Control System — the git hosting platform (GitHub, Gitea, GitLab)
    NFRNon-Functional Requirement — encryption, tagging, observability standards
    IRIntermediate Representation — the engine-agnostic stack definition between contract and Terraform
    Internal
    \ No newline at end of file diff --git a/docs/presentations/the-developer-experience.md b/docs/presentations/the-developer-experience.md index 0f35e14..ce995db 100644 --- a/docs/presentations/the-developer-experience.md +++ b/docs/presentations/the-developer-experience.md @@ -2,45 +2,67 @@ > **Subtitle:** Agentic Cloud Delivery Platform > **Audience:** Senior Leadership, CTO, Head of Cloud, Head of Infrastructure, Head of DevOps -> **Length:** ~15 minutes · 13 slides +> **Length:** ~15 minutes · 10 main + 6 appendix = 16 slides > **Purpose:** Sell the developer experience and the citizen developer experience to tech leadership — velocity without sacrificing safety, and security/observability/compliance as platform defaults rather than per-team effort. -> **Maturity framing:** "Testing" = shipped and verified. "Planned" = on the roadmap, not yet shipped. +> **Maturity framing:** "Testing" = works internally, dev pilot-ready. "Planned" = on the roadmap. "Agentic" = involves AI agents or autonomous decision-making. --- -## Slide 1 — Two Consumer Surfaces, One Platform +## Slide 1 — Title -The platform serves **two kinds of consumer** through two coordinated interfaces — but both converge on the **same contract, the same policy envelope, and the same evidence stream.** +The consumer surface is intentionally tiny. The platform's surface is large and opinionated. + +> **Speaker notes:** Brief introduction — this deck covers *who uses the platform and how fast/safe they ship*, not the internal mechanics (that's the companion deck). Set the frame: velocity without sacrificing safety, and security/observability/compliance as platform defaults rather than per-team effort. + +--- + +## Slide 2 — Where ACDL Sits in Your World + +Story beat: Here's who uses the platform and where the boundary is. + +The platform serves **two kinds of consumer** through two coordinated paths — but both converge on the **same contract, the same policy envelope, and the same evidence stream.** ```mermaid flowchart TD - A["Technical developer"] --> C["Contract YAML"] - B["Citizen developer
    (non-technical)"] --> D["Declares intent in
    natural language"] - D --> E["Agent produces
    the contract"] - C --> F["Same platform:
    resolve → check → plan → policy
    → confidence → evidence → apply"] - E --> F - F --> G["Same safety guarantees,
    same audit trail"] + U1["Anything upstream
    (IDE / agentic SDLC / vibe coding)"] --> T["Technical developer
    writes app + contract"] + U1 --> C["Citizen developer
    declares intent"] + T --> K["Contract YAML"] + C --> AI["An AI agent maps intent
    to a reviewed-skill contract"] + AI --> K + K --> ACDL["ACDL — infrastructure only
    resolve → check → plan → policy
    → confidence → evidence → apply"] + ACDL --> AWS["AWS resources provisioned + governed"] ``` - **Technical developer** — owns app code + a contract + a thin CI definition. Uses the full module catalog and inputs. -- **Citizen developer** — declares intent in plain language; an AI agent produces a contract that passes the **same** safety envelope as a senior engineer's. +- **Citizen developer** — declares intent in plain language; an AI agent produces a contract that passes the **same** safety envelope as a senior engineer's. Agentic +- **Upstream is anything** — your IDE, an agentic SDLC, or vibe coding on a laptop. ACDL doesn't care how the contract was produced. +- **ACDL is infrastructure only** — it provisions and governs AWS resources. Application deployment is upstream. The platform is **opinionated in what it accepts, regardless of who is declaring.** There is no "citizen developer mode" with weaker checks. -> **Speaker notes:** This is the thesis of the deck. The two surfaces are *parallel*, not a progression — a citizen developer doesn't "graduate" to the developer surface. Both produce a contract; both get the same treatment. The leadership takeaway: we expand who can ship safely without lowering the bar. +> **Speaker notes:** This is the thesis of the deck. The two surfaces are *parallel*, not a progression — a citizen developer doesn't "graduate" to the developer surface. Both produce a contract; both get the same treatment. The scope boundary matters: anything upstream of the contract is out of ACDL's concern — ACDL is the infrastructure layer that takes a contract and governs the AWS resources. The leadership takeaway: we expand who can ship safely without lowering the bar. --- -## Slide 2 — What a Developer Actually Does +## Slide 3 — The Contract — The Entire Consumer Surface + +Story beat: Now let's look at what a consumer actually writes — it's tiny. Three things. That is the entire consumer-side surface. -```mermaid -flowchart LR - A["1. App code
    (top level of the repo)"] --> D["Push to main"] - B["2. Contract
    (.acdl/contract.yaml)"] --> D - C["3. CI definition
    (.github/workflows/deploy.yml
    — one 'uses:' line)"] --> D - D --> E["Platform does the rest"] +1. **App code** — the consumer's service, at the top level of the repo +2. **A contract** — a single YAML file: module, environment, inputs +3. **A one-line CI definition** — a thin `uses:` wrapper pointing at a versioned platform workflow + +```yaml +uses: acdl/pipelines/deploy.yaml@v1.6 +module: microservice +environment: dev +inputs: + cpu: 256 + memory: 512 + desired_count: 2 + port: 8080 ``` The developer does **not**: @@ -51,16 +73,196 @@ The developer does **not**: - Hold cloud credentials. - Maintain a state backend, a VPC, or a runner. -> **Speaker notes:** Hold this slide. The audience should sit with how small the consumer surface is. Every item in the "does not" list is a category of toil the platform removes. For the Head of DevOps: this is the lever for throughput — the bottleneck moves off the platform team's ticket queue. +> **Speaker notes:** Hold this slide. The audience should sit with how small the consumer surface is. Every item in the "does not" list is a category of toil the platform removes. The contract is the API — deliberately tiny so that it can be reviewed, validated, and audited. For the Head of DevOps: this is the lever for throughput — the bottleneck moves off the platform team's ticket queue. --- -## Slide 3 — The Citizen Developer Experience +## Slide 4 — The Developer Feedback Loop -A non-technical consumer ships a production deployment **by declaring intent** — without authoring a workflow, a configuration file, or an infrastructure module. +Story beat: Once you push, here's what you see — in real time, in your own logs. + +Developers see **what the platform is doing**, in real time. Testing + +- **Streamed output by default** — the infrastructure plan, policy-check results, and each check record flow to stdout. +- **PR comments after every successful pipeline stage** — a developer always knows where they stand without refreshing a dashboard. +- **Clear, explainable halt reasons** — a policy violation, an insufficient confidence signal, or a missing attestation. **Never an opaque debugging exercise.** +- **Connection strings posted as PR comments** — human-readable, no hunting. Runtime secrets go to encrypted Parameter Store (KMS-encrypted, namespaced), never to logs. +- **Errors become GitHub issues, automatically** — a failed deploy opens an issue on the platform repo. The consumer's only grant is the onboarding-granted Lambda-invoke permission — no separate `issues: write` scope on the consumer side. + +> **Speaker notes:** This directly answers "but developers hate platforms that hide what they're doing." The platform is opinionated about *what* runs, not *opaque* about *that* it runs. The PR-comment-after-each-stage pattern is a small thing that compounds into trust. The "errors become issues" point is a DX win that also helps the platform team — every consumer failure is a tracked, queryable artifact, not a lost log line. The Head of DevOps should hear: the platform closes the feedback loop, it doesn't just push a green/red status. + +--- + +## Slide 5 — Versioned, Predictable Releases + +Story beat: You control when you absorb platform improvements — no surprise upgrades. + +Consumers control **when** they absorb platform improvements. Testing + +- **Floating MAJOR + MINOR tags** (e.g. `@v1.6`) — a consumer automatically receives patch updates within the line. +- **Semantic versioning with a clear contract:** interface → MAJOR, behavior → MINOR, lifecycle → PATCH. +- **A consumer can pin to an exact version** for maximum stability, or float on MAJOR only (`@v1`) to absorb new features on their own cadence. +- **Unversioned references (`@main`, bare) are discouraged** — the versioned tag is the only immutability lever a consumer has. +- **Automated release job** computes the next semver on merge to main, creates the tag, and updates the floating tags. + +> **Speaker notes:** This is the "no surprise upgrades" story. Leadership hears two things: (1) consumers aren't forced to chase the platform, (2) the platform isn't forced to support N forks of every workflow. The versioning discipline is what makes both true. + +--- + +## Slide 6 — Friendly Onboarding + +Story beat: First impressions matter — the platform fails gracefully, not opaquely. + +First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully. Testing + +When no environment is bound, the platform emits a **user-friendly onboarding prompt** instead of failing opaquely. The prompt tells the consumer: + +1. That no environment is bound to their repo yet. +2. What the platform will provision on their behalf (account, network, state, role). +3. The expected turnaround for the platform team to grant the environment. +4. How to request an environment. + +The pipeline then **exits without attempting a deployment** — no partial state, no confusing errors. + +Citizen developer onboarding path: planned + +> **Speaker notes:** This looks like a small thing; it's actually a cultural one. The platform's posture is "help me get started," not "you should have known." For the Head of DevOps: this is what drives adoption. Platforms that fail opaquely on first run get routed around. + +--- + +## Slide 7 — Safe Promotion Path + +Story beat: Promotion is a workflow choice, not a contract edit — and the bar rises automatically. + +The contract is environment-agnostic. The platform raises the bar automatically. + +```mermaid +flowchart LR + DEV["dev
    autonomous"] -->|raise the bar| QA["qa
    QA attests"] + QA -->|raise the bar| PROD["prod
    SRE attests"] + PROD -->|raise the bar| DR["dr
    SRE attests + DR drill"] +``` + +**Approach A — One contract, one job per environment.** A single contract is referenced by multiple jobs; the environment is passed by each job and interpolated at runtime. The contract itself never changes. + +```yaml +jobs: + dev: + uses: acdl/.github/workflows/deploy.yml@v1.6 + with: { contract: .acdl/contract.yaml, environment: dev } + qa: + needs: dev + uses: acdl/.github/workflows/deploy.yml@v1.6 + with: { contract: .acdl/contract.yaml, environment: qa } +``` + +**Approach B — Environment-specific contracts.** When inputs genuinely differ per environment, each job points at its own contract file. The pipeline, policy, and confidence model stay identical. + +```yaml +jobs: + dev: + uses: acdl/.github/workflows/deploy.yml@v1.6 + with: { contract: .acdl/contract-dev.yaml } + qa: + needs: dev + uses: acdl/.github/workflows/deploy.yml@v1.6 + with: { contract: .acdl/contract-qa.yaml } +``` + +Whichever approach a team picks, the platform applies the same rising bar: + +| Environment | What the platform adds | Maturity | +|---|---|---| +| dev | Confidence ≥ 0.50, fully autonomous | Testing | +| qa | QA human attestation + confidence ≥ 0.75 | Planned | +| prod | SRE human attestation + confidence ≥ 0.90 | Planned | +| dr | SRE human attestation + confidence ≥ 0.95 + a disaster-recovery drill reference | Planned | + +- **No staging environment** — the design deliberately removes the "staging is basically prod but not really" anti-pattern. Dev is the only autonomous environment. +- **Separation of duties is enforced** — the QA approver cannot be the prod approver. +- **Timeout discipline** — 1 business day = warn + escalate; 2 business days = auto-freeze + re-submit. + +> **Speaker notes:** Promotion is a workflow choice, not a contract mutation — this matters because it means a promotion can be reviewed as a *diff in the workflow*, not as a rewritten contract. Approach A keeps the single source of truth; Approach B lets teams whose inputs genuinely vary keep that variation explicit and reviewable. For leadership: the DX win is that the contract stays stable across environments; the safety win is that the platform raises the threshold and attestation bar automatically based on the target environment the job declares. The consumer can't bypass the gates — they pick *which* environment to target, and the platform applies the right bar. Be honest about maturity: dev is tested and pilot-ready; qa/prod/dr wiring is planned. + +--- + +## Slide 8 — Safe Decommission + +Story beat: Tearing down is as deliberate as deploying — and just as gated. + +Tearing down a stack is **as deliberate as deploying one.** Testing + +```yaml +uses: acdl/.github/workflows/deploy.yml@v1.8 +with: + contract: .acdl/contract.yaml + mode: decommission + changeRequestId: "CHG0678912" +``` + +A 2-step pipeline with **two SRE human-attestation gates**: + +1. **Validate the change request** — the platform queries the CMDB and asserts the CR is `approved` and matches the consumer repo. No CR, no decommission. +2. **Disable deletion protection** → **SRE approves** → **Zero all counts + destroy** → **a second SRE approves.** + +The per-stack encryption key enters a **grace window** (default 30 days) so encrypted data remains recoverable. The key is permanently deleted only after the window expires. + +> **Speaker notes:** The counter-argument to "deletion protection makes cleanup impossible" is this slide. Decommission is a first-class, gated, two-approval flow — not a lock with no key, and not an ungated `terraform destroy`. For the Head of Infrastructure: the CMDB validation means decommission is auditable, not just possible. + +--- + +## Slide 9 — Self-Service Module Catalog + +Story beat: You don't author infrastructure — you pick from pre-built, security-reviewed building blocks. + +Developers pick from **pre-built, security-reviewed building blocks.** Testing + +- **Primitives** — single-purpose resources (S3, VPC, ECS, IAM, load balancer, container registry, CloudFront, WAF, RDS). Each has documented inputs/outputs, usage, compliance extension points, and versioning. +- **Modules** — composed patterns (a static site with CDN + WAF; a microservice with VPC + ECS + load balancer + registry). +- **Validated examples per module** — `simple.yaml` + `complex.yaml` + variation files, validated against the contract schema in CI. Examples cannot drift from the schema silently. +- **Auto-promotion of patterns** — auto-promoted to the catalog after 3 observed usages. Planned Agentic +- **Compliance extension points** — each module lists where GDPR, SOX, SOC2, DORA controls will wire in. Planned + +> **Speaker notes:** The catalog is what makes "declare intent" practical — you can only declare a module that exists. For leadership: the catalog is the leverage. One well-reviewed module serves every consumer; a fix to the module serves every consumer on the next run. This is the compounding asset. + +--- + +## Slide 10 — The Desired Outcomes + +Story beat: Here's what this delivers to the organization. + +- **Velocity without sacrificing safety.** Speed is in the ergonomics (a simple contract, a one-line `uses:`); safety is in the gates the consumer cannot bypass. +- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation. Encryption, deletion protection, uptime monitoring, policy checks, and evidence are on by construction. +- **Auditability as a byproduct, not a project.** Every production change is traceable to a human attestation and a tamper-evident evidence event — captured during the deploy, not reconstructed for the audit. +- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources. One consumer can never affect another. +- **The bottleneck moves off the platform team's ticket queue.** A merged change progresses through lower environments without a platform engineer joining a thread. The platform team invests in the platform, not in per-deployment hand-holding. +- **Infrastructure as a utility, not a craft.** Teams consume infrastructure, they don't maintain it — and the platform compounds value over time by learning from recurring patterns. +- **A path to the citizen developer.** The same safety envelope that serves a senior engineer will serve a non-technical consumer. Agentic + +> **Speaker notes:** Close on the strategic frame. The platform is not "a CI/CD tool" — it is the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require. Invite questions; the companion deck ("How the Platform Works") covers the internal mechanics in more depth. + +--- + +## Appendix — Contents + +For deep dives — these slides cover details omitted from the main 10. + +1. **A1 — The Citizen Developer Experience** (full) +2. **A2 — No Platform Code, No Cloning** (detail) +3. **A3 — Local Reproducibility** (detail) +4. **A4 — The Road to the North Star** (phased roadmap) +5. **A5 — Glossary** + +> **Speaker notes:** These are backup slides for Q&A. Use them when the audience asks for the detail behind a main-slide claim. Don't walk through them in the main talk unless time permits. + +--- + +## A1 — The Citizen Developer Experience + +A non-technical consumer ships a production deployment **by declaring intent** — without authoring a workflow, a configuration file, or an infrastructure module. Think of this as **vibe coding on a laptop** — the consumer describes what they want; an AI agent turns that into a contract that the platform treats identically to a senior engineer's. - The consumer opens an issue describing what they need (e.g. "a web API for the pricing service"). -- An AI agent maps the intent to a contract referencing a module from the **reviewed skill catalog.** +- An AI agent maps the intent to a contract referencing a module from the **reviewed skill catalog.** Agentic - The contract enters the **same pipeline** and must clear the **same confidence gate** before promotion. **Guardrails that make this safe:** @@ -68,54 +270,15 @@ A non-technical consumer ships a production deployment **by declaring intent** - Skills are **versioned, signed, and reviewed for sensitive data before release** (Infra & Ops owns the review — it is the mandatory release gate). - Agents are **stateless** — all state lives in the platform. The platform does not run the skill blindly; it trusts and **always verifies** on the platform side. - The agent's trace and submission confidence are captured in the contract (`profile: agentic`), so a reviewer can see *how* the contract was produced. -- **Initial skill catalog:** web API, worker, scheduled job, static asset, basic observability bootstrap. *(Catalog is planned; the agentic surface is on the roadmap.)* +- **Initial skill catalog:** web API, worker, scheduled job, static asset, basic observability bootstrap. -> **Speaker notes:** Be honest about maturity: the *mechanism* (agent → contract → same pipeline) is designed and the stub was proven in the v1.0 demo; the full skill catalog and real agent runtime are planned. But the design point matters to leadership now: we are building for a world where more of the org can ship safely, not where more of the org has to become a platform engineer. +Skill catalog + real agent runtime: planned Agentic + +> **Speaker notes:** Be honest about maturity: the *mechanism* (agent → contract → same pipeline) is designed and the stub was proven in the v1.0 demo; the full skill catalog and real agent runtime are planned. The "vibe coding on a laptop" framing is intentional — it meets the citizen developer where they already are, but every submission still passes the same safety envelope. The design point matters to leadership now: we are building for a world where more of the org can ship safely, not where more of the org has to become a platform engineer. --- -## Slide 4 — The Contract - -A 5-line YAML file. This is the entire consumer-facing interface to production. - -```yaml -# .acdl/contract.yaml — a static site -uses: acdl/pipelines/deploy.yaml@v1.6 -module: static-assets -environment: dev -inputs: - bucket_name: my-static-site-assets - region: us-east-1 -``` - -```yaml -# .acdl/contract.yaml — a microservice -uses: acdl/pipelines/deploy.yaml@v1.6 -module: microservice -environment: dev -inputs: - image: my-registry/my-microservice:latest - port: 8080 - env: - LOG_LEVEL: info -``` - -Four fields: - -| Field | Meaning | -|---|---| -| `uses` | The central pipeline, pinned to a versioned tag | -| `module` | A name from the module catalog | -| `environment` | `dev`, `qa`, `prod`, or `dr` | -| `inputs` | The handful of values that vary per deployment | - -An invalid contract (missing field, unknown module, wrong type) **fails fast at validation** with a clear error — not an opaque failure three stages in. - -> **Speaker notes:** The contract is the API. It is deliberately tiny so that it can be reviewed, validated, and audited. For leadership: this is what makes "declare intent" concrete — it's a one-screen file, not a 300-line Terraform root module. - ---- - -## Slide 5 — No Platform Code, No Cloning +## A2 — No Platform Code, No Cloning Consumers `uses:` a **versioned** central workflow. The platform fetches itself at run time. The consumer **never touches platform internals.** @@ -132,186 +295,51 @@ flowchart LR - The runner checks out the consumer repo, then checks out the platform repo into the workspace. - The platform installs its own runtime dependencies. The consumer installs nothing. - The consumer **never clones the platform repo, never invokes platform scripts locally** (optional `--check-only` validation is available but not required for the happy path). +- When the platform ships a fix, every consumer on a floating MAJOR.MINOR tag gets it on their next run — no per-repo upgrade project. -> **Speaker notes:** The Head of Cloud cares about this: there is no "platform code in every consumer repo" problem. When the platform ships a fix, every consumer on a floating MAJOR.MINOR tag gets it on their next run — no per-repo upgrade project. +> **Speaker notes:** The Head of Cloud cares about this: there is no "platform code in every consumer repo" problem. The version-pinned `uses:` line is the *only* coupling, and it's a coupling that updates itself within the line. --- -## Slide 6 — Versioned, Predictable Releases +## A3 — Local Reproducibility -Consumers control **when** they absorb platform improvements. +The entire CI pipeline runs **from the shell**, not just in CI. Testing -- **Floating MAJOR + MINOR tags** (e.g. `@v1.6`) — a consumer on `@v1.6` automatically receives patch updates within the 1.6 line. -- **Semantic versioning with a clear contract:** interface changes → MAJOR, behavior changes → MINOR, lifecycle fixes → PATCH. -- **A consumer can pin to an exact version** for maximum stability, or float on MAJOR only (`@v1`) to absorb new features on their own cadence. -- **Unversioned references (`@main`, bare) are discouraged** — the versioned tag is the only immutability lever a consumer has. -- **Automated release job** computes the next semver on merge to main, creates the tag, and updates the floating tags. *(Testing.)* +- `scripts/run_ci.sh` mirrors the CI pipeline locally — the same three stages (lint → test → check-only) in sequence. +- `scripts/run_platform.sh --check-only` runs the platform **offline** — no AWS, no policy engine, no outbox required. Validates a contract end-to-end before pushing. +- `--plan-only` runs through the infrastructure plan without applying. +- The CI and deploy pipelines are defined by **declarative contracts** (YAML instances validated against JSON Schemas) — a single source of truth that both workflows implement. -> **Speaker notes:** This is the "no surprise upgrades" story. Leadership hears two things: (1) consumers aren't forced to chase the platform, (2) the platform isn't forced to support N forks of every workflow. The versioning discipline is what makes both true. +> **Speaker notes:** This is the "no surprises before you push" story. A consumer can validate their contract offline, run the plan offline, and only push when they're confident. The same declarative contract drives both the local tooling and CI — there's no "works on my machine, fails in CI" gap. --- -## Slide 7 — Instant Feedback +## A4 — The Road to the North Star -Developers see **what the platform is doing**, in real time, in their own run logs. +*Proposed phasing — not formally planned.* -- **Streamed output by default** — the infrastructure plan, policy-check results, and each `PolicyCheckResult` record (severity, rule ID, pass/fail) flow to stdout. *(Testing.)* -- **PR comments after every successful pipeline stage** — a developer always knows where they stand without refreshing a dashboard. *(Testing.)* -- **Clear, explainable halt reasons** — a policy violation, an insufficient confidence signal, or a missing attestation. **Never an opaque, manual-debugging exercise.** -- **A `--quiet` mode** suppresses streaming for log-only contexts. - -> **Speaker notes:** This directly answers "but developers hate platforms that hide what they're doing." The platform is opinionated about *what* runs, not *opaque* about *that* it runs. The PR-comment-after-each-stage pattern is a small thing that compounds into trust. - ---- - -## Slide 8 — Deploy Outputs That Just Work - -After a successful deploy, the developer gets their connection information **without hunting for it** — and without secrets leaking into logs. - -- **Human-readable connection strings** posted as a structured GitHub PR comment / job summary. *(Testing.)* -- **Runtime-injectable values** written to encrypted Parameter Store (`SecureString`, KMS-encrypted, namespaced `/acdl/{env}/{contractId}/{output_name}`). *(Testing.)* -- **No raw secrets in logs** — the platform enforces this by construction. -- **Errors become GitHub issues, automatically** — a failed deploy reports through the platform Lambda, which opens (or comments on) an issue on the platform repo. The consumer's only grant is the onboarding-granted Lambda-invoke permission — no separate `issues: write` scope on the consumer side. *(Testing.)* - -> **Speaker notes:** The "errors become issues" point is a DX win that also helps the platform team — every consumer failure is a tracked, queryable artifact, not a lost log line. The Head of DevOps should hear: the platform closes the feedback loop, it doesn't just push a green/red status. - ---- - -## Slide 9 — Friendly Onboarding - -First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully. - -- When a consumer pipeline runs for the first time and **no environment is bound**, the platform detects this and emits a **user-friendly onboarding prompt** instead of failing opaquely. *(Testing.)* -- The prompt tells the consumer: - 1. That no environment is bound to their repo yet. - 2. What the platform will provision on their behalf (account, network, state, role). - 3. The expected turnaround for the platform team to grant the environment. - 4. How to request an environment. -- The pipeline then **exits without attempting a deployment** — no partial state, no confusing errors. -- **Both onboarding paths end in a sandbox dev submission that must pass the confidence gate** before the consumer is promoted. *(Developer path shipped; citizen developer path planned.)* - -> **Speaker notes:** This looks like a small thing; it's actually a cultural one. The platform's posture is "help me get started," not "you should have known." For the Head of DevOps: this is what drives adoption. Platforms that fail opaquely on first run get routed around. - ---- - -## Slide 11 — Safe Promotion Path - -The contract is environment-agnostic by design. Promotion is **a workflow choice, not a contract edit** — the same contract carries cleanly from dev to qa to prod. The platform raises the bar automatically as the target environment becomes more sensitive. - -**Approach A — One contract, one job per environment.** A single contract is referenced by multiple jobs in the CI workflow; the environment is passed by each job and interpolated at runtime. The contract itself never changes. - -```yaml -# .github/workflows/deploy.yml — one job per environment, one shared contract -jobs: - dev: - uses: acdl/.github/workflows/deploy.yml@v1.6 - with: - contract: .acdl/contract.yaml - environment: dev - qa: - needs: dev - uses: acdl/.github/workflows/deploy.yml@v1.6 - with: - contract: .acdl/contract.yaml - environment: qa - prod: - needs: qa - uses: acdl/.github/workflows/deploy.yml@v1.6 - with: - contract: .acdl/contract.yaml - environment: prod +```mermaid +flowchart LR + P1["Phase 1
    Core platform
    (contract, catalog, evidence)"] --> P2["Phase 2
    Safe promotion
    qa/prod/dr wiring"] + P2 --> P3["Phase 3
    Agentic surface
    (skill catalog + agents)"] + P3 --> P4["Phase 4
    North star
    citizen developer GA"] ``` -**Approach B — One job per environment, environment-specific contracts.** When inputs genuinely differ per environment (different capacity, different config), each job points at its own contract file. The pipeline, policy, and confidence model stay identical. +> **Speaker notes:** This is a proposed phasing, not a formally committed plan — call that out explicitly. Phase 1 is what's tested today. Phase 2 is the next milestone (qa/prod/dr wiring). Phase 3 introduces the agentic surface. Phase 4 is the north star: citizen developer GA on the same safety envelope. Use this only when an audience member asks "how do you get from here to there." -```yaml -jobs: - dev: - uses: acdl/.github/workflows/deploy.yml@v1.6 - with: - contract: .acdl/contract-dev.yaml - qa: - needs: dev - uses: acdl/.github/workflows/deploy.yml@v1.6 - with: - contract: .acdl/contract-qa.yaml - prod: - needs: qa - uses: acdl/.github/workflows/deploy.yml@v1.6 - with: - contract: .acdl/contract-prod.yaml -``` +--- -Whichever approach a team picks, the platform applies the same rising bar: +## A5 — Glossary -| Environment | What the platform adds | +| Term | Meaning | |---|---| -| dev | Confidence ≥ 0.50, fully autonomous | -| qa | QA human attestation + confidence ≥ 0.75 | -| prod | SRE human attestation + confidence ≥ 0.90 | -| dr | SRE human attestation + confidence ≥ 0.95 + a disaster-recovery drill reference | +| **OIDC** | OpenID Connect — federation protocol for short-lived tokens, no long-lived credentials | +| **ABAC** | Attribute-Based Access Control — access scoped by resource tags + repo identity, not roles | +| **CMK** | Customer-Managed Key — per-stack encryption key, 90-day rotation, no shared keys | +| **CMDB** | Configuration Management Database — validates change requests for decommission | +| **RPO** | Recovery Point Objective — RPO = 0 means evidence is written synchronously, no data loss | +| **HITL** | Human-in-the-Loop — deliberate human attestation required for qa/prod/dr environments | +| **VCS** | Version Control System — the git hosting platform (GitHub, Gitea, GitLab) | +| **NFR** | Non-Functional Requirement — encryption, tagging, observability standards | -- **No staging environment** — the design deliberately removes the "staging is basically prod but not really" anti-pattern. Dev is the only autonomous environment. -- **Separation of duties is enforced** — the QA approver cannot be the prod approver. *(Design tested; wiring for qa/prod/dr is planned.)* -- **Timeout discipline** — 1 business day = warn + escalate; 2 business days = auto-freeze + re-submit. - -> **Speaker notes:** Promotion is a workflow choice, not a contract mutation — this matters because it means a promotion can be reviewed as a *diff in the workflow*, not as a rewritten contract. Approach A (one contract, environment passed by the job) keeps the single source of truth; Approach B (environment-specific contracts) lets teams whose inputs genuinely vary keep that variation explicit and reviewable. For leadership: the DX win is that the contract stays stable across environments; the safety win is that the platform raises the threshold and attestation bar automatically based on the target environment the job declares. The consumer can't bypass the gates — they pick *which* environment to target, and the platform applies the right bar. - ---- - -## Slide 12 — Safe Decommission - -Tearing down a stack is **as deliberate as deploying one** — and just as gated. - -```yaml -# Consumer's deploy workflow call -uses: acdl/.github/workflows/deploy.yml@v1.8 -with: - contract: .acdl/contract.yaml - mode: decommission - changeRequestId: "CHG0678912" -``` - -A 2-step pipeline with **two SRE human-attestation gates** *(available today)*: - -1. **Validate the change request** — the platform queries the CMDB and asserts the CR is `approved` and matches the consumer repo. No CR, no decommission. -2. **Disable deletion protection** (resolve with `deletion_protection: false`, plan + apply) → **SRE approves.** -3. **Zero all counts + destroy** (the platform zeroes every scalable count, plan + apply) → **a second SRE approves.** -4. **Confirmation** — the platform confirms the stack is destroyed. - -**After decommission:** - -- The per-stack encryption key enters a **grace window** (default 30 days) so encrypted data remains recoverable. The key is permanently deleted only after the window expires. -- Uptime monitoring is **not** automatically destroyed — it can be left running to watch the decommissioned endpoints go dark, or destroyed separately. - -> **Speaker notes:** The counter-argument to "deletion protection makes cleanup impossible" is this slide. Decommission is a first-class, gated, two-approval flow — not a lock with no key, and not an ungated `terraform destroy`. For the Head of Infrastructure: the CMDB validation means decommission is auditable, not just possible. - ---- - -## Slide 13 — Self-Service Module Catalog - -Developers pick from **pre-built, security-reviewed building blocks** — they don't author infrastructure from scratch. - -- **Primitives** — single-purpose resources (S3, VPC, ECS cluster, ECS service, IAM role, load balancer, container registry, CloudFront, WAF, RDS). Each has documented inputs, outputs, usage, compliance extension points, and versioning. *(Testing.)* -- **Modules** — composed patterns (a static site with CDN + WAF; a microservice with VPC + ECS + load balancer + registry). *(Testing.)* -- **Validated examples per module** — every module ships `simple.yaml` + `complex.yaml` + variation files, validated against the contract schema in CI. Examples cannot drift from the schema silently. *(Testing.)* -- **Auto-promotion of patterns** — a thin-composition layer is auto-promoted to the catalog after 3 observed usages. *(Mechanism planned.)* -- **Compliance extension points** — each module lists where GDPR, SOX, SOC2, DORA controls will wire in. *(Compliance milestone is planned.)* - -> **Speaker notes:** The catalog is what makes "declare intent" practical — you can only declare a module that exists. For leadership: the catalog is the leverage. One well-reviewed module serves every consumer; a fix to the module serves every consumer on the next run. This is the compounding asset. - ---- - -## Slide 14 — The Outcome for Leadership - -What this platform delivers to the organization: - -- **Velocity without sacrificing safety.** The speed is in the ergonomics (a 5-line contract, a one-line `uses:`); the safety is in the gates the consumer cannot bypass. -- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation. Encryption, deletion protection, uptime monitoring, policy checks, and evidence are on by construction. -- **Auditability as a byproduct, not a project.** Every production change is traceable to a human attestation and a tamper-evident evidence event — captured during the deploy, not reconstructed for the audit. -- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources. One consumer can never affect another. -- **The bottleneck moves off the platform team's ticket queue.** A merged change progresses through lower environments without a platform engineer joining a thread. The platform team invests in the platform, not in per-deployment hand-holding. -- **Infrastructure as a utility, not a craft.** The platform abstracts compute, networking, and state. Teams consume infrastructure, they don't maintain it — and the platform compounds value over time by learning from recurring patterns. -- **A path to the citizen developer.** The same safety envelope that serves a senior engineer is the one that will serve a non-technical consumer — expanding who can ship safely without lowering the bar. - -> **Speaker notes:** Close on the strategic frame. The platform is not "a CI/CD tool" — it is the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require. Invite questions; the companion deck ("How the Platform Works") covers the internal mechanics in more depth. \ No newline at end of file +> **Speaker notes:** Keep this slide in your back pocket for the audience member who asks "what does ABAC actually mean?" Don't read it aloud. \ No newline at end of file diff --git a/scripts/sync_to_gl.sh b/scripts/sync_to_gl.sh new file mode 100755 index 0000000..83da69b --- /dev/null +++ b/scripts/sync_to_gl.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +# scripts/sync_to_gl.sh - copy ~/acdl contents to ~/gl/acdl and push. +# +# Copies the ACDL source tree into the GitLab mirror at ~/gl/acdl. +# Hidden files/dirs are NOT copied EXCEPT for .github (so GitLab CI +# workflows stay current) and .gitignore. The terraform/ tree is +# omitted entirely, and .gitignore patterns are honored. The +# destination's existing .git directory is preserved untouched. +# +# After syncing, commits any changes on the current branch with a +# timestamped message and pushes it to its upstream (origin/main). +# +# Run manually: +# bash scripts/sync_to_gl.sh # sync + commit + push +# bash scripts/sync_to_gl.sh -v # verbose (list copied files) +# bash scripts/sync_to_gl.sh --no-push # sync + commit only, no push +# bash scripts/sync_to_gl.sh --dry-run # show what would happen +# SRC=~/acdl DST=~/gl/acdl bash scripts/sync_to_gl.sh +set -euo pipefail + +SRC="${SRC:-$HOME/acdl}" +DST="${DST:-$HOME/gl/acdl}" +VERBOSE=0 +NO_PUSH=0 +DRY_RUN=0 + +for arg in "$@"; do + case "$arg" in + -v|--verbose) VERBOSE=1 ;; + --no-push) NO_PUSH=1 ;; + --dry-run) DRY_RUN=1 ;; + -h|--help) + sed -n '2,21p' "$0" + exit 0 + ;; + *) echo "FAIL: unknown argument: $arg" >&2; exit 1 ;; + esac +done + +fail() { echo "FAIL: $*" >&2; exit 1; } +run() { + if [ "$DRY_RUN" = "1" ]; then + echo " [dry-run] $*" + else + "$@" + fi +} + +[ -d "$SRC" ] || fail "source not found: $SRC" +[ -d "$DST" ] || fail "destination not found: $DST (create it first)" +[ -d "$DST/.git" ] || fail "destination has no .git: $DST/.git (restore it first)" + +echo "=== sync_to_gl ===" +echo "source: $SRC" +echo "destination: $DST" +[ "$NO_PUSH" = "1" ] && echo "mode: sync + commit (no push)" +[ "$DRY_RUN" = "1" ] && echo "mode: dry-run (no changes made)" +echo "" + +# Sanity: refuse if DST is not inside ~/gl or is the same as SRC. +case "$DST" in + "$HOME"/gl/*) : ;; + *) fail "destination must live under ~/gl (got $DST)" ;; +esac +[ "$SRC" != "$DST" ] || fail "source and destination are identical" + +# --- sync (rsync) ----------------------------------------------------------- + +# Build rsync exclude list: every hidden entry in SRC except .github +# and .gitignore. +EXCLUDES=() +for hidden in "$SRC"/.*; do + name="$(basename "$hidden")" + case "$name" in + .|...) continue ;; + .github|.gitignore) continue ;; # keep + esac + EXCLUDES+=("--exclude=/$name") +done + +# Never touch the destination's .git. "protect" makes rsync skip it +# entirely (neither transfer nor delete) even under --delete; this is +# stronger than --exclude, which --delete-excluded would wipe out. +# Drop it from the transfer set too. +EXCLUDES+=("--exclude=/.git") + +# Omit the terraform/ tree entirely. +EXCLUDES+=("--exclude=/terraform") + +# rsync filters: protect .git, then honor per-directory .gitignore +# via dir-merge (:-) semantics so patterns anchor like git does. +FILTERS=( + "--filter=P .git" + "--filter=:- .gitignore" +) + +# Use --delete (prune extras in the synced tree) but NOT --delete-excluded: +# that would wipe destination paths covered by our --exclude rules, which +# is exactly what must NOT happen for .git. +RSYNC_ARGS=(-a --delete) +[ "$VERBOSE" = "1" ] && RSYNC_ARGS+=(-v) + +echo "rsync excludes: ${EXCLUDES[*]}" +echo "rsync filters: ${FILTERS[*]}" +echo "" + +if [ "$DRY_RUN" = "1" ]; then + echo "[dry-run] rsync would run:" + printf ' %q ' rsync "${RSYNC_ARGS[@]}" "${FILTERS[@]}" "${EXCLUDES[@]}" "$SRC/" "$DST/"; echo +else + rsync "${RSYNC_ARGS[@]}" "${FILTERS[@]}" "${EXCLUDES[@]}" "$SRC/" "$DST/" + echo "rsync: OK" +fi + +echo "" + +# --- git commit + push ------------------------------------------------------ + +cd "$DST" + +# Refuse to run inside a merge/rebase/conflict state. +git rev-parse --is-inside-work-tree >/dev/null +git_dir_state() { + local f + for f in MERGE_HEAD CHERRY_PICK_HEAD REVERT_HEAD BISECT_LOG; do + [ -e ".git/$f" ] && return 1 + done + [ -d ".git/rebase-merge" -o -d ".git/rebase-apply" ] && return 1 + return 0 +} +git_dir_state || fail "destination .git is mid-operation (merge/rebase/etc); resolve it then re-run" + +branch="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)" +[ -n "$branch" ] || fail "HEAD is detached; checkout a branch first (got $(git rev-parse --short HEAD))" + +# Stage everything in the working tree (including deletions). +run git add -A + +# Commit only if there is something staged. +if git diff --cached --quiet; then + echo "git: no changes to commit on branch '$branch'" +else + ts="$(date -u +%Y-%m-%d\ %H:%M\ UTC)" + msg="chore: sync from source mirror $ts" + echo "git: committing on branch '$branch'" + [ "$VERBOSE" = "1" ] && git diff --cached --stat + run git commit -m "$msg" +fi + +# Push (current branch to its upstream) unless suppressed. +if [ "$NO_PUSH" = "1" ]; then + echo "git: --no-push set, skipping push" + PUSHED=0 +else + upstream="$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null || true)" + if [ -z "$upstream" ]; then + fail "no upstream configured for branch '$branch'; set one with: git -C $DST branch --set-upstream-to=origin/$branch $branch" + fi + if [ "$DRY_RUN" = "1" ]; then + echo " [dry-run] git push to $upstream" + else + echo "git: pushing '$branch' to $upstream" + git push + echo "git: push OK" + fi +fi + +echo "" +echo "=== sync_to_gl OK ===" +echo "copied $SRC -> $DST" +[ "$DRY_RUN" = "1" ] && echo "(dry-run: nothing actually written or pushed)" +[ "$NO_PUSH" = "1" ] && echo "(no-push: changes committed but not pushed)" +exit 0 \ No newline at end of file