From e4a1e87820ca9d0bd6e145f708d3ad32a85b1025 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 10:44:55 +0200 Subject: [PATCH 1/6] Add deployment capability docs and register in nav Move the deployment capability spec, index, and GitHub + Azure design from MSXOrg/Platform into Capabilities/deployment, converting cross-repo links to relative links and registering the pages in the site navigation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../deployment/designs/github-azure.md | 224 +++++++++++++++++ src/docs/Capabilities/deployment/index.md | 29 +++ src/docs/Capabilities/deployment/spec.md | 232 ++++++++++++++++++ src/zensical.toml | 5 + 4 files changed, 490 insertions(+) create mode 100644 src/docs/Capabilities/deployment/designs/github-azure.md create mode 100644 src/docs/Capabilities/deployment/index.md create mode 100644 src/docs/Capabilities/deployment/spec.md diff --git a/src/docs/Capabilities/deployment/designs/github-azure.md b/src/docs/Capabilities/deployment/designs/github-azure.md new file mode 100644 index 0000000..05f74e9 --- /dev/null +++ b/src/docs/Capabilities/deployment/designs/github-azure.md @@ -0,0 +1,224 @@ +--- +title: GitHub + Azure design +description: How the deployment spec is delivered with GitHub as the deployment platform and Azure as the service provider, using Terraform to compute and apply the approved effect. +--- + +# Deployment — GitHub + Azure design + +This design delivers the [Deployment spec](../spec.md) for one combination: +**GitHub** as the deployment platform and **Azure** (including Microsoft Entra +ID) as the service provider, with **Terraform (open source)** as the change +engine. The computed effect the spec speaks of is a **saved Terraform plan**; +the record of state is a remote Terraform state per environment. + +Another design covers another combination — Azure DevOps with Azure, GitHub with +AWS, GitHub with GitHub — without changing the spec. + +## Approach + +A change is proposed as a pull request. On the pull request the process computes +the effect for **every environment in the promotion path** and shows them all; +approving and merging the pull request is the single approval of the code change +together with all of those effects ([FR2](../spec.md#fr2), [FR3](../spec.md#fr3)). +The merge then runs the promotion, deploying to each environment in order using +the **exact plans that were approved** ([FR4](../spec.md#fr4)), running tests +between environments ([FR7](../spec.md#fr7)) and recording every action +([FR9](../spec.md#fr9)). + +```mermaid +flowchart TB + subgraph Review [Pull request — one approval] + C[Change as code] --> P[Plan per environment\ndev · test · preprod · prod] + P --> R[All plans + tests shown in the PR] + end + R -->|team approves code + all effects, merges| P0 + subgraph Promote [Merge — promotion using approved plans] + P0[Apply approved dev plan] --> T0[Automated tests] --> P1[Apply approved test plan] + P1 --> T1[Automated tests] --> P2[Apply approved preprod plan] + P2 --> T2[Automated tests] --> P3[Apply approved prod plan] + end + P3 --> Rec[Every action recorded] + P0 -.effect no longer valid.-> Stop[Stop · recompute · re-approve] +``` + +### Why this shape + +- **One approval for the whole path.** Because the reviewer sees the effect on + every environment, a single approval is sufficient; the design adds no + per-environment reviewer gate, matching the spec's non-goal. +- **Approved plans, not re-derived ones.** The saved plans reviewed on the pull + request are the artifacts the promotion applies. This trades the convenience of + re-planning at deploy time for the guarantee the spec requires — the deployed + effect equals the approved effect. + +### Alternatives considered + +- **Re-plan at deploy.** Rejected: the deploy would compute its own effect, which + can differ from the approved one — it fails [FR4](../spec.md#fr4). +- **Approve only the source diff, plan at deploy.** Rejected: the reviewer never + sees the effect, failing [FR2](../spec.md#fr2)–[FR3](../spec.md#fr3). +- **A required-reviewer gate on each environment.** Rejected as redundant: the + effects for all environments are already approved together; extra gates add + friction without adding a decision. + +## Components + +| Component | Role | +| --- | --- | +| **Reusable workflow** (`.github/workflows/terraform.yml`, `workflow_call`) | The provider-agnostic core: init, format, validate, plan, render, policy scan, test, apply. | +| **Plan (pull-request) workflow** | For every environment in the path, computes `plan -out`, renders it into the pull request, scans it, and uploads it as the approved artifact. | +| **Promotion (merge) workflow** | Walks the environments in order, applying each approved plan and running tests between them. | +| **Azure identity step** | `azure/login` federated sign-in; sets `ARM_USE_OIDC=true` so the `azurerm` and `azuread` providers use the same token. | +| **State backend** | Azure Storage per environment, with blob-lease locking and encryption at rest — the record of state. | +| **Environments** | GitHub Environments per environment name — the per-environment **identity boundary** and deployment branch policy (not an approval gate). | +| **Break-glass workflow** (`workflow_dispatch`) | The emergency path. | +| **Drift job** (`schedule`) | Periodic `plan -detailed-exitcode` that opens an issue on drift. | + +## The effect is a saved plan + +Terraform's saved plan is what makes "approve the effect, then apply exactly it" +concrete: + +1. `terraform plan -out=tfplan.` computes the effect against that + environment's state and writes it to a file that pins provider versions and + the state's **serial** and **lineage**. +2. `terraform show` renders it for humans (pull-request comment) and as JSON for + policy scanning; `sha256(tfplan.)` is recorded. +3. `terraform apply tfplan.` carries out **exactly** that effect, and + refuses with `Saved plan is stale` if the state advanced — the native backstop + for [FR5](../spec.md#fr5). + +## Review and approval + +The pull-request workflow computes a plan for **each** environment in the +promotion path and makes all of them visible in the pull request, alongside +format, validation, policy, and test results. These are **required checks**, so a +change cannot merge unless every environment's effect computed successfully. A +branch ruleset on `main` requires review (CODEOWNERS for `**/*.tf`), dismisses +stale approvals on new commits, and requires branches to be up to date — so the +shown effects reflect current `main`. Approving and merging is the single +approval the spec requires ([FR3](../spec.md#fr3)); the approved plan artifacts +are retained for the promotion. + +## Promotion using the approved plans + +On merge, the promotion workflow retrieves the plan artifacts approved on the +pull request (keyed to the merge commit) and, for each environment in order: + +1. **Verifies** `sha256(tfplan.)` against the value recorded at plan time, + defeating artifact swap. +2. **Applies** `terraform apply tfplan.` — exactly the approved effect + ([FR4](../spec.md#fr4)). If the plan is stale, the apply fails closed and the + promotion stops; a fresh plan is produced and re-approved + ([FR5](../spec.md#fr5)). +3. **Tests** — runs the environment's automated tests; a failing required test + stops the promotion before the next environment ([FR7](../spec.md#fr7)). + +Deployments against the same state are **serialised** by a concurrency group +keyed per state with `cancel-in-progress: false`, backed by the state lock, and +an in-flight apply is never cancelled ([FR6](../spec.md#fr6)). + +## Identity and access (OIDC) + +- The workflow requests the GitHub OIDC token with `permissions: id-token: write`; + `azure/login` federates on it using the non-secret `client-id`, `tenant-id`, + and `subscription-id` — no client secret exists ([NFR1](../spec.md#nfr1)). +- Azure federated credentials pin the subject claim + `repo:MSXOrg/Platform:environment:`, binding identity to the environment + ([NFR3](../spec.md#nfr3)). +- Two identities per environment: **read-only** for computing the effect and + **write** for applying it, the write identity reachable only from that + environment ([NFR2](../spec.md#nfr2)). +- Entra ID resources use the `azuread` provider on the same federated token — + Entra rides the Azure identity context, no separate secret. Managing GitHub + itself uses the `github` provider with a GitHub App installation token, never a + personal access token. + +## Break-glass path + +The emergency path ([FR8](../spec.md#fr8)) is a `workflow_dispatch` "emergency +deploy" with required inputs `reason`, `incident_id`, target environment, and a +typed confirmation, restricted to authorised operators. It still runs +`plan -out` then `apply tfplan`, so the effect is shown before it deploys; it +records the inputs and logs as an immutable run record, and opens a follow-up +issue to bring the change back through the standard pull-request path. There is +no flag on the standard workflow that skips review. + +## Records and drift + +Every run's rendered plans, apply logs, and test results are retained as run +artifacts and logs, and each apply is recorded in the environment's deployment +history — the audit trail for [FR9](../spec.md#fr9)/[NFR4](../spec.md#nfr4). A +scheduled `plan -detailed-exitcode` job detects drift and opens an issue +([FR10](../spec.md#fr10)). + +## Interface + +```yaml +# .github/workflows/terraform.yml (reusable — workflow_call) +on: + workflow_call: + inputs: + environment: { type: string, required: true } # dev | test | preprod | prod + working-directory: { type: string, required: true } + action: { type: string, default: plan } # plan | apply + terraform-version: { type: string, default: "1.9.x" } +``` + +The core is provider-agnostic; the Azure sign-in is the single service-provider +step. A different service provider (AWS) or deployment platform (Azure DevOps) +reuses this design's shape with its own identity step and is documented as its +own design under [Deployment](../index.md). + +## Security boundaries and threats + +- **Credential exposure** — OIDC only; no static cloud secret exists, and the + default token is read-only. +- **Plan artifact confidentiality** — a saved plan can contain sensitive values + in plaintext. Retention is short, artifacts are restricted, sensitive variables + and outputs are marked, and pull-request comments carry scrubbed renders; on a + public repository plan artifacts are never exposed unscrubbed. +- **Artifact swap** — each apply verifies `sha256(tfplan.)` before applying. +- **Cross-environment escalation** — federated subjects are pinned per + environment; the write identity is reachable only from its environment. +- **Stale apply** — Terraform's serial/lineage guard, plus serialisation and + branch-freshness rules. + +## Testing and operability + +- **Dry run** the full path on `dev`: pull-request plans for every environment, + review, merge, promotion with tests. +- **Exercise the stale path** by mutating state between approval and deploy and + confirming the apply fails closed. +- **Exercise break-glass** end to end and confirm the reconciliation follow-up is + opened and attribution is recorded. + +## Repository hardening + +Third-party actions are pinned to commit SHAs; allowed actions are restricted; +secret scanning with push protection is enabled as defence in depth even under an +OIDC model. + +## Decisions + +One-way-door decisions are recorded here beside the spec, per +[Decision before change](../../../Ways-of-Working/Principles/AI-First-Development.md#decision-before-change). + +- **Approve every environment's effect on the pull request; apply those exact + saved plans on merge.** Chosen over re-plan-at-deploy (does not apply the + approved effect) and per-environment gates (redundant given the effects are + already approved together). Revisit only if a saved plan can no longer be the + review-to-deploy contract. + +## References + +- GitHub — security hardening with OpenID Connect: +- GitHub — configuring OIDC in Azure: +- GitHub — managing environments: +- GitHub — reusing workflows: +- GitHub — control concurrency: +- HashiCorp — automate Terraform with GitHub Actions: +- HashiCorp — `terraform plan` / `terraform apply` (saved plan): · +- Terraform AzureRM provider — OIDC: +- Azure — `azure/login`: +- Microsoft — GitHub Actions + Terraform OIDC on Azure sample: diff --git a/src/docs/Capabilities/deployment/index.md b/src/docs/Capabilities/deployment/index.md new file mode 100644 index 0000000..f2a8a62 --- /dev/null +++ b/src/docs/Capabilities/deployment/index.md @@ -0,0 +1,29 @@ +--- +title: Deployment +description: How a change to managed resources is approved together with its effect and deployed exactly as approved — one spec, one design per deployment platform and service provider. +--- + +# Deployment + +How a change to the resources Platform manages moves from a proposal to live: its +**effect on the resources** is computed and shown, the team approves the code +change together with that effect across every environment it will pass through, +and the approved effect is exactly what is deployed — with every action recorded. + +The **spec** is the durable contract and is deliberately free of any technology. +Each **design** delivers that contract for one combination of **deployment +platform** and **service provider** (for example GitHub with Azure, Azure DevOps +with Azure, GitHub with AWS, or GitHub with GitHub). Adding a combination adds a +design; it never changes the spec. + +## Spec + +| Page | Description | +| --- | --- | +| [Spec](spec.md) | The why and what — a change is approved together with its effect, and the approved effect is exactly what deploys. | + +## Designs + +| Design | Deployment platform | Service provider | Description | +| --- | --- | --- | --- | +| [GitHub + Azure](designs/github-azure.md) | GitHub | Azure | GitHub Actions and Terraform deploy Azure and Entra resources with passwordless identity, approving the code change together with its per-environment effect. | diff --git a/src/docs/Capabilities/deployment/spec.md b/src/docs/Capabilities/deployment/spec.md new file mode 100644 index 0000000..9885a65 --- /dev/null +++ b/src/docs/Capabilities/deployment/spec.md @@ -0,0 +1,232 @@ +--- +title: Spec +description: Requirements for the deployment capability — a change to managed resources is approved together with its effect, and the approved effect is exactly what is deployed. +--- + +# Deployment — Spec + +## Premise + +Platform manages infrastructure through code. A change to that code changes real +resources, and it is only safe when the people approving it approve the +**effect** the change has on those resources — not just the words in the diff. +The deployment capability makes the effect a first-class, reviewable thing: +before a change deploys, its effect on every environment it will pass through is +computed and shown; the team approves the code change **and** that effect +together; and the approved effect is exactly what the deployment carries out. + +This rests on **[Decision before change](../../Ways-of-Working/Principles/AI-First-Development.md#decision-before-change)** — +the review is the decision point, and it decides on the real consequence of the +change, not only its source. + +## Problem and why now + +Reviewing a code change is not the same as reviewing what it does. Two changes +that read identically can have very different effects on live resources +depending on the current state of those resources, and a deployment that +re-derives its own actions can carry out something no one approved. Platform is +the foundation for MSXOrg infrastructure and needs a deployment contract where +the approved effect is the deployed effect, across the full promotion path, with +every action recorded. + +## Users and jobs + +- **A contributor** proposes a change to managed resources and needs to see, and + have others see, exactly what it will do before it is accepted. +- **The reviewing team** decides whether the change and its effect are acceptable + for every environment it will reach. +- **An operator** needs the accepted change to deploy predictably, with a record + of everything that happened, and a controlled way to act in an emergency. + +## Outcomes and impact + +- What is deployed to each environment equals what was approved for that + environment. +- The team approves the effect on resources, not only the code, so surprises at + deploy time are eliminated. +- Every deployment leaves a complete, attributable record of what it did. + +**Impact.** Change-failure rate and time-to-restore improve because approval is +bound to the real effect and every action is recorded; lead time for changes +stays low because the whole approved path is automated. Domain signal: the share +of deployments whose deployed effect equals their approved effect (target 100%). + +## Scope + +- Changes to the resources Platform manages, expressed as code, from proposal + through to deployment across every environment in the promotion path (for + example `dev` → `test` → `preprod` → `prod`). +- The review, approval, testing, and record-keeping around those changes. + +Out of scope: + +- The choice of deployment platform, service provider, and change engine — each + combination is a **[design](index.md)**, not part of this contract. +- Application or runtime release — this capability governs managed resources. +- Provisioning of the state, identity, and trust the deployment relies on, which + are prerequisites the designs assume. + +## Non-goals + +- A separate approval gate per environment. One approval of the change together + with the effect it has on **every** environment in its path covers the whole + promotion; the capability does not add per-environment sign-offs on top of the + agreed change set. +- A bespoke deployment portal or user interface. +- Deploying more than one independent change against the same resources at once. + +## Requirements + +Requirements use [BCP 14](https://www.rfc-editor.org/info/bcp14) keywords. + +### FR1 — A change is expressed as code and proposed for review { #fr1 } + +Every change to managed resources MUST be expressed as code and proposed for +review before it is deployed. A change that reaches resources without review MUST +be possible only through the emergency path ([FR8](#fr8)). + +### FR2 — The effect of a change is computed and shown for review { #fr2 } + +For each environment the change will pass through, the process MUST compute the +change's **effect on the managed resources** — what it creates, updates, and +destroys — and make every one of these effects available during review. +Computing the effect successfully MUST be a condition of acceptance. + +### FR3 — Approval covers the code change together with its effect { #fr3 } + +Acceptance MUST approve the code change **and** its computed effect on every +environment in the promotion path, as one decision. Approving the code alone, +without its effect, MUST NOT satisfy this requirement. + +### FR4 — The approved effect is exactly what is deployed { #fr4 } + +The deployment to each environment MUST carry out the exact effect that was +approved for that environment. The process MUST NOT re-derive the effect between +approval and deployment in a way that could act on resources no one approved. + +### FR5 — Deployment fails closed when the approved effect is no longer valid { #fr5 } + +If the approved effect for an environment is no longer valid for that +environment's current state at deployment time, the deployment MUST stop without +carrying out a divergent effect. Resolution MUST be to recompute the effect and +have it approved again — never to force the invalid one. + +### FR6 — Changes against the same resources are serialised { #fr6 } + +At most one change MUST be deployed against a given set of resources at a time. +Concurrent deployments against the same resources MUST be prevented, and a +deployment in progress MUST NOT be interrupted partway. + +### FR7 — The process runs its automated tests { #fr7 } + +The process MUST run its defined automated tests as part of carrying out a +change, and a failing required test MUST stop the promotion before it reaches the +next environment. + +### FR8 — An audited emergency path exists { #fr8 } + +An emergency path MUST be able to deploy a change without the standard review. +It MUST require a recorded reason and an incident reference, MUST be restricted +to authorised people, MUST still compute and show the effect before it deploys, +and MUST open a follow-up that brings the change back through the standard path. + +### FR9 — Every deployment documents the actions it took { #fr9 } + +Every deployment, standard or emergency, MUST record the actions it performed — +who approved, who deployed, when, against which environment, the effect applied, +and the outcome of its tests — retained as an immutable record. + +### FR10 — Drift is detected and surfaced { #fr10 } + +Divergence between the real resources and the recorded state SHOULD be detected +on an ongoing basis and surfaced for action. + +### NFR1 — No standing secrets { #nfr1 } + +Access to managed resources MUST use short-lived, federated identity. Zero +long-lived credentials or static secrets exist in the repository or its +automation. + +### NFR2 — Least privilege separated by phase { #nfr2 } + +Computing a change's effect MUST use read-only access; deploying it MUST use +write access scoped to the target environment; the two MUST be distinct. + +### NFR3 — Per-environment identity boundary { #nfr3 } + +Each environment MUST have its own access identity, so an effect approved for one +environment cannot be deployed to another. Zero cross-environment deployments. + +### NFR4 — Complete auditability { #nfr4 } + +100% of deployments, including emergency ones, MUST produce a retained, +attributable record ([FR9](#fr9)). + +## Acceptance criteria + +```gherkin +Feature: Deployment approves and applies the effect, not just the code + + Scenario: The whole promotion path is approved at once + Given a change that will pass through dev, test, preprod, and prod + When it is proposed for review + Then the effect on the resources of each of those environments is shown for review + And accepting the change approves the code together with all of those effects + + Scenario: The approved effect is what deploys + Given an accepted change with an approved effect per environment + When it is deployed to an environment + Then the effect carried out equals the effect approved for that environment + And the deployment records who approved, who deployed, and what changed + + Scenario: Resources moved after approval + Given an approved effect for an environment + And that environment's resources have changed since the effect was computed + When the deployment runs + Then it stops without changing resources + And a fresh effect must be computed and approved before any deployment + + Scenario: A required test fails during promotion + Given a change being promoted across environments + When a required automated test fails in one environment + Then the promotion stops before the next environment + And the failure is recorded + + Scenario: Two changes race the same resources + Given two accepted changes targeting the same resources + When both attempt to deploy + Then they deploy one after another, never concurrently + And no deployment is interrupted partway + + Scenario: Emergency change during an incident + Given an authorised person triggers the emergency path with a reason and an incident reference + When the change is deployed + Then its effect is computed and shown before it deploys + And an attributable record is retained + And a follow-up to reconcile the change is opened + + Scenario: No standing secrets + Given the repository and its automation + When they are inspected for credentials + Then no long-lived secret is present + And access is obtained through short-lived, per-environment identity +``` + +## Constraints, assumptions, and dependencies + +- The change is expressed as **code** and its effect can be computed and captured + as an artifact before it is carried out. +- A **review and approval** mechanism records who approved a change and its + effect. +- A **record of state** for each environment exists, against which an effect is + computed and whose change can invalidate a previously approved effect. +- The specifics of **how** — the deployment platform, the service provider, the + change engine, and the identity mechanism — are supplied by a + [design](index.md), not this spec. + +## Where this connects + +- [Designs](index.md) — how these requirements are delivered for a specific + deployment platform and service provider. +- [Documentation Model](../../Ways-of-Working/Documentation-Model.md) — why this spec holds only the why and the what. +- [PR Format](../../Ways-of-Working/PR-Format.md) — the review that approves the change and its effect. diff --git a/src/zensical.toml b/src/zensical.toml index fdc6ac3..e2feb21 100644 --- a/src/zensical.toml +++ b/src/zensical.toml @@ -133,6 +133,11 @@ nav = [ {"Spec" = "Capabilities/downstream-release-propagation/spec.md"}, {"Design" = "Capabilities/downstream-release-propagation/design.md"}, ]}, + {"Deployment" = [ + "Capabilities/deployment/index.md", + {"Spec" = "Capabilities/deployment/spec.md"}, + {"GitHub + Azure design" = "Capabilities/deployment/designs/github-azure.md"}, + ]}, {"VS Code Extension Framework" = [ "Capabilities/vscode-extension-framework/index.md", {"Spec" = "Capabilities/vscode-extension-framework/spec.md"}, From c5940614de195476684cb45af8113953090e07d5 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 10:47:08 +0200 Subject: [PATCH 2/6] Unify Capability and Framework into one spec-and-design model Document framework as an adjective for a capability whose design composes other capabilities, note the Frameworks section is being absorbed into Capabilities, and regenerate the affected index tables. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/docs/Capabilities/index.md | 6 +++++ src/docs/Frameworks/index.md | 22 +++++++++++-------- .../Ways-of-Working/Documentation-Model.md | 7 ++++++ src/docs/index.md | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/docs/Capabilities/index.md b/src/docs/Capabilities/index.md index daab518..1bade54 100644 --- a/src/docs/Capabilities/index.md +++ b/src/docs/Capabilities/index.md @@ -12,6 +12,11 @@ capability's folder. See the [Documentation Model](../Ways-of-Working/Documentation-Model.md) for how spec and design relate and evolve. +A capability whose design **composes other capabilities** is called a +*framework* — an adjective, not a different kind of document. It lives here with +the same spec-and-design shape as any other capability; the standalone +[Frameworks](../Frameworks/index.md) section is being folded into this one. + | Section | Description | @@ -20,6 +25,7 @@ and design relate and evolve. | [Dependency Updates](dependency-updates/index.md) | How a repository's pinned dependencies are kept current and secure through automated, labelled update pull requests. | | [Merge Automation](merge-automation/index.md) | How a pull request's required status checks become the machine-readable signal that drives automated approval and merge — green merges, red holds, nothing bypasses the gate. | | [Downstream Release Propagation](downstream-release-propagation/index.md) | How a release in one repository propagates to the repositories that depend on it, via a delegated agent pull request. | +| [Deployment](deployment/index.md) | How a change to managed resources is approved together with its effect and deployed exactly as approved — one spec, one design per deployment platform and service provider. | | [VS Code Extension Framework](vscode-extension-framework/index.md) | How a VS Code extension is built, tested, versioned, packaged, and published — one GitHub-native pipeline, opt-in from a template and a single settings file. | | [PowerShell on GitHub](powershell-on-github/index.md) | How we make GitHub a first-class platform for PowerShell through reusable modules, actions, and capability gaps we close over time. | diff --git a/src/docs/Frameworks/index.md b/src/docs/Frameworks/index.md index a3d1e56..63bb2e5 100644 --- a/src/docs/Frameworks/index.md +++ b/src/docs/Frameworks/index.md @@ -1,18 +1,22 @@ --- title: Frameworks -description: The complete, end-to-end frameworks the ecosystem ships — opinionated automation a project adopts wholesale to go from source to shipped. +description: Frameworks are capabilities whose design composes other capabilities — being absorbed into Capabilities, documented with the same spec and design. --- # Frameworks -The complete, end-to-end frameworks a project adopts wholesale. Where a -[capability](../Capabilities/index.md) is a single independently versioned thing -the ecosystem builds, a framework composes many of them into one opinionated -pipeline that takes a repository from source to shipped with a single -configuration file. - -Each framework is documented as its own section — an overview plus the pages that -cover how it works, how to use it, and how to configure it. +A *framework* is a [capability](../Capabilities/index.md) whose design **composes +other capabilities** into one opinionated pipeline that takes a repository from +source to shipped with a single configuration file. "Framework" is an adjective +for that kind of capability, not a separate documentation model: each framework +carries the same **spec** and **design** as any other capability. + +This standalone section is being **absorbed into +[Capabilities](../Capabilities/index.md)** — the evidence it should be is already +here: `vscode-extension-framework` lives under Capabilities with a spec and a +design, while the pages below still lack that pair. As each framework gains its +spec and design it moves under Capabilities; until then it is documented as its +own section here. diff --git a/src/docs/Ways-of-Working/Documentation-Model.md b/src/docs/Ways-of-Working/Documentation-Model.md index 8eeff33..892200a 100644 --- a/src/docs/Ways-of-Working/Documentation-Model.md +++ b/src/docs/Ways-of-Working/Documentation-Model.md @@ -26,6 +26,13 @@ user (human or agent) can rely on. It never prescribes implementation. The mechanism, the moving parts, the configuration. Both are durable, and both evolve — neither is a one-time plan. +Everything the ecosystem builds is documented this one way — there is no second +shape. A capability whose **design composes other capabilities** is simply called +a *framework*: "framework" is an adjective for that kind of capability, not a +separate section or a different pair of documents. It carries the same spec and +design as any other capability, and lives beside them under +[Capabilities](../Capabilities/index.md). + Only the detail of a *single change* — the paths touched, the trade-off taken this once — stays out of both, living in the [issue](Issues/Process/Format.md) and the pull request where it belongs. diff --git a/src/docs/index.md b/src/docs/index.md index c5259c7..4380a7a 100644 --- a/src/docs/index.md +++ b/src/docs/index.md @@ -50,7 +50,7 @@ Each area below carries its own index. Start here, read the descriptions, and fo | [Agents](Agents/index.md) | The roles agents play across the ecosystem — authored once as documentation and pointed to from each repository. | | [Coding Standards](Coding-Standards/index.md) | The shared baseline every repository inherits, and the per-language standards that build on it. | | [Capabilities](Capabilities/index.md) | The capabilities the ecosystem builds, each documented by a spec (why and what) and a design (how and what). | -| [Frameworks](Frameworks/index.md) | The complete, end-to-end frameworks the ecosystem ships — opinionated automation a project adopts wholesale to go from source to shipped. | +| [Frameworks](Frameworks/index.md) | Frameworks are capabilities whose design composes other capabilities — being absorbed into Capabilities, documented with the same spec and design. | | [Dictionary](Dictionary/index.md) | Shared vocabulary for the MSX ecosystem — the terms a reader or agent meets across these docs. | From 63b68038fc42323966dee93462a8cfb20cbe1bd6 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 10:49:36 +0200 Subject: [PATCH 3/6] Disable markdownlint MD051 for attr_list anchors The docs use python-markdown attr_list heading anchors like '{ #fr1 }' that markdownlint's MD051 cannot resolve, producing false positives on valid same-page requirement links. Test-DocumentationLink.ps1 is the authoritative fragment validator and already recognises these anchors. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/linters/.markdown-lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index 4c0f98c..a5eb09c 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -21,6 +21,7 @@ MD029: false # Ordered list item prefix MD033: false # Allow inline HTML MD036: false # Emphasis used instead of a heading MD041: false # First line in file should be a top level heading, PULL_REQUEST_TEMPLATE.md is an exception +MD051: false # Link fragments: docs use python-markdown attr_list anchors ({ #fr1 }) that markdownlint can't resolve; Test-DocumentationLink.ps1 validates fragments instead MD060: false # Table column style — content mixes compact and aligned tables ################# From e76fcf89b4e245acb468de6f73d88be0f80b0d15 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 11:04:23 +0200 Subject: [PATCH 4/6] Frame deployment designs as "Deploying from " Rename the design to "Deploying Azure from GitHub" and adopt the service provider / CI/CD platform vocabulary across the deployment spec, index, and design so the two axes of each combination are unambiguous; rename the design file to azure-from-github.md to match. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../{github-azure.md => azure-from-github.md} | 17 +++++++++-------- src/docs/Capabilities/deployment/index.md | 17 ++++++++++------- src/docs/Capabilities/deployment/spec.md | 6 +++--- src/docs/Capabilities/index.md | 2 +- src/zensical.toml | 2 +- 5 files changed, 24 insertions(+), 20 deletions(-) rename src/docs/Capabilities/deployment/designs/{github-azure.md => azure-from-github.md} (94%) diff --git a/src/docs/Capabilities/deployment/designs/github-azure.md b/src/docs/Capabilities/deployment/designs/azure-from-github.md similarity index 94% rename from src/docs/Capabilities/deployment/designs/github-azure.md rename to src/docs/Capabilities/deployment/designs/azure-from-github.md index 05f74e9..9b7dbb1 100644 --- a/src/docs/Capabilities/deployment/designs/github-azure.md +++ b/src/docs/Capabilities/deployment/designs/azure-from-github.md @@ -1,18 +1,19 @@ --- -title: GitHub + Azure design -description: How the deployment spec is delivered with GitHub as the deployment platform and Azure as the service provider, using Terraform to compute and apply the approved effect. +title: Deploying Azure from GitHub +description: How the deployment spec is delivered with Azure as the service provider and GitHub as the CI/CD platform, using Terraform to compute and apply the approved effect. --- -# Deployment — GitHub + Azure design +# Deploying Azure from GitHub This design delivers the [Deployment spec](../spec.md) for one combination: -**GitHub** as the deployment platform and **Azure** (including Microsoft Entra -ID) as the service provider, with **Terraform (open source)** as the change +**Azure** (including Microsoft Entra ID) as the service provider and **GitHub** +as the CI/CD platform, with **Terraform (open source)** as the change engine. The computed effect the spec speaks of is a **saved Terraform plan**; the record of state is a remote Terraform state per environment. -Another design covers another combination — Azure DevOps with Azure, GitHub with -AWS, GitHub with GitHub — without changing the spec. +Another design covers another combination — deploying Azure from Azure DevOps, +deploying AWS from GitHub, deploying GitHub from GitHub — without changing the +spec. ## Approach @@ -166,7 +167,7 @@ on: ``` The core is provider-agnostic; the Azure sign-in is the single service-provider -step. A different service provider (AWS) or deployment platform (Azure DevOps) +step. A different service provider (AWS) or CI/CD platform (Azure DevOps) reuses this design's shape with its own identity step and is documented as its own design under [Deployment](../index.md). diff --git a/src/docs/Capabilities/deployment/index.md b/src/docs/Capabilities/deployment/index.md index f2a8a62..8e3cd01 100644 --- a/src/docs/Capabilities/deployment/index.md +++ b/src/docs/Capabilities/deployment/index.md @@ -1,6 +1,6 @@ --- title: Deployment -description: How a change to managed resources is approved together with its effect and deployed exactly as approved — one spec, one design per deployment platform and service provider. +description: How a change to managed resources is approved together with its effect and deployed exactly as approved — one spec, and one design for each combination of deploying a service provider from a CI/CD platform. --- # Deployment @@ -11,10 +11,13 @@ change together with that effect across every environment it will pass through, and the approved effect is exactly what is deployed — with every action recorded. The **spec** is the durable contract and is deliberately free of any technology. -Each **design** delivers that contract for one combination of **deployment -platform** and **service provider** (for example GitHub with Azure, Azure DevOps -with Azure, GitHub with AWS, or GitHub with GitHub). Adding a combination adds a -design; it never changes the spec. +Each **design** delivers that contract for one combination, named **"deploying +<service provider> from <CI/CD platform>"** so the two parts are +clear: the **service provider** is where the resources live (Azure, AWS, +GitHub), and the **CI/CD platform** is what runs the deployment (GitHub, Azure +DevOps) — for example deploying Azure from GitHub, deploying Azure from Azure +DevOps, deploying AWS from GitHub, or deploying GitHub from GitHub. Adding a +combination adds a design; it never changes the spec. ## Spec @@ -24,6 +27,6 @@ design; it never changes the spec. ## Designs -| Design | Deployment platform | Service provider | Description | +| Design | Service provider | CI/CD platform | Description | | --- | --- | --- | --- | -| [GitHub + Azure](designs/github-azure.md) | GitHub | Azure | GitHub Actions and Terraform deploy Azure and Entra resources with passwordless identity, approving the code change together with its per-environment effect. | +| [Deploying Azure from GitHub](designs/azure-from-github.md) | Azure | GitHub | GitHub Actions and Terraform deploy Azure and Entra resources with passwordless identity, approving the code change together with its per-environment effect. | diff --git a/src/docs/Capabilities/deployment/spec.md b/src/docs/Capabilities/deployment/spec.md index 9885a65..12afc4f 100644 --- a/src/docs/Capabilities/deployment/spec.md +++ b/src/docs/Capabilities/deployment/spec.md @@ -60,7 +60,7 @@ of deployments whose deployed effect equals their approved effect (target 100%). Out of scope: -- The choice of deployment platform, service provider, and change engine — each +- The choice of CI/CD platform, service provider, and change engine — each combination is a **[design](index.md)**, not part of this contract. - Application or runtime release — this capability governs managed resources. - Provisioning of the state, identity, and trust the deployment relies on, which @@ -220,13 +220,13 @@ Feature: Deployment approves and applies the effect, not just the code effect. - A **record of state** for each environment exists, against which an effect is computed and whose change can invalidate a previously approved effect. -- The specifics of **how** — the deployment platform, the service provider, the +- The specifics of **how** — the CI/CD platform, the service provider, the change engine, and the identity mechanism — are supplied by a [design](index.md), not this spec. ## Where this connects - [Designs](index.md) — how these requirements are delivered for a specific - deployment platform and service provider. + service provider and CI/CD platform. - [Documentation Model](../../Ways-of-Working/Documentation-Model.md) — why this spec holds only the why and the what. - [PR Format](../../Ways-of-Working/PR-Format.md) — the review that approves the change and its effect. diff --git a/src/docs/Capabilities/index.md b/src/docs/Capabilities/index.md index 1bade54..96cc28a 100644 --- a/src/docs/Capabilities/index.md +++ b/src/docs/Capabilities/index.md @@ -25,7 +25,7 @@ the same spec-and-design shape as any other capability; the standalone | [Dependency Updates](dependency-updates/index.md) | How a repository's pinned dependencies are kept current and secure through automated, labelled update pull requests. | | [Merge Automation](merge-automation/index.md) | How a pull request's required status checks become the machine-readable signal that drives automated approval and merge — green merges, red holds, nothing bypasses the gate. | | [Downstream Release Propagation](downstream-release-propagation/index.md) | How a release in one repository propagates to the repositories that depend on it, via a delegated agent pull request. | -| [Deployment](deployment/index.md) | How a change to managed resources is approved together with its effect and deployed exactly as approved — one spec, one design per deployment platform and service provider. | +| [Deployment](deployment/index.md) | How a change to managed resources is approved together with its effect and deployed exactly as approved — one spec, and one design for each combination of deploying a service provider from a CI/CD platform. | | [VS Code Extension Framework](vscode-extension-framework/index.md) | How a VS Code extension is built, tested, versioned, packaged, and published — one GitHub-native pipeline, opt-in from a template and a single settings file. | | [PowerShell on GitHub](powershell-on-github/index.md) | How we make GitHub a first-class platform for PowerShell through reusable modules, actions, and capability gaps we close over time. | diff --git a/src/zensical.toml b/src/zensical.toml index e2feb21..a2721e9 100644 --- a/src/zensical.toml +++ b/src/zensical.toml @@ -136,7 +136,7 @@ nav = [ {"Deployment" = [ "Capabilities/deployment/index.md", {"Spec" = "Capabilities/deployment/spec.md"}, - {"GitHub + Azure design" = "Capabilities/deployment/designs/github-azure.md"}, + {"Deploying Azure from GitHub" = "Capabilities/deployment/designs/azure-from-github.md"}, ]}, {"VS Code Extension Framework" = [ "Capabilities/vscode-extension-framework/index.md", From 9d68dfd9cb78e40f9b5172a8212c6488931a5a29 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 12:36:45 +0200 Subject: [PATCH 5/6] State the Frameworks section is retired once absorbed into Capabilities Sharpen the framing so the one-directional consolidation and the eventual removal of the empty section are explicit, matching the end-state acceptance criterion in the tracking issue. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/docs/Frameworks/index.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/docs/Frameworks/index.md b/src/docs/Frameworks/index.md index 63bb2e5..5e405cb 100644 --- a/src/docs/Frameworks/index.md +++ b/src/docs/Frameworks/index.md @@ -12,11 +12,12 @@ for that kind of capability, not a separate documentation model: each framework carries the same **spec** and **design** as any other capability. This standalone section is being **absorbed into -[Capabilities](../Capabilities/index.md)** — the evidence it should be is already -here: `vscode-extension-framework` lives under Capabilities with a spec and a -design, while the pages below still lack that pair. As each framework gains its -spec and design it moves under Capabilities; until then it is documented as its -own section here. +[Capabilities](../Capabilities/index.md)** and will be **removed** once empty — +the consolidation runs one direction, because `capability` is the noun and +`framework` is only an adjective. The evidence it should move is already here: +`vscode-extension-framework` lives under Capabilities with a spec and a design. +As each framework below gains its spec and design it moves under Capabilities; +when both have moved, this section and its navigation entry are deleted. From 6676df65197729eee9152aa0ba36192e5f3476af Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 15:44:41 +0200 Subject: [PATCH 6/6] fix: use hyphenated end-to-end to satisfy textlint terminology Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/docs/Capabilities/deployment/designs/azure-from-github.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/Capabilities/deployment/designs/azure-from-github.md b/src/docs/Capabilities/deployment/designs/azure-from-github.md index 9b7dbb1..e608245 100644 --- a/src/docs/Capabilities/deployment/designs/azure-from-github.md +++ b/src/docs/Capabilities/deployment/designs/azure-from-github.md @@ -191,7 +191,7 @@ own design under [Deployment](../index.md). review, merge, promotion with tests. - **Exercise the stale path** by mutating state between approval and deploy and confirming the apply fails closed. -- **Exercise break-glass** end to end and confirm the reconciliation follow-up is +- **Exercise break-glass** end-to-end and confirm the reconciliation follow-up is opened and attribution is recorded. ## Repository hardening