diff --git a/bootstrap/AGENTS.template.md b/bootstrap/AGENTS.template.md index 94eb063..97c923e 100644 --- a/bootstrap/AGENTS.template.md +++ b/bootstrap/AGENTS.template.md @@ -148,6 +148,19 @@ In a repository, its root `AGENTS.md` names the host and organization and lists Clear task language may shortcut the index trail: `Review this PR ` enters Review, `Make this issue ` enters Define, and `Implement ` enters Implement. The linked documentation owns each procedure; this file does not define a separate agent or skill. +## Interactions + +Some phrases operate on the session rather than on the work, and each one resolves to a procedure defined in the canonical Ways of Working: + +| Phrase | Means | +| --- | --- | +| `wrap up` | The session is ending — scan for untracked work and land each item in its proper artifact. | +| `park` | Move a tangent into an issue in the repository that owns it, then resume the original task. | +| `triage` | Classify and route an item without starting implementation. | +| `handoff` | Bring the artifacts to a state another participant can resume from. | + +Read `Ways-of-Working/Session-Interactions.md` in the canonical docs for what each one does. This table is a route, not a definition. + ## Work in the selected repository 1. Read its `README.md` to understand the repository and its build. diff --git a/src/docs/Capabilities/agentic-development/advisory-agents.md b/src/docs/Capabilities/agentic-development/advisory-agents.md new file mode 100644 index 0000000..122448a --- /dev/null +++ b/src/docs/Capabilities/agentic-development/advisory-agents.md @@ -0,0 +1,107 @@ +--- +title: Advisory Agents +description: The pattern for automation that analyses work and publishes its conclusion as advice, without deciding, relabelling, or committing. +--- + +# Advisory Agents + +Some useful automation produces a **judgement** rather than a change: whether a pull request +looks ready, whether a specification covers its requirements, whether a change carries risk +its description does not mention. The judgement is worth having and cannot be expressed as a +passing or failing check, because it is not a fact — it is an opinion, and opinions can be +wrong. + +The advisory pattern exists so that such automation can be useful without becoming +authoritative. + +## Advice, not authority + +An advisory agent MUST publish its conclusion as advice and MUST NOT be the thing that +decides. + +Concretely, it MUST NOT: + +- Commit to the branch it is advising on. +- Merge, close, or approve the artifact it is advising on. +- Overwrite a decision a human has recorded. +- Re-apply a decision a human has changed. + +The last two are the ones that get violated by accident. An agent that sets a label on every +run will faithfully undo the maintainer who corrected it, and it will do so without malice +and without noticing. From the maintainer's side, the correction simply does not stick, and +the reasonable conclusion is that the automation is broken and should be turned off. + +So the constraint is behavioural, not just permissive: it is not enough that a human *can* +override the agent. Overriding it MUST be final. + +## Seed once, never relabel + +Where an advisory agent expresses its conclusion as a label, it MUST apply that label only +when no label from the same set is present, and MUST leave the set alone thereafter. + +This gives the agent exactly one turn to speak. It supplies an initial assessment when there +is none — which is the case where the automation adds most, because the alternative is +nothing — and from then on the field belongs to whoever curates it. + +The rule is what makes the agent safe to run repeatedly. Combined with idempotence, it means a +re-run on an artifact a human has since touched changes nothing at all. + +## Idempotent by construction + +An advisory agent MUST be safe to run repeatedly on the same artifact. + +It will be: events fire more than once, workflows are re-run, and an artifact under active +review is re-examined many times. An agent whose output accumulates turns a busy pull request +into an unreadable one, and the noise costs more than the advice is worth. + +In practice that means an agent updates its previous conclusion in place rather than adding a +new one, and says nothing when it has nothing new to say. Silence at steady state is a +feature. + +## Triggering late + +An advisory agent SHOULD trigger when work is declared ready rather than on every change. + +Advice on unfinished work is mostly advice about the unfinished parts, and a contributor +learns to ignore it — after which the one useful comment is ignored too. Waiting until the +author says the work is ready both makes the advice relevant and makes it clear what it is +about. + +Where an agent must run earlier, it SHOULD scope its advice to what is stable, and MUST NOT +present provisional findings as conclusions. + +## Advice is legible + +An advisory agent's output MUST make clear what it is: which agent produced it, what it +examined, and that it is advice rather than a gate. + +An unattributed conclusion is indistinguishable from a requirement, and a reviewer who cannot +tell the difference either treats optional advice as blocking or treats a real constraint as +optional. Naming the source also makes the agent's own failures diagnosable: advice that is +consistently wrong is a fixable bug in the agent, but only if it is traceable to the agent. + +Where advice rests on a rule, it MUST cite the documentation that carries the rule, so the +reviewer can check the rule rather than trusting the agent's summary of it. An agent MUST NOT +introduce a requirement that no documentation states — if the rule is real it belongs in +documentation, and if it is not, the agent is inventing policy. + +## Composition + +Several advisory agents MAY examine the same artifact, and MUST NOT depend on each other's +output or on the order in which they run. + +Independence is what keeps them cheap to add and remove. A chain of advisors is a pipeline +with failure modes, and a pipeline whose stages are opinions has failure modes nobody can +debug. + +Where two advisors disagree, both conclusions stand and the human resolves them. That is not +a defect in the design; disagreement between two opinions is information, and suppressing it +would mean picking a winner arbitrarily. + +## Where this connects + +- [Spec](spec.md#requirements) — the requirement that advice and authority are separate. +- [Agent Interaction](agent-interaction.md) — the artifacts an advisory agent publishes onto. +- [Automation Labels](../../Ways-of-Working/Automation-Labels.md) — label ownership, which is what makes seed-once enforceable. +- [Review Etiquette](../../Ways-of-Working/Review-Etiquette.md) — the human review the advice feeds into. +- [AI-First Development](../../Ways-of-Working/Principles/AI-First-Development.md#4-eyes-or-n-eyes-principle) — why an automated reviewer adds eyes rather than replacing them. diff --git a/src/docs/Capabilities/agentic-development/agent-interaction.md b/src/docs/Capabilities/agentic-development/agent-interaction.md new file mode 100644 index 0000000..098f3ce --- /dev/null +++ b/src/docs/Capabilities/agentic-development/agent-interaction.md @@ -0,0 +1,111 @@ +--- +title: Agent Interaction +description: How humans and agents coordinate through issues, labels, and pull requests, and why intent and implementation are kept in separate artifacts. +--- + +# Agent Interaction + +Once more than one participant works a repository — several people, several agents, or a mix +— they need somewhere to coordinate. The tempting answer is a conversation: a chat, a thread, +a prompt history. The problem with a conversation is that it is not part of the repository. +It cannot be reviewed, it cannot be queried, and it disappears when the session does. + +The platform already provides durable coordination artifacts. This page states how the +framework uses them. + +## Coordination happens on artifacts + +All coordination between humans and agents MUST happen through platform artifacts — issues, +labels, and pull requests — rather than through any channel that leaves no trace in the +repository. + +The rule follows from +[everything as code](../../Ways-of-Working/Principles/Engineering-Practices.md#everything-as-code): +if the decision is not in the repository, the repository does not record why it looks the way +it does. It also follows from practical asymmetry — an agent's session ends and takes its +context with it, while an issue persists and can be picked up by a different agent, a +different runtime, or a person, weeks later. + +| Artifact | What it carries | Why it, specifically | +| --- | --- | --- | +| **Issue** | The intent: what is wanted, why, and what "done" means | Durable, addressable, and independent of who acts on it | +| **Label** | A discrete decision or state transition | Machine-readable, so automation can react without parsing prose | +| **Pull request** | The proposed implementation, and the negotiation over it | Reviewable line by line, and revertible as one unit | +| **Comment** | Reasoning, questions, and advice attached to its subject | Stays with the artifact it concerns rather than in a separate stream | + +## Two artifacts, two questions + +Intent MUST be recorded separately from implementation. The issue states *what* is wanted and +*why*; the pull request proposes *how*. + +The separation earns its keep at rejection. When a single artifact holds both, discarding a +bad implementation discards the reasoning that motivated it, and the next attempt starts from +nothing. When they are separate, the pull request closes and the issue stands — still stating +what is needed, now with a documented approach that did not work. + +It also puts each question in front of the right reviewer. Whether something *should* be done +is a question about priorities and fit; whether an implementation is *correct* is a question +about the code. These are different judgements, often made by different people, and merging +them into one thread means the cheaper question crowds out the harder one. + +```mermaid +flowchart LR + intent["Issue
what and why"] --> impl["Pull request
how"] + impl -->|"rejected"| intent + impl -->|"accepted"| merged["Merged"] +``` + +The consequence is a rule about creation order: an issue exists before the work that resolves +it. Where a change is genuinely trivial, the pull request MAY stand alone, but it then carries +its own statement of intent, because something has to. + +## Labels are the control channel + +Where automation must be told something, it MUST be told with a label rather than with prose. + +Prose is where humans express nuance, which is exactly what makes it a poor instruction to a +machine: parsing it means guessing, and a guess about whether a maintainer approved something +is a guess with consequences. A label is unambiguous, appears in the artifact's timeline with +who applied it and when, and can be required or forbidden by a rule. + +Labels used for coordination MUST follow the organization's +[label vocabulary](../../Ways-of-Working/Automation-Labels.md), so that a label's owner and +meaning are knowable without reading the workflow that consumes it. Automation MUST ignore +labels it does not own. + +## An agent is a participant, not an authority + +An agent operating on these artifacts MUST do so under the same rules as any other +participant. + +That means it opens issues and pull requests rather than pushing to protected branches, its +changes are reviewed, and its conclusions are advice until someone acts on them. The +[four-eyes principle](../../Ways-of-Working/Principles/AI-First-Development.md#4-eyes-or-n-eyes-principle) +does not weaken because one pair of eyes is automated; an agent reviewing an agent is +[advisory](advisory-agents.md), and human authority over the merge remains. + +The symmetry is deliberate. A process that gave agents a privileged path would have two sets +of rules, and the agent path would be the one nobody audits. + +## Handover is a state, not a message + +Work passed from one participant to another MUST be handed over through the artifact's own +state — its labels, its assignment, its review status — and MUST NOT depend on a message +having been delivered. + +An agent that finishes its part and describes the next step in a comment has produced +something a human must read and act on. An agent that finishes its part and moves the +artifact into the state the next stage reacts to has produced something the process picks up. +The first is a notification; the second is a handover. + +This is what allows a chain of work to survive interruption. Any participant can determine +what happens next by looking at the artifact, without reconstructing a conversation. + +## Where this connects + +- [Spec](spec.md#requirements) — the requirement that coordination happens on durable artifacts and that intent is separable from implementation. +- [Advisory Agents](advisory-agents.md) — how an agent publishes judgement onto these artifacts without taking authority. +- [Automation Labels](../../Ways-of-Working/Automation-Labels.md) — the label vocabulary and ownership rules. +- [Workflow](../../Ways-of-Working/Workflow.md) — the stages these artifacts move through. +- [Issue Format](../../Ways-of-Working/Issues/index.md) — what an issue states. +- [PR Format](../../Ways-of-Working/PR-Format.md) — what a pull request states. diff --git a/src/docs/Capabilities/agentic-development/conformance.md b/src/docs/Capabilities/agentic-development/conformance.md new file mode 100644 index 0000000..f0ce149 --- /dev/null +++ b/src/docs/Capabilities/agentic-development/conformance.md @@ -0,0 +1,112 @@ +--- +title: Conformance +description: What a repository must provide to be conformant with the agentic development framework, what it may add, and the duplication checks that keep the router thin. +--- + +# Conformance + +A framework that cannot be checked is a preference. This page states what conformance to +agentic development means for a single repository: the baseline it MUST provide, what it MAY +add, and the conditions that indicate it has drifted. + +The checks are deliberately structural. Whether a repository's documentation is *good* is a +review question; whether its agent context resolves correctly is a property that can be +determined by looking. + +## The mandatory baseline + +A conformant repository MUST provide all of the following. + +| Requirement | Conformant when | +| --- | --- | +| **A router agent file** | The repository root holds a single agent instruction file, and it routes rather than instructs ([design](design.md#pointer-files)) | +| **Reading order** | The router states the order in which context is read, from repository-local to organization-canonical | +| **Client routes** | Every supported runtime's expected instruction path exists and resolves to the router, carrying no content of its own ([client behavior](design.md#client-behavior)) | +| **Canonical coordinates** | The router names the organization's canonical documentation and memory locations, so context is reachable without prior knowledge | +| **Freshness** | Canonical context is refreshed at the start of every session, in every runtime ([refresh hooks](design.md#refresh-hooks)) | +| **Precedence** | The router states that local files never override a standard and that memory never overrides documentation | + +The baseline is small on purpose. Every item is something an agent needs before it can find +anything else; nothing on the list is a judgement about how the repository should be +documented. + +## The router stays thin + +The router MUST NOT restate a standard. + +This is the check that decays fastest, because adding one useful line to the router is always +easier than finding where that line belongs. A router that has grown into a summary is the +worst of both outcomes: it is not authoritative, so it may be wrong, and it is convenient, so +it is what gets read. + +A repository is non-conformant where any of these hold: + +- The router contains a rule that also appears in organization documentation. +- The router contains a rule that appears nowhere else — meaning documentation is missing, and + the router is standing in for it. +- A client route contains instructions rather than a pointer to the router. +- A path-scoped rule file restates something the router or a standard already says. +- A named intent carries a copy of the procedure it invokes + ([plugin distribution](plugin-distribution.md)). + +Each of these is the same fault: one fact in two places, which is +[one fact one place](../../Ways-of-Working/Documentation-Model.md) violated, and which +resolves in whichever direction the reader happens to look first. + +The remedy is always the same, and it is not deletion of the content: move the content to the +layer that owns it, then reduce the local file to a pointer. + +## What a repository may add + +Beyond the baseline, a repository MAY add: + +- **Path-scoped rules**, for a caveat that is genuinely local to a directory and cannot be + stated as a standard. +- **Runtime adapters**, for a client the organization has not yet standardized on, provided the + adapter is a route and adds no content. +- **Named intents**, for recurring workflows, provided they remain pointers. +- **Repository-specific documentation**, which is normal and expected — the constraint is on + restating standards, not on documenting the repository. + +An addition MUST NOT introduce a requirement. Where local practice differs from a standard, +the resolution is to change the standard or to record an +[exemption](../repository-governance/spec.md#exemption), not to encode the difference where +only agents will read it. + +## Levels + +Conformance is graded, so that a repository can be positioned honestly rather than being +either compliant or not. + +| Level | Meaning | +| --- | --- | +| **Baseline** | Every mandatory item is present; an agent can resolve context correctly from a cold start | +| **Consistent** | Baseline, plus no duplication findings: no local file restates a standard | +| **Uniform** | Consistent, plus every supported runtime resolves identically, and the shared tool layer is declared in each ([MCP servers](mcp-servers.md)) | + +Only **Baseline** is required. The higher levels describe a repository whose agent context +needs no per-runtime knowledge to work with, which is the state the framework is aiming at. + +## Checking + +Conformance MUST be checkable without running an agent. + +The baseline is a set of file and content properties, and duplication is detectable by +comparing local text against the standards it might be restating. Both are amenable to the +same [drift detection](../repository-governance/design.md#drift-detection-and-reconciliation) +the organization already applies to repository structure, and a conformance finding MUST name +its remedy for the same reason every other finding does: a finding that only reports a problem +becomes a number people learn to ignore. + +Determining conformance by asking an agent whether it understood the repository MUST NOT be +treated as a check. The answer is generated from the same context whose adequacy is in +question. + +## Where this connects + +- [Spec](spec.md) — the requirements this checklist measures against. +- [Design](design.md) — the mechanisms each baseline item refers to. +- [MCP Servers](mcp-servers.md) — the shared tool layer the **Uniform** level requires. +- [Plugin Distribution](plugin-distribution.md) — the pointer discipline the duplication checks apply to intents. +- [Repository Governance](../repository-governance/index.md) — the drift detection and exemption machinery conformance checking reuses. +- [Documentation Model](../../Ways-of-Working/Documentation-Model.md) — one fact one place, which the duplication checks enforce. diff --git a/src/docs/Capabilities/agentic-development/design.md b/src/docs/Capabilities/agentic-development/design.md index 05bfbd6..ccee5ec 100644 --- a/src/docs/Capabilities/agentic-development/design.md +++ b/src/docs/Capabilities/agentic-development/design.md @@ -23,9 +23,11 @@ Current project scopes follow the same shape: | Host | Organization | Docs | Memory | | --- | --- | --- | --- | -| `dnb.ghe.com` | `AI-Platform` | `AI-Platform/docs` | `AI-Platform/memory` | | `github.com` | `MSXOrg` | `MSXOrg/docs` | `MSXOrg/memory` | | `github.com` | `PSModule` | `PSModule/docs` | `PSModule/memory` | +| `` | `` | `/docs` | `/memory` | + +The last row is the general case: any adopting organization on any GitHub host — public or an enterprise instance — plugs into the same shape without changing the framework. ## Repository roles @@ -135,13 +137,13 @@ flowchart TD pointer --> locate["Resolve host, org, docs, and memory roots"] locate --> host{"Which project scope?"} - host -->|"dnb.ghe.com / AI-Platform"| aip["AI-Platform context"] host -->|"github.com/MSXOrg"| msx["MSXOrg context"] host -->|"github.com/PSModule"| psmodule["PSModule context"] + host -->|"any adopting org"| other["<host>/<org> context"] - aip --> refresh["Refresh selected docs + memory
stop unless exactly synchronized"] - msx --> refresh + msx --> refresh["Refresh selected docs + memory
stop unless exactly synchronized"] psmodule --> refresh + other --> refresh refresh --> repo["Read README, CONTRIBUTING,
and local docs"] repo --> path["Apply path-scoped local rules"] path --> orgdocs["Read organization
documentation"] @@ -223,6 +225,40 @@ The bootstrap clones missing repositories and fetches every existing context rep MSXOrg is the default project. Additional projects plug in a name, relative workspace path, docs URL, and memory URL. For example, PSModule can use `projects/PSModule/{docs,memory}` beneath the same workspace while reusing the identical synchronization and validation path. Repository agent files retain this small coordinate block because it is required before project documentation can be reached; the reusable bootstrap behavior remains central. +## Refresh hooks + +The freshness gate is only worth as much as the last time it ran. A workspace bootstrapped +once is current at that moment and progressively less so afterwards, and an agent reading a +week-old clone reads a standard that has since changed while believing it is canonical. + +So the refresh runs at the **start of every session**, not once per machine. What differs +between runtimes is where the trigger hangs, never what it does: + +| Runtime shape | Lifecycle point | How the refresh attaches | +| --- | --- | --- | +| Local interactive agent | Session start | A session-start hook in the runtime's own configuration invokes the bootstrap before the first turn. | +| Hosted or remote agent | Environment setup | The environment's setup steps run the bootstrap while the workspace is being prepared, so the agent starts against fresh context. | +| Review-time agent | Pull request event | Instructions are read from the pull request's head branch, so freshness follows the branch under review rather than a local clone. | +| Batch or scheduled agent | Job start | The job's first step is the bootstrap; a scheduled run has no earlier lifecycle point to rely on. | + +Each of these is one **declaration** of the same behaviour. The bootstrap is a single +idempotent operation — clone what is missing, fetch what exists, verify each clone is clean, +on the remote default branch, and exactly equal to the fetched head — and a hook does +nothing but call it at the right moment. That is what makes a new runtime cheap to support: +the work is finding its lifecycle point, not writing another refresh. + +The refresh MUST be idempotent, because it runs far more often than it changes anything. A +hook that is expensive or noisy when everything is already current gets disabled, and a +disabled hook is worse than no hook, because the workspace still looks bootstrapped. + +Where a runtime offers no lifecycle point at all, the refresh MUST be invoked explicitly +before context is read. It MUST NOT be skipped on the grounds that the workspace was +bootstrapped recently; "recently" is not a state the agent can observe, and the gate exists +precisely to replace that judgement with a check. + +Each shape's obligations beyond the refresh — its entry file, tool declaration, and identity — +are set out in [Runtime Integration](runtime-integration.md). + ## Memory writing rules Agents write memory only when a lesson is likely to matter again. Good memory is: @@ -233,7 +269,13 @@ Agents write memory only when a lesson is likely to matter again. Good memory is - free of secrets, credentials, and private personal notes; - updated or removed when it becomes wrong. -Session-specific notes stay out of durable memory unless they become reusable project knowledge. +Memory is written by **horizon**: organization-wide lessons and per-repository facts are +durable and shared, while notes about the task in hand are session-scoped and never pushed +([memory repository template](memory-template.md#memory-has-three-horizons)). A session note +becomes durable only by being deliberately promoted and rewritten as a statement of fact. + +Durable memory is committed and pushed as it is written, one commit per discrete lesson, so +that nothing depends on a session ending cleanly. ## Client behavior @@ -277,6 +319,12 @@ Because Copilot code review reads the head branch, a pull request that changes ` - [Spec](spec.md) — the requirements this design delivers. - [Memory Repository Template](memory-template.md) — the concrete scaffold every organization's canonical `memory` repository instantiates. +- [MCP Servers](mcp-servers.md) — the shared tool layer every runtime declares in its own format. +- [Plugin Distribution](plugin-distribution.md) — how named intents are packaged and kept pointer-based. +- [Runtime Integration](runtime-integration.md) — what each runtime shape supplies, and why it never supplies process. +- [Agent Interaction](agent-interaction.md) — how agents and humans coordinate through platform artifacts. +- [Advisory Agents](advisory-agents.md) — agents that produce advice rather than commits. +- [Conformance](conformance.md) — the checklist a repository is measured against. - [Agentic Development](../../Ways-of-Working/Agentic-Development.md) — the way-of-working standard this framework implements. - [Documentation Model](../../Ways-of-Working/Documentation-Model.md) — why spec and design are split. - [README-Driven Context](../../Ways-of-Working/Readme-Driven-Context.md) — why local repository context remains the front door. diff --git a/src/docs/Capabilities/agentic-development/index.md b/src/docs/Capabilities/agentic-development/index.md index f12268e..8cd8142 100644 --- a/src/docs/Capabilities/agentic-development/index.md +++ b/src/docs/Capabilities/agentic-development/index.md @@ -16,5 +16,11 @@ A repository adopts the framework by carrying a short router and the client rout | [Spec](spec.md) | Requirements for refresh-first, index-first agentic development through canonical documentation, memory, and thin pointers. | | [Design](design.md) | How the agentic development framework is built — OKF documentation, org memory, thin repo pointers, and deterministic context resolution. | | [Memory Repository Template](memory-template.md) | The concrete, copy-pasteable scaffold every organization's memory repository instantiates, and why it deliberately breaks from the Repository Standard. | +| [MCP Servers](mcp-servers.md) | How one logical set of tool servers is defined once and declared by every runtime in its own format, so a documented procedure does not depend on which client runs it. | +| [Runtime Integration](runtime-integration.md) | How a runtime is wired into the framework — the entry file it reads, the lifecycle point its refresh attaches to, the permissions it needs, and what a new runtime must supply to be supported. | +| [Plugin Distribution](plugin-distribution.md) | How recurring workflows are packaged as named intents that point to canonical documentation, and why a packaged shortcut never carries a copy of the procedure. | +| [Agent Interaction](agent-interaction.md) | How humans and agents coordinate through issues, labels, and pull requests, and why intent and implementation are kept in separate artifacts. | +| [Advisory Agents](advisory-agents.md) | The pattern for automation that analyses work and publishes its conclusion as advice, without deciding, relabelling, or committing. | +| [Conformance](conformance.md) | What a repository must provide to be conformant with the agentic development framework, what it may add, and the duplication checks that keep the router thin. | diff --git a/src/docs/Capabilities/agentic-development/mcp-servers.md b/src/docs/Capabilities/agentic-development/mcp-servers.md new file mode 100644 index 0000000..bfa0bdc --- /dev/null +++ b/src/docs/Capabilities/agentic-development/mcp-servers.md @@ -0,0 +1,104 @@ +--- +title: MCP Servers +description: How one logical set of tool servers is defined once and declared by every runtime in its own format, so a documented procedure does not depend on which client runs it. +--- + +# MCP Servers + +An agent that can only read files is limited to what the repository already knows. Useful +work needs the platform itself: opening an issue, reading a pull request, querying a +tracker, fetching a reference. Those capabilities reach the agent through **tool servers** +declared by the runtime — the [Model Context Protocol](https://modelcontextprotocol.io/) is +the interface they present. + +The design question is not which servers exist. It is whether they are the *same* servers +everywhere. + +## One logical layer + +The set of tool servers available to agents MUST be defined once, as a property of the +organization rather than of a runtime. + +The alternative is what happens by default: each runtime accumulates the servers whoever +configured it happened to need. The result is a documented procedure that works in one +client and fails in another, and the failure is not legible — the agent does not report +"this client lacks that tool", it reports that it could not do the thing. A contributor then +concludes the procedure is wrong. + +A shared layer makes capability a fixed premise. When documentation says an agent opens an +issue, that instruction holds for every agent, because the server that opens issues is part +of the layer rather than part of someone's local setup. + +| Server kind | Why the layer includes it | +| --- | --- | +| **Source platform** | Issues, pull requests, reviews, and repository metadata are the artifacts the process is defined in terms of ([agent interaction](agent-interaction.md)) | +| **Issue tracker** | Where planning lives, when it lives outside the source platform | +| **Knowledge base** | Where reference material lives, when it is not in a `docs` repository | +| **Organization-specific services** | Whatever else the organization's procedures name | + +The kinds are stable; the specific services are the organization's choice. What matters is +that the choice is made once. + +## Same contract, different declaration syntax + +Every runtime MUST declare the same logical set, in its own native configuration format. + +Runtimes disagree about where server configuration lives, what the file is called, and how a +server's transport and credentials are expressed. None of that is worth fighting. What is +worth insisting on is that the *contract* — which servers, offering which capabilities — is +identical, so translation is mechanical: + +```text +one logical server set + │ + ├──> runtime A: its own configuration file, its own schema + ├──> runtime B: its own configuration file, its own schema + └──> runtime C: its own configuration file, its own schema +``` + +This is the same relationship the framework already uses for instructions, where +[client routes](design.md#client-behavior) differ in filename and are identical in content. A +declaration is a route to a capability, and a route holds nothing that can drift. + +The consequence is a rule about additions: adding a server means adding it to the logical set +and then to every runtime's declaration. A server declared in one runtime only is a local +convenience, and MUST NOT be relied on by documentation. + +## Credentials are not part of the layer + +The layer defines *which* servers and *what* they offer. It MUST NOT carry credentials. + +Authentication is per-operator and per-environment: a local agent authenticates as the +person running it, a hosted agent as the identity its environment grants. Both reach the +same servers with different permissions, and that difference is correct — it is +[least privilege](../../Ways-of-Working/Principles/Purpose-and-Direction.md#least-privilege) +working as intended. A server declaration that embedded a credential would either leak it or +force every agent to share one identity. + +So a declaration names the server and how to reach it, and resolves its credential from the +environment. An agent that cannot authenticate to a server MUST fail visibly rather than +silently proceeding without the capability, because a procedure that assumed the capability +will otherwise produce a confusing partial result. + +## Tools do not replace documentation + +A tool server changes what an agent *can do*. It MUST NOT change what the agent is *supposed +to do*. + +The distinction matters because tool descriptions are themselves instructions, and a +capable server is tempting to treat as guidance: it knows how to open an issue, so let it +decide what an issue should contain. That inverts the framework — the procedure for opening +an issue is documentation, and the server is how the documented result is achieved. + +A server MUST NOT be the source of a process rule, for the same reason a +[skill or command MUST NOT define a workflow stage](spec.md#requirements): a rule that lives +in a tool is a rule nobody reviews, and it disagrees with the documentation the moment either +one changes. + +## Where this connects + +- [Spec](spec.md#requirements) — the requirement that the tool layer is defined once and declared per runtime. +- [Design](design.md#client-behavior) — the same route-not-copy relationship applied to instruction files. +- [Plugin Distribution](plugin-distribution.md) — named intents, which use tools but do not define procedure either. +- [Agent Interaction](agent-interaction.md) — the platform artifacts the source-platform server exists to operate on. +- [Conformance](conformance.md) — how a repository's runtime declarations are checked against the shared set. diff --git a/src/docs/Capabilities/agentic-development/memory-template.md b/src/docs/Capabilities/agentic-development/memory-template.md index c3cc042..e96a733 100644 --- a/src/docs/Capabilities/agentic-development/memory-template.md +++ b/src/docs/Capabilities/agentic-development/memory-template.md @@ -12,42 +12,84 @@ document defines an exact file layout — this page is that layout. It is the on every adopting organization's `memory` repository instantiates. Content differs per organization; structure does not. +## Memory has three horizons + +Not every remembered thing has the same lifetime, and treating them alike is what makes a +memory repository degrade. A convention that holds everywhere, a fact true of one +repository, and a note that matters only until the current task finishes are three +different kinds of knowledge, and mixing them means the durable content is buried in the +ephemeral. + +The scaffold therefore separates memory by **horizon** — how long the entry stays true and +how widely it applies: + +| Horizon | Scope | Lifetime | Shared | +| --- | --- | --- | --- | +| **User** | Applies across every repository in the organization | Until the practice itself changes | Yes — committed and pushed | +| **Repository** | Applies to one repository | As long as that repository keeps the shape the entry describes | Yes — committed and pushed | +| **Session** | Applies to work in progress right now | Until the task finishes | No — local only, never pushed | + +Horizon is a property of the entry, not of its subject. A workaround for one repository's +build quirk is repository-scoped even though it is about a build; a decision to always +verify a command before recording it is user-scoped even though it was learned in one +repository. + ## Scaffold ```text memory/ -├── README.md # front door: what this repo is, that it's private, "commit straight to main, no PR" -├── CONTRIBUTING.md # short: direct push to main, no PR/review gate, keep entries short/dated/factual +├── README.md # front door: what this repo is, that it's private, "commit straight to main, no PR" +├── CONTRIBUTING.md # short: direct push to main, no PR/review gate, keep entries short and factual ├── AGENTS.md # cross-client agent entry point: orients an agent landing here cold, points at index.md and the memory-writing rules ├── .gitattributes -├── .gitignore -├── index.md # OKF root index (okf_version frontmatter), links to the sections below -├── gotchas/ # short, dated entries: pitfalls, conventions, verified commands +├── .gitignore # ignores session/ so ephemeral notes are never pushed +├── index.md # OKF root index (okf_version frontmatter), links to the scopes below +├── user/ # organization-wide, durable: conventions, verified commands, recurring gotchas, ecosystem facts │ └── index.md -├── knowledge/ # durable facts about the ecosystem, tools, cross-repo relationships -│ ├── index.md -│ └── repos/ # one file per repo worth remembering repo-specific facts about (created lazily as needed) -└── agents/ # per-workflow-stage knowledge; empty stub until stage-specific lessons exist - └── index.md +├── repo/ # per-repository, durable: one folder per repository worth remembering facts about +│ └── index.md # created lazily: repo//index.md once a repository accumulates facts +└── session/ # ephemeral working notes for the task in hand — git-ignored, never pushed + └── .gitkeep ``` -Create `knowledge/repos/.md` files lazily, only once a repository accumulates facts -worth remembering — the folder starts empty in a freshly scaffolded `memory` repository. +`repo//` folders are created lazily, only once a repository accumulates facts worth +remembering — `repo/` starts with nothing but its index in a freshly scaffolded repository. + +## Why `session/` is git-ignored + +A session note is a scratchpad: what has been tried, what the current hypothesis is, which +file is half-edited. It is genuinely useful while the task runs and actively harmful +afterwards, because a later agent reading it cannot tell a live hypothesis from a settled +fact. + +So `session/` is ignored rather than merely short-lived. Ignoring it, instead of relying on +discipline to delete it, means the ephemeral content cannot leak into shared memory at all: -## How the scaffold maps to what memory owns +- An agent MAY write freely to `session/` without weighing whether the note is worth + keeping, which is the only way a scratchpad is useful. +- Nothing in `session/` reaches another person or another machine, so no one inherits + someone else's half-finished reasoning as though it were knowledge. +- Promoting a session note to durable memory is a **deliberate move** into `user/` or + `repo//`, rewritten as a statement of fact. Promotion is the moment the entry gets + reviewed for whether it is actually true, and an ignored folder is what forces that moment + to exist. -[Design](design.md#memory) already states what the `memory` repository owns. Each -top-level folder is one of those responsibilities made concrete: +An agent that wants a note to survive the session MUST move it, not leave it and hope. -| Folder | Owns (from [Design](design.md#memory)) | +## How the scopes map to what memory owns + +[Design](design.md#memory) already states what the `memory` repository owns. Each scope is +one horizon of those responsibilities: + +| Scope | Owns (from [Design](design.md#memory)) | | --- | --- | -| `gotchas/` | Recurring gotchas and lessons learned. | -| `knowledge/` | Active project context that should survive a single chat session, project-specific preferences that are factual rather than private user preference, and issue/PR/incident notes worth reusing. | -| `agents/` | Agent workflow-stage working knowledge. | +| `user/` | Recurring gotchas and lessons learned, durable facts about the ecosystem and its tools, and project-specific preferences that are factual rather than private user preference. | +| `repo//` | Facts true of one repository: its shape, its quirks, its cross-repository relationships, and issue, pull request, or incident notes worth reusing. | +| `session/` | Active context for the task in hand, which should survive a single chat session but MUST NOT outlive the task. | `index.md` is the root map described in [Design's indexes section](design.md#indexes-as-the-mindmap): -it links to `gotchas/index.md`, `knowledge/index.md`, and `agents/index.md` so a human or -agent can start at the root and drill inward. +it links to `user/index.md` and `repo/index.md` so a human or agent can start at the root and +drill inward. It does not link into `session/`, which has no shared content to index. `AGENTS.md` doesn't map to a `memory` ownership bullet — it isn't content memory owns, it's the framework's [client behavior table](design.md#client-behavior) entry point: "Cross-client agents | @@ -59,6 +101,26 @@ orients an *agent* specifically, pointing straight at `index.md` and the contribution-process-flavored (direct push, no PR) even though this repository's real audience is agents, not human contributors. +## Commit after every discrete action + +Durable memory MUST be committed and pushed as soon as it is written, one commit per +discrete thing learned. + +Batching memory writes until the end of a session is how memory gets lost. An agent session +can end at any point — the task completes, the context window fills, the process is +interrupted — and anything still uncommitted at that moment is gone. A lesson learned in +the first minute and pushed in the first minute survives all three endings. + +Micro-commits also make memory legible in the way documentation is: one commit is one +lesson, so the history reads as a list of things learned rather than a periodic dump. A +memory entry whose commit bundles nine unrelated observations cannot be reverted, cited, or +blamed independently. + +Because memory changes land directly on the default branch +([spec](spec.md#requirements)), there is no batching pressure from a review gate. The only +reason to hold a memory write is that it is not yet true, and an entry that is not yet true +belongs in `session/`. + ## A deliberate exception to the Repository Standard [Repository Standard](../../Ways-of-Working/Repository-Standard.md) lists the files every @@ -87,7 +149,9 @@ exception, made explicit rather than left as an oversight: audience is the organization's own humans and agents. A `memory` repository still carries `README.md`, `CONTRIBUTING.md`, `AGENTS.md`, `.gitattributes`, -and `.gitignore` — the minimum needed to explain itself and behave predictably in git. +and `.gitignore` — the minimum needed to explain itself and behave predictably in git. The +`.gitignore` is load-bearing rather than conventional here: it is what keeps `session/` out +of the shared history. ## Visibility @@ -95,6 +159,11 @@ and `.gitignore` — the minimum needed to explain itself and behave predictably context, half-finished reasoning, and organization-specific detail that isn't meant for a public audience, even when the adjoining `docs` repository is public. +Privacy and the `session/` ignore rule solve different problems and neither substitutes for +the other. Privacy decides *who* may read durable memory; the ignore rule decides *what* +becomes durable at all. A private repository full of stale hypotheses is still a repository +an agent will read and believe. + ## Where this connects - [Spec](spec.md) — the requirement that every organization has a `memory` repository. diff --git a/src/docs/Capabilities/agentic-development/plugin-distribution.md b/src/docs/Capabilities/agentic-development/plugin-distribution.md new file mode 100644 index 0000000..978c136 --- /dev/null +++ b/src/docs/Capabilities/agentic-development/plugin-distribution.md @@ -0,0 +1,97 @@ +--- +title: Plugin Distribution +description: How recurring workflows are packaged as named intents that point to canonical documentation, and why a packaged shortcut never carries a copy of the procedure. +--- + +# Plugin Distribution + +Some workflows recur often enough to earn a name. "Review this pull request", "open an issue +for this", "wrap up and hand off" — each is a procedure the organization has already +documented, invoked repeatedly, by everyone. + +Runtimes offer somewhere to put such things: a command, a skill, a named agent, a plugin. +The name differs; the shape does not. This page calls them **named intents**, and states what +one may contain. + +## An intent is a pointer + +A named intent MUST resolve to the canonical documentation for its workflow and MUST contain +only the runtime mechanics needed to get there. + +This is the same rule that governs [client routes](design.md#client-behavior), applied to +behaviour instead of instructions, and for the same reason. An intent that restates its +procedure is a second definition of that procedure — one that no reviewer of the +documentation knows exists, and that starts disagreeing with the documentation the moment +either changes. Whichever one an agent happens to load then determines what the organization +appears to require. + +So the division is strict: + +| An intent MAY contain | An intent MUST NOT contain | +| --- | --- | +| A pointer to the canonical procedure | The steps of the procedure | +| Which arguments it takes and how they map to the procedure's inputs | Standards, review criteria, or acceptance rules | +| Runtime mechanics: which tools to enable, how to gather the current artifacts | A workflow stage definition | +| Where to start reading | A copy of anything in `docs` | + +The test is whether the intent would still be correct if the documentation changed. If it +would silently become wrong, it is carrying a copy. + +## Why intents are worth having anyway + +A pointer sounds like it adds nothing. What it adds is **reliable entry**. + +Without a named intent, reaching a procedure depends on the prompt being phrased in a way +that leads the agent to the right document. That usually works and occasionally doesn't, and +when it doesn't the agent invents a plausible process instead of following the documented +one. An intent removes the guess: the name maps to exactly one starting point. + +Named intents also make the set of recurring workflows **visible**. A runtime that lists its +available intents is showing the organization's procedures, which is a discovery path a +newcomer can use without knowing any file path. + +## One model, many runtimes + +The set of named intents MUST be one shared model, and each runtime MUST express that same +set in its own format. + +An intent available in one client and absent in another produces the failure mode the +framework exists to avoid: the same request behaves differently depending on which client +receives it, and no one can tell whether the difference is intended. Because an intent is +only a pointer plus mechanics, translating one into another runtime's format is mechanical — +there is no logic to port. + +## Distribution is by reference + +An intent MUST NOT bundle a copy of the documentation it points to. + +Bundling is tempting because it makes an intent self-contained and therefore easy to +distribute. It is also how the framework's central premise gets broken: a bundled procedure +is a snapshot, and a snapshot distributed to many places is drift with extra steps. The +canonical documentation is available to every agent through the +[freshness gate](design.md#refresh-hooks); the intent can rely on it being there and current. + +The practical consequence is that updating a procedure needs no redistribution. The +documentation changes, and every intent pointing at it is immediately correct — which is the +whole reason the intent holds a pointer instead of a copy. + +## Intents do not define process + +A named intent MUST NOT define a workflow stage, and MUST NOT add a requirement of its own. + +The prohibition matters most where an intent looks like the natural place for a rule: a +review intent that "also checks X". If X is genuinely required, it belongs in the review +procedure, where it applies to every review including the ones nobody invoked an intent for. +If it is not required, the intent is inventing policy that no one reviewed. + +An intent whose contents grew past mechanics is a signal that the documentation is missing +something. The fix is to move the content into the documentation and shrink the intent back +to a pointer. + +## Where this connects + +- [Spec](spec.md#requirements) — the requirement that named intents stay pointer-based and that no client convenience redefines a stage. +- [Design](design.md#pointer-files) — the pointer-file discipline this extends from instructions to behaviour. +- [MCP Servers](mcp-servers.md) — the tool layer an intent's mechanics may enable, which likewise defines no procedure. +- [Workflow](../../Ways-of-Working/Workflow.md) — the canonical stage procedures intents point at. +- [Conformance](conformance.md) — the anti-duplication checks that catch an intent carrying a copy. diff --git a/src/docs/Capabilities/agentic-development/runtime-integration.md b/src/docs/Capabilities/agentic-development/runtime-integration.md new file mode 100644 index 0000000..d4261ea --- /dev/null +++ b/src/docs/Capabilities/agentic-development/runtime-integration.md @@ -0,0 +1,143 @@ +--- +title: Runtime Integration +description: How a runtime is wired into the framework — the entry file it reads, the lifecycle point its refresh attaches to, the permissions it needs, and what a new runtime must supply to be supported. +--- + +# Runtime Integration + +The framework is deliberately indifferent to which agent runtime a contributor uses. A +procedure documented once holds for every runtime, because a runtime supplies *capability +and lifecycle*, never process. + +That indifference is not free. It holds only while each runtime is integrated the same way, +and a runtime integrated by improvisation becomes the one place where the documented process +is not what actually happens. So integration is itself a defined shape: four things a runtime +MUST supply, and nothing else. + +## What a runtime must supply + +| Obligation | What it means | Where it is defined | +| --- | --- | --- | +| **Entry file** | The instruction file the runtime reads first, which routes to the canonical router rather than restating it | [Pointer files](design.md#pointer-files) | +| **Lifecycle point** | The moment before the first turn where the context refresh runs | [Refresh hooks](design.md#refresh-hooks) | +| **Tool declaration** | The shared tool server set, expressed in the runtime's own configuration format | [MCP Servers](mcp-servers.md#same-contract-different-declaration-syntax) | +| **Identity** | The credential the runtime authenticates with, and the permissions that identity holds | [Permissions](#permissions-follow-the-identity-not-the-runtime) | + +A runtime that supplies all four is supported. A runtime missing any of them is usable by a +human who knows what is missing, and MUST NOT be relied on by documentation — because the +documentation cannot say which part of the procedure will silently not happen. + +Nothing on that list is process. That is the point: the list is short because integration +work is *plumbing*, and plumbing is where a new runtime's cost should be. + +## Runtime shapes, not runtime names + +Integration differs by the **shape** of the runtime, not by which product it is. Shapes are +stable; the products occupying them change, and a design pinned to a product name expires +with it. + +Four shapes cover the field: + +| Shape | Where it runs | Distinguishing constraint | +| --- | --- | --- | +| **Local interactive** | On a contributor's machine, driven turn by turn | Has a durable local workspace, so context can go stale between sessions | +| **Hosted** | In a prepared environment, given a task and left to work | Workspace is created per run, so setup is the only chance to establish context | +| **Review-time** | Triggered by a platform event on a pull request | Reads instructions from the branch under review, not from a local clone | +| **Scheduled** | On a timer, with no human present | No earlier lifecycle point exists, and no one is watching a failure | + +The same bootstrap, the same router, and the same tool contract serve all four. What changes +is only where the trigger hangs — which is exactly the property that makes adding a runtime +cheap. + +### Local interactive + +The durable workspace is the hazard. A local runtime is the only shape whose context +survives between sessions, which means it is the only shape that can read a week-old standard +while believing it is canonical. + +So the refresh MUST attach to a session-start lifecycle point in the runtime's own +configuration, and it MUST run before the first turn rather than on first use of context. A +refresh triggered by need is a refresh that has already been skipped once. + +Where the runtime offers no session-start point, the refresh MUST be invoked explicitly +before context is read. + +### Hosted + +A hosted runtime gets a fresh workspace per run, so staleness is not the risk — *absence* +is. The environment either establishes context during setup or the agent works without it. + +The refresh therefore belongs in the environment's setup steps, and setup failure MUST fail +the run. An agent that starts successfully against missing context produces work that looks +finished and was never governed, which is the most expensive failure in the set because it is +the one that reaches review looking normal. + +### Review-time + +A review-time runtime reads from the head branch of the pull request it is reviewing. This +inverts the usual freshness problem: context follows the branch under review, so a change to +the instructions is in effect for the very pull request that proposes it. + +That is correct and worth stating, because it means a contributor can change agent behaviour +and see the result in the same review — and it means instruction changes MUST be reviewed as +carefully as code, since they are live before merge. + +### Scheduled + +A scheduled runtime has no lifecycle point earlier than the job itself, so the bootstrap is +the job's first step. It also has no human to notice a problem, which raises the bar on +failure handling: a scheduled run MUST fail loudly and MUST NOT proceed with partial context, +because a silent partial run repeats on the schedule. + +## Permissions follow the identity, not the runtime + +Every runtime reaches the same tool servers. What differs is the identity it authenticates +as, and therefore what it is permitted to do. + +A local runtime acts as the person operating it. A hosted or scheduled runtime acts as the +identity its environment grants. A review-time runtime acts with whatever the platform event +provides. These are different permission sets reaching an identical tool contract, and the +difference is +[least privilege](../../Ways-of-Working/Principles/Purpose-and-Direction.md#least-privilege) +working as designed rather than an inconsistency to normalise. + +Two rules follow: + +- A runtime MUST be granted the narrowest permission set its documented tasks require. A + runtime that only advises does not need write access, and + [advisory automation](advisory-agents.md) that holds it will eventually be asked to use it. +- A permission a runtime lacks MUST surface as a visible failure, never as a quietly skipped + step. An agent that cannot open an issue and continues anyway reports success for work that + did not happen. + +Permissions are not part of the shared tool layer, for the same reason +[credentials are not](mcp-servers.md#credentials-are-not-part-of-the-layer): they are a +property of the operator and the environment, not of the capability. + +## Adding a runtime + +Adding a runtime is a documentation change plus four declarations, in this order: + +1. Identify its **shape** from the table above; the shape determines the lifecycle point. +2. Add its **entry file** as a route to the canonical router, carrying no process content. +3. Attach the **refresh** to its lifecycle point, using the existing idempotent bootstrap. +4. Declare the **shared tool set** in the runtime's native configuration format. +5. Record the **identity** it authenticates as and the permissions that identity holds. + +No step writes a procedure, and none of them may be satisfied by copying an existing runtime's +instructions. A copy is the failure mode this whole model exists to prevent: two runtimes with +their own copies of a procedure disagree the moment either is edited, and neither one is +obviously wrong. + +If integrating a runtime appears to require new process rules, the rules belong in +documentation and the requirement is a signal that the documentation was incomplete — not +that this runtime is special. + +## Where this connects + +- [Design](design.md#refresh-hooks) — the lifecycle table each shape's refresh attaches to, and the idempotence requirement. +- [Design](design.md#client-behavior) — why entry files differ in filename and are identical in content. +- [MCP Servers](mcp-servers.md) — the shared tool layer every runtime declares. +- [Plugin Distribution](plugin-distribution.md) — named intents, which are per-runtime packaging over the same documented procedures. +- [Session Interactions](../../Ways-of-Working/Session-Interactions.md) — the phrase vocabulary a runtime recognises without defining. +- [Conformance](conformance.md#checking) — how a repository's runtime declarations are verified. diff --git a/src/docs/Capabilities/agentic-development/spec.md b/src/docs/Capabilities/agentic-development/spec.md index 0696cce..d22b4b0 100644 --- a/src/docs/Capabilities/agentic-development/spec.md +++ b/src/docs/Capabilities/agentic-development/spec.md @@ -7,7 +7,7 @@ description: Requirements for refresh-first, index-first agentic development thr ## Premise -An agent does useful work only when it knows which project it is serving, which standards apply, and what the team has already learned. That context MUST be project-scoped, durable, reviewable, and readable by humans and agents alike. The project boundary is the GitHub organization: `dnb.ghe.com/AI-Platform`, `github.com/MSXOrg`, `github.com/PSModule`, and any future organization that adopts the framework. +An agent does useful work only when it knows which project it is serving, which standards apply, and what the team has already learned. That context MUST be project-scoped, durable, reviewable, and readable by humans and agents alike. The project boundary is the GitHub organization — `github.com/MSXOrg`, `github.com/PSModule`, and any other organization that adopts the framework, on any GitHub host. Each organization owns two canonical repositories: @@ -35,6 +35,10 @@ Applies to any organization that wants a shared project knowledge base and memor - Markdown documents with YAML frontmatter, following the [Open Knowledge Format](../../Dictionary/index.md#open-knowledge-format) model. - Thin repository pointer files: a required `AGENTS.md` router, and a route to it for every client that cannot read it. - Path-scoped rule files, reserved for local caveats that cannot live in repository or central documentation. +- The shared tool layer agents call out to, and how each runtime declares it. +- Named intents that package recurring workflows as pointers to canonical procedure. +- Advisory automation that publishes judgement without taking authority. +- Coordination between humans and agents on platform artifacts. - Refresh-first, index-first discovery from canonical context repositories to the Workflow and its stage procedures. - Deterministic context resolution by host, organization, repository, path, and task. - Human-reviewed changes to canonical knowledge through pull requests. @@ -65,15 +69,22 @@ Applies to any organization that wants a shared project knowledge base and memor - **Deterministic context resolution.** Agents MUST resolve context in layers: system and client policy, user preferences, the repository router, the context-repository freshness gate, repository context, path-scoped repository rules, organization docs, any inherited ecosystem docs, organization memory, then current task context. - **Local-first availability.** The docs and memory repositories SHOULD be available locally in a predictable workspace so agents can read them without relying on search or web access. - **Fresh context before use.** Every canonical context repository MUST be fetched and exactly synchronized with its remote default branch before its contents are read. Dirty, locally ahead, diverged, wrong-branch, or unreachable repositories MUST stop context resolution rather than fall back to stale content. +- **Refresh once per session, not once per machine.** The freshness gate MUST run at the start of every agent session, in every runtime. A workspace that was synchronized at some earlier point MUST NOT be treated as current, because elapsed time is not a state the agent can observe. The refresh MUST be idempotent, so that running it when nothing has changed is cheap and silent; a refresh that is expensive or noisy at steady state gets bypassed, and a bypassed gate is worse than none because the workspace still appears synchronized. +- **Memory is scoped by horizon.** Memory MUST separate entries that apply organization-wide, entries that apply to one repository, and notes that apply only to the task in hand. Session-scoped notes MUST NOT be shared: they MUST be excluded from the repository's history so that a scratchpad cannot be inherited as knowledge. Making a session note durable MUST be a deliberate act of promotion, which is where the entry is checked for whether it is actually true. +- **Durable memory is committed as it is written.** A memory entry MUST be committed and pushed when it is written, one commit per discrete lesson, so that no remembered thing depends on a session ending cleanly. +- **One tool layer, declared per runtime.** Where agents use external tools, the set of tool servers MUST be defined once as a logical layer and each runtime MUST declare that same set in its own native configuration format. A runtime MUST NOT define tools of its own that other runtimes lack, because a capability available in one client and absent in another makes the documented procedure conditional on which client is running it. +- **Named intents stay pointer-based.** A packaged shortcut for a recurring workflow — however a runtime names it — MUST resolve to the canonical documentation for that workflow and MUST contain only the runtime mechanics needed to get there. It MUST NOT restate the procedure, since a shortcut that carries a copy of the process becomes a second, silently diverging definition of it. +- **Advice and authority are separate.** An automated agent MAY analyse work and publish its conclusion as advice on the artifact under review. It MUST NOT be the thing that decides: it MUST NOT overwrite a human's decision, MUST NOT re-apply a decision a human has changed, and MUST NOT commit to the branch it is advising on. Its output is an input to the review, not a substitute for it. +- **Coordination happens on durable artifacts.** Where agents and humans coordinate, they MUST do so through the platform's own artifacts — issues, labels, and pull requests — rather than through a channel that leaves no trace in the repository. Intent MUST be separable from implementation: the issue states *what* is wanted and *why*, and the pull request proposes *how*, so that a rejected implementation does not discard the intent. - **Reviewed knowledge changes.** Changes to the `docs` repository MUST happen through pull requests. Changes to memory MAY be lighter-weight, but MUST remain versioned in git. - **No cross-project bleed.** An agent working in one organization MUST NOT apply another organization's standards or memory unless the current task explicitly asks for cross-organization work. - **Traceable memory.** Memory entries SHOULD identify the context they came from and SHOULD be short, factual, and linked to the relevant issue, pull request, document, or repository when one exists. ## Success criteria -- An agent working in `github.com/PSModule/` reads PSModule docs and memory, not MSXOrg or AI-Platform rules. +- An agent working in `github.com/PSModule/` reads PSModule docs and memory, not another organization's rules. - An agent working in `github.com/MSXOrg/` resolves `github.com/MSXOrg/docs` and `github.com/MSXOrg/memory` as the canonical project context. -- An agent working in `dnb.ghe.com/AI-Platform/` resolves `dnb.ghe.com/AI-Platform/docs` and `dnb.ghe.com/AI-Platform/memory` as the canonical project context. +- An agent working in `//` for any adopting organization resolves `//docs` and `//memory` as the canonical project context, with no change to the framework. - A new product repository can adopt the framework by adding a router and the client routes that reach it, without copying standards or memory pages. - An agent reads the repository's own README and CONTRIBUTING before it reads an organization standard, and still applies the organization standard when the two disagree. - A human can start at `docs/index.md` or `memory/index.md` and navigate to the same context an agent uses. @@ -104,6 +115,12 @@ This is the order in which context is **read**, nearest first. It is not the ord - [Design](design.md) — how these requirements are delivered. - [Memory Repository Template](memory-template.md) — the concrete scaffold every organization's canonical `memory` repository instantiates. +- [MCP Servers](mcp-servers.md) — how one logical tool layer is declared across runtimes. +- [Runtime Integration](runtime-integration.md) — what a runtime supplies to be supported, and why process is never part of it. +- [Plugin Distribution](plugin-distribution.md) — how named intents stay pointers to documentation. +- [Agent Interaction](agent-interaction.md) — issues, labels, and pull requests as the coordination substrate. +- [Advisory Agents](advisory-agents.md) — the pattern for automation that advises without deciding. +- [Conformance](conformance.md) — how a repository is measured against this spec. - [Agentic Development](../../Ways-of-Working/Agentic-Development.md) — the existing way-of-working standard this framework operationalizes. - [Documentation Model](../../Ways-of-Working/Documentation-Model.md) — how specs and designs are written and kept evergreen. - [Open Knowledge Format](../../Dictionary/index.md#open-knowledge-format) — the Markdown and frontmatter model used for knowledge pages. diff --git a/src/docs/Capabilities/dependency-updates/design.md b/src/docs/Capabilities/dependency-updates/design.md index a4a7544..9c769a2 100644 --- a/src/docs/Capabilities/dependency-updates/design.md +++ b/src/docs/Capabilities/dependency-updates/design.md @@ -13,9 +13,57 @@ The behaviour in the [spec](spec.md) is delivered by the platform-native updater | Kind | Trigger | Cadence | | --- | --- | --- | -| **Version update** | A newer version of a pin exists | Scheduled (e.g. weekly), with a cooldown before a freshly published version is proposed | +| **Version update** | A newer version of a pin exists | Scheduled, with a cooldown before a freshly published version is proposed | | **Security update** | A published advisory affects a pin | On disclosure, out of band from the schedule | +## Coverage from manifests + +Which ecosystems are covered is not a judgement call — it is a function of the +files the repository contains. Each ecosystem announces itself with a manifest: + +| Ecosystem | Announced by | Coverage path | +| --- | --- | --- | +| GitHub Actions | Workflow and composite-action definitions under `.github/` | Native updater | +| Containers | A container definition or a base-image reference | Native updater | +| Language packages | The ecosystem's manifest and lockfile at the directory root it governs | Native updater when supported | +| Infrastructure definitions | The module or provider constraint file for the tool in use | Native updater when supported | + +The organization owns a **native-support catalogue** and an **exception register**. +The catalogue names the ecosystems the platform-native updater supports; generation +emits one updater entry per supported ecosystem and directory. An unsupported +manifest is not forced into an invalid native entry. Instead, it must have an +exception-register entry naming the manifest and directory, why native support is +absent, the responsible owner, and the shared centrally managed mechanism that +checks and proposes updates. + +Reconciliation compares every manifest with the generated native configuration and +the exception register. Adding an ecosystem is therefore either the manifest plus +regenerated native configuration, or the manifest plus a central exception request. +A repository never introduces a bespoke updater: the exception consumes a mechanism +operated once for the organization, and remains visible until native support exists. + +## Cadence and cooldown + +Frequency and the timezone the schedule is expressed in are **organization +configuration**, not constants. A schedule expressed in a timezone nobody works in +lands pull requests outside the hours anyone triages them. + +```yaml +schedule: + interval: weekly + day: monday + time: "09:00" + timezone: +``` + +The **cooldown** is a deliberate delay between a version's publication and its +proposal. It costs a few days of currency and buys the chance for an upstream project +to withdraw or supersede a bad release before every consumer has a pull request open +against it. Currency is the goal; being first is not. + +Security updates ignore both settings. An advisory means the pinned version is known +bad now, and waiting for a schedule window or a cooldown would be waiting on purpose. + ## The updater Dependabot opens **one PR per outdated or vulnerable dependency** — or one per @@ -73,16 +121,39 @@ consuming artifact. So the two coexist: the **release bump** label (default reads; the **`update:*`** label is advisory metadata that drives review routing, never the bump. -## Update-level policy +## Grouping + +Grouping trades review granularity for review cost, and the trade is only worth +making where the granularity carries no information: + +| Group | Contents | Rationale | +| --- | --- | --- | +| Per-ecosystem minor and patch | Every minor and patch update within one ecosystem, in one pull request | Twelve patch bumps reviewed separately cost twelve reviews and reveal no more than one | +| Isolated major | One pull request per major update | This is the diff a reviewer has to read; batching it hides it | + +A group MUST NOT span ecosystems. Reviewing an ecosystem's updates requires knowing +that ecosystem's conventions, and a pull request mixing several leaves no reviewer +qualified for the whole diff. + +Grouping also bounds the blast radius of a failure. When a grouped pull request goes +red, the failure is attributable to one ecosystem; when a cross-ecosystem batch goes +red, isolating the cause means splitting the pull request by hand. + +## Review posture | Update level | Handling | | --- | --- | -| `update:patch`, `update:minor` | Eligible for **auto-merge** once all required checks pass. | -| `update:major` | **Human review required**; never auto-merged. | +| `update:patch`, `update:minor` | Eligible for **automatic merge** once every required check passes. | +| `update:major` | **Human review required**; never merged automatically. | + +The asymmetry follows [SemVer](https://semver.org/): a minor or patch release +promises compatibility, so passing checks is evidence enough, and a human reading the +diff adds ceremony rather than information. A major release promises nothing, so the +checks cannot substitute for reading it. -Auto-merge is gated on green CI, never a bypass — every update passes the full -check suite before it can merge. A repository may tighten this (require review -for `update:minor` too) but never loosen it to auto-merge `update:major`. +Automatic merge is gated on green checks, never a bypass — every update passes the +full suite before it can merge. A repository MAY tighten this (requiring review for +`update:minor` too) and MUST NOT loosen it to merge `update:major` automatically. ## Security updates @@ -92,17 +163,28 @@ and the same release path as any other update. ## Configuration surface -| Surface | Where | -| --- | --- | -| Ecosystems, directories, schedule, cooldown, grouping | `.github/dependabot.yml` | -| Static labels (`dependencies` + ecosystem) | `.github/dependabot.yml` | -| `update:*` labels | update metadata → labelling step | -| Auto-merge policy | branch protection / auto-merge automation | -| Security updates | repository security settings (on by default) | +| Surface | Where | Set by | +| --- | --- | --- | +| Native ecosystems and directories | `.github/dependabot.yml` | Generated from supported manifests | +| Unsupported ecosystems | Central exception register | Centrally managed shared mechanism | +| Schedule interval, day, time, timezone | `.github/dependabot.yml` | Organization configuration | +| Cooldown | `.github/dependabot.yml` | Organization configuration | +| Grouping | `.github/dependabot.yml` | Generated: per-ecosystem minor/patch groups, majors isolated | +| Static labels (`dependencies` + ecosystem) | `.github/dependabot.yml` | Generated | +| `update:*` labels | Update metadata → labelling step | Derived per pull request | +| Automatic-merge policy | Branch protection and merge automation | Organization configuration | +| Security updates | Repository security settings | On by default | + +Everything marked *generated* is reproducible from the repository, so a difference +between the committed file and a fresh generation is drift. Everything marked +*organization configuration* is a deliberate choice that generation MUST preserve +rather than overwrite. ## Where this connects - [Spec](spec.md) — the requirements this design delivers. +- [Automation Labels](../../Ways-of-Working/Automation-Labels.md) — the ownership and namespacing rules the label scheme follows. +- [Repository Governance](../repository-governance/design.md#drift-detection-and-reconciliation) — the reconciliation that compares generated configuration against what is committed. - [Release Management](../release-management/design.md) — the release an update PR cuts. - [Downstream Release Propagation](../downstream-release-propagation/design.md) — the internal counterpart; propagation PRs are dependency updates too. - [GitHub Actions](../../Coding-Standards/GitHub-Actions.md#keep-pinned-actions-current) — the Action-pin specifics this builds on. diff --git a/src/docs/Capabilities/dependency-updates/spec.md b/src/docs/Capabilities/dependency-updates/spec.md index dc90cba..b024b8c 100644 --- a/src/docs/Capabilities/dependency-updates/spec.md +++ b/src/docs/Capabilities/dependency-updates/spec.md @@ -26,27 +26,76 @@ This capability rests on the [Principles](../../Ways-of-Working/Principles/index ## Scope Any repository that pins external dependencies: Action and workflow SHAs, -container base images, language packages and lockfiles, Terraform providers and -modules. Two questions are asked of every pin — **currency** (is a newer version -available?) and **security** (does the pinned version carry a known advisory?). +container base images, language packages and lockfiles, and the manifests of any +other ecosystem the repository actually uses. Two questions are asked of every pin — +**currency** (is a newer version available?) and **security** (does the pinned +version carry a known advisory?). + +Out of scope: what an update *does* to the artifact. That is the release the update +produces, and it is governed by [Release +Management](../release-management/spec.md). ## Requirements -- **Automatic checking.** Version currency is checked on a schedule; security advisories trigger updates out of band. No human watches upstream releases. -- **One reviewed PR per update.** Each update is a pull request that passes the full CI gate before merge; nothing is applied unreviewed. -- **Two labelled dimensions.** Every update PR is labelled with its category and ecosystem, and with the dependency's own version-change level. -- **Labels MUST NOT collide with release versioning.** The label that signals the *dependency's* version level MUST NOT reuse the release-bump labels (`Major` / `Minor` / `Patch` / `NoRelease`). A dependency update is artifact-affecting and therefore *produces a release*; sharing one label set across the two dimensions would bump this repository's version off the wrong signal. -- **SHA pins stay immutable.** An Action or workflow update rewrites the pin to the new commit SHA with the version as a trailing comment. -- **Security first.** Security updates are prioritised over scheduled version updates. +### Coverage + +- **FR1 — Every ecosystem present is classified.** Coverage MUST be derived from + the manifests the repository actually contains. Each detected ecosystem MUST + either be supported by the platform-native updater or have a centrally managed + exception; an unclassified manifest is an uncovered pin that ages silently. +- **FR2 — Native support is configured.** Every detected ecosystem in the + centrally maintained native-support catalogue MUST have an updater entry for + the directory its manifest governs. +- **FR3 — Unsupported ecosystems use a central exception.** An ecosystem the + native updater does not support MUST be recorded in the centrally managed + exception register with its manifest, scope, reason, owner, and shared update + mechanism. A repository MUST NOT create or maintain a bespoke updater. +- **FR4 — Coverage is verifiable, not asserted.** The manifest inventory MUST be + comparable against the configured native ecosystems and the central exception + register, so a gap is a detectable finding rather than something noticed when a + pin is years stale. +- **FR5 — Native configuration is generated, not hand-maintained.** Native + updater configuration SHOULD be produced from supported manifests rather than + written by hand. A generated configuration cannot drift from the repository; a + hand-written one drifts when a supported ecosystem is added. + +### Cadence + +- **FR6 — Currency is checked on a schedule.** No human watches upstream releases. +- **FR7 — The schedule is configuration.** Frequency and the timezone it is expressed in MUST be configurable per organization. There is no correct global cadence: a repository whose consumers deploy continuously wants updates sooner than one that ships quarterly, and a schedule expressed in a timezone nobody works in produces pull requests nobody triages. +- **FR8 — Freshly published versions wait.** A version MUST NOT be proposed the moment it appears. A cooldown between publication and proposal lets an upstream project withdraw or supersede a bad release before every consumer has a pull request open against it. +- **FR9 — Security advisories bypass the schedule.** An advisory affecting a pin raises an update on disclosure, out of band, and MUST be prioritised over scheduled currency updates. + +### Batching + +- **FR10 — Low-risk updates MAY be grouped; breaking ones MUST NOT be.** Minor and patch updates within one ecosystem MAY share a pull request, because reviewing twelve patch bumps separately costs twelve reviews and yields no more information than one. A major update MUST be isolated, because it is the update whose diff has to be read. +- **FR11 — Grouping never crosses ecosystems.** A group's review requires knowing one ecosystem's conventions; mixing ecosystems in one pull request means no single reviewer is qualified for the whole diff. + +### Review and labelling + +- **FR12 — One reviewed pull request per update or group.** Each update is a pull request that passes the full check suite before merge. Nothing is applied unreviewed, and no update takes a side channel around the gate. +- **FR13 — Update level is labelled.** Every update pull request MUST carry the category, the ecosystem, and the dependency's own version-change level, so review routing and triage do not require opening the diff. +- **FR14 — Update labels MUST NOT reuse the release bump vocabulary.** The label that signals the *dependency's* version level MUST be namespaced away from the reserved release-bump labels ([automation labels](../../Ways-of-Working/Automation-Labels.md#reserved-vocabularies)). A dependency update is artifact-affecting and therefore produces a release; one shared vocabulary across the two dimensions would set this repository's version from the upstream project's decision. +- **FR15 — Review posture follows update level.** Patch and minor updates MAY merge automatically once every required check passes. A major update MUST require human review and MUST NOT merge automatically. A repository MAY tighten this and MUST NOT loosen it. +- **FR16 — Automatic merge is never a bypass.** Where an update merges without review, it does so because the checks passed, not because the checks were skipped. + +### Non-functional + +- **NFR1 — SHA pins stay immutable.** An update to a SHA-pinned dependency rewrites the pin to the new commit SHA and records the human-readable version alongside it, so the pin stays exact and stays legible. +- **NFR2 — Update pull requests carry their evidence.** Each one includes the upstream release notes or changelog for the range it crosses. A reviewer deciding on a bump should not have to leave the pull request to find out what changed. +- **NFR3 — The mechanism is platform-native.** Checking, advisory correlation, and pull request creation are platform functions, not bespoke automation, so no repository maintains an updater of its own. ## Success criteria - An outdated or vulnerable pin produces a labelled pull request with no human trigger. -- No dependency PR merges without passing the same checks as any other PR. +- An ecosystem added to a repository without a corresponding updater entry is a detectable finding, not a silent gap. - The dependency's version level is legible from labels without opening the diff, and never changes this repository's release bump by itself. +- No dependency pull request merges without passing the same checks as any other pull request. ## Where this connects -- [Design](design.md) — the label scheme, the updater, and the auto-merge policy. -- [Release Management](../release-management/spec.md) — the versioning update PRs feed into, and the bump labels these must not reuse. +- [Design](design.md) — the label scheme, the updater, and the automatic-merge policy. +- [Release Management](../release-management/spec.md) — the versioning update pull requests feed into, and the bump vocabulary these must not reuse. +- [Automation Labels](../../Ways-of-Working/Automation-Labels.md) — the namespacing rule that keeps the two version dimensions disjoint. +- [Repository Governance](../repository-governance/spec.md) — the reconciliation that detects an uncovered ecosystem. - [GitHub Actions](../../Coding-Standards/GitHub-Actions.md#keep-pinned-actions-current) — keeping pinned Actions current. diff --git a/src/docs/Capabilities/downstream-release-propagation/design.md b/src/docs/Capabilities/downstream-release-propagation/design.md index 2245f48..0272d04 100644 --- a/src/docs/Capabilities/downstream-release-propagation/design.md +++ b/src/docs/Capabilities/downstream-release-propagation/design.md @@ -10,8 +10,8 @@ release coordinates, builds a self-contained prompt per dependent, and delegates the change to a cloud agent **in the dependent** via the [Agent Tasks API](https://docs.github.com/rest/agent-tasks/agent-tasks). The brief travels entirely in the prompt. The agent first creates or reuses the -dependent's Task delivery issue, then opens the pull request with that Task as -its one closing reference. +dependent's Task or Bug delivery issue, then opens the pull request with that +delivery leaf as its one closing reference. ```mermaid flowchart TD @@ -19,7 +19,7 @@ flowchart TD notify --> resolve["Resolve version + immutable ref (SHA / digest) + notes"] resolve --> fan{"For each dependent"} fan --> delegate["Create agent task in dependent
self-contained prompt with full context"] - delegate --> issue["Create or reuse Task delivery issue"] + delegate --> issue["Create or reuse Task / Bug delivery issue"] issue --> pr["Agent opens closing PR: bump + related fixes + impact"] pr --> review["Human review + merge"] ``` @@ -64,22 +64,51 @@ of truth for the change. ## Delegation -The delegation step creates an agent task in the dependent carrying the prompt -and the instruction to open the PR, then polls until the task reaches `queued`, -`in_progress`, or `completed` (a fast task may go straight to `completed`). It -**fails** only if the task cannot be created or lands in `failed`, `timed_out`, -or `cancelled`. Fan-out is a **matrix** of dependents (pinned-reference shape) or -a single configured `notify_repo` (published-artifact shape), with -`fail-fast: false` so one dependent's failure does not stop the rest. +Two delegation modes can carry the request into the dependent. Both create or +reuse a real Task or Bug delivery leaf before a pull request exists, so the +delivery path satisfies the [Definition of Ready](../../Ways-of-Working/Definition-of-Ready-and-Done.md#delivery-leaf-readiness). + +| | **Task-first** | **Issue-first** | +| --- | --- | --- | +| The request is | an agent task created after its delivery leaf exists | an issue in the dependent, which the agent picks up | +| The agent produces | a pull request closing the delivery leaf | a pull request closing that issue | +| Idempotency key | the delivery issue — one per producer version per dependent | the issue itself — one issue per producer version per dependent | +| Visible before the agent starts | the delivery issue and task state | the issue | +| Suits | immediate execution after the delivery leaf is ready | propagation that needs triage, discussion, or scheduling before work starts | + +**Issue-first is the default:** it creates or reuses one Task or Bug in the +dependent per producer version, with independently verifiable acceptance criteria +and an executable local plan. The issue is the delivery leaf before the agent +starts, then the agent opens the pull request that closes exactly that issue. +Idempotency is by **existence**: the issue is the durable record that this version +was propagated, so a repeat run finds and reuses it. + +**Task-first** is available only when the agent task is created after the same +Task or Bug is created or reused. The task carries the issue number and instruction +to close it, then is polled until it reaches `queued`, `in_progress`, or +`completed` (a fast task may go straight to `completed`). It fails only if the +task cannot be created or lands in `failed`, `timed_out`, or `cancelled`. An agent +task is execution state, not a delivery record; it never authorizes a standalone +delivery pull request. + +Either way the model is chosen per producer, not per release, so a dependent +receives propagation in one consistent shape. + +Fan-out is a **matrix** of dependents (pinned-reference shape) or a single +configured `notify_repo` (published-artifact shape), with `fail-fast: false` so +one dependent's failure does not stop the rest. ## Agent instructions -The agent is told to: **create or reuse one Task delivery issue** for the -dependent's slice; **apply the bump** (every matching reference, bringing any -mutable-tag pins into SHA-pinned compliance); **apply the related changes it can -make safely**; **call out** larger or riskier work under a follow-up section -rather than forcing it into the bump; **summarise impact** in the PR body; and -**open the pull request** with exactly that Task as its closing issue. +The agent is given the same instructions under either delegation model: + +- **Apply the bump.** Every matching reference, bringing any mutable-tag pins into SHA-pinned compliance. +- **Read the release notes for related work.** The notes are the producer's own account of what changed; the agent treats new or renamed configuration keys, new environment variables or secrets, changed defaults, and migrations as part of the update, not as someone else's problem. +- **Apply the related changes it can make safely.** A change that is mechanical and verifiable belongs in this pull request. +- **Call out** larger or riskier work under a follow-up section rather than forcing it into the bump. Scope that needs a decision is surfaced, not guessed at. +- **Summarise impact** in the PR body: what moved, what it requires of the dependent, and what was deliberately left out. +- **Open the pull request** — closing exactly the Task or Bug delivery leaf + created or reused for this producer version. ## Permissions and credentials @@ -100,7 +129,8 @@ and a release it publishes cannot trigger a `release:` workflow. So the job: | Condition | Behaviour | | --- | --- | -| Task not created (missing permission / capability off) | Step **fails** with the error; re-run via `workflow_dispatch`. | +| Delegation not created (missing permission / capability off) | Step **fails** with the error; re-run via `workflow_dispatch`. | +| This version already propagated to this dependent | Step **succeeds**, reporting the existing delivery issue and pull request if one exists; no duplicate is created. | | Task lands in a failed / timed-out / cancelled state | Step **fails** with the reported state. | | One dependent's leg fails | Fails independently (`fail-fast: false`); others proceed. | | Prerelease published | Propagation is skipped. | diff --git a/src/docs/Capabilities/downstream-release-propagation/spec.md b/src/docs/Capabilities/downstream-release-propagation/spec.md index dee44d8..4f8c3de 100644 --- a/src/docs/Capabilities/downstream-release-propagation/spec.md +++ b/src/docs/Capabilities/downstream-release-propagation/spec.md @@ -37,14 +37,23 @@ Two shapes occur; both are the same mechanism with a different artifact: - **Automatic on stable release.** A stable producer release MUST trigger propagation to every declared dependent. Prereleases MUST NOT propagate. - **Full context, not just a number.** Each dependent receives the new version, the immutable reference (commit SHA or image digest), the release notes, and any related-change context the update implies. -- **A Task and a PR per dependent, opened by an agent.** The mechanical work — the bump plus the fixes that make it work — is delegated to a cloud agent *in the dependent*. The agent creates or reuses one Task delivery leaf and opens a pull request that closes exactly that Task. +- **A pull request per dependent, opened by an agent.** The mechanical work — the bump plus the fixes that make it work — is delegated to a cloud agent *in the dependent*, which opens the pull request. **How** the agent is engaged is a design choice, not a requirement: the spec requires the delegation and the pull request, not a particular delegation mechanism. +- **A delivery leaf before the pull request.** The dependent MUST create or reuse + a Task or Bug for the producer version before the agent opens its pull request. + The leaf carries the executable local plan and acceptance criteria required by + the [Definition of Ready](../../Ways-of-Working/Definition-of-Ready-and-Done.md#delivery-leaf-readiness), + and the pull request closes exactly that leaf. +- **Idempotent by identity.** Propagation MUST be safe to run more than once for + the same producer version. A repeated run reuses the existing delivery Task or + Bug and MUST NOT open a second pull request for it. - **Humans decide.** A human reviews and merges each PR; the agent applies what it can safely do now and calls out larger or riskier work as follow-up. -- **Backfill on demand.** Propagation MUST be re-runnable for a specific release — for a missed event, or a dependent added after the release. +- **Backfill on demand.** Propagation MUST be re-runnable for a specific release — for a missed event, or a dependent added after the release. Backfill uses the same idempotency, so re-running for an already-propagated dependent is a no-op rather than a duplicate. ## Success criteria -- A stable release yields one Task and one closing PR in each declared dependent, carrying the immutable reference and an impact summary without manual coordination. +- A stable release yields one pull request in each declared dependent, carrying the immutable reference and an impact summary without manual coordination. - A prerelease yields none. +- Running propagation twice for the same version yields the same one pull request per dependent, not two. - A dependent added after a release can be back-filled without cutting a new release. ## Where this connects diff --git a/src/docs/Capabilities/index.md b/src/docs/Capabilities/index.md index 9c55a94..2d85da8 100644 --- a/src/docs/Capabilities/index.md +++ b/src/docs/Capabilities/index.md @@ -21,6 +21,7 @@ the same spec-and-design shape as any other capability. | Section | Description | | --- | --- | | [Release Management](release-management/index.md) | How a source change becomes a versioned, immutable artifact, driven entirely on the GitHub platform. | +| [Repository Governance](repository-governance/index.md) | How every repository in an organization is classified, protected, and continuously reconciled against the controls its classification declares. | | [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. | diff --git a/src/docs/Capabilities/merge-automation/design.md b/src/docs/Capabilities/merge-automation/design.md index e539359..fbe057d 100644 --- a/src/docs/Capabilities/merge-automation/design.md +++ b/src/docs/Capabilities/merge-automation/design.md @@ -60,7 +60,7 @@ flowchart TD ## Eligibility Which pull requests may merge without a human mirrors the -[dependency-update policy](../dependency-updates/design.md#update-level-policy): +[dependency-update policy](../dependency-updates/design.md#review-posture): low-risk, well-labelled changes are eligible; anything that can break consumers, or that policy marks for review, needs a human approval even on green. The eligible set is configuration, and it only ever **tightens** the gate — diff --git a/src/docs/Capabilities/release-management/design-publishing-targets.md b/src/docs/Capabilities/release-management/design-publishing-targets.md new file mode 100644 index 0000000..af3a67f --- /dev/null +++ b/src/docs/Capabilities/release-management/design-publishing-targets.md @@ -0,0 +1,71 @@ +--- +title: Publishing Targets +description: The contract every publishing destination documents, with GitHub Releases as the reference target. +--- + +# Release Management — Publishing Targets + +A **publishing target** is any destination that accepts a versioned artifact and serves it to consumers. The [release pipeline](design.md#the-pipeline) publishes to targets through one contract, so the process is the same whether a repository has one destination or five. + +This page holds the contract and the targets that satisfy it. It is the boundary that lets a new destination be added without touching the [spec](spec.md). + +## The contract + +A target is described by six answers. They are the questions the release process needs answered in order to publish safely, and they are the questions that differ between destinations: + +| Dimension | What it settles | +| --- | --- | +| **Version scheme** | the exact string form a version takes, and what the target accepts as valid | +| **Prerelease representation** | how a prerelease is expressed, and how the target sorts it relative to stable versions | +| **Immutability** | whether a published version can be replaced, and what happens on a repeated publish of the same version | +| **Unpublish** | whether a version can be withdrawn, what withdrawal does to existing consumers, and whether the version number becomes reusable | +| **Sliding tags** | whether the target supports mutable pointers such as `latest`, and how they are moved | +| **Release record** | where the durable, linkable evidence of the release lives | + +A target MUST document all six before it is used. An undocumented dimension is a surprise waiting for the first failed release — most often around immutability, where publishing the same version twice is a success on one target and a hard error on another. + +## Target summary + +| Target | Version scheme | Prerelease | Immutable | Unpublish | Sliding tags | Release record | +| --- | --- | --- | --- | --- | --- | --- | +| **GitHub Releases** | `vMAJOR.MINOR.PATCH` git tag | SemVer suffix, flagged as prerelease | tag and assets are treated as immutable | delete is possible; treated as exceptional | yes — git tags | the Release itself | +| **PowerShell Gallery** | `MAJOR.MINOR.PATCH` module version | SemVer suffix on the module version | yes — a version is published once | unlist only; the version is never reusable | no | the gallery listing | +| **VS Code Marketplace** | `MAJOR.MINOR.PATCH` extension version | separate prerelease channel on the same version line | yes | unpublish removes the extension version | channel acts as the pointer | the marketplace listing | +| **NuGet** | `MAJOR.MINOR.PATCH` package version | SemVer suffix on the package version | yes | unlist only; the version is never reusable | no | the package listing | +| **Container registry** | `:` plus a content digest | SemVer suffix in the tag | the **digest** is immutable; the tag is not | tag or manifest deletion | yes — mutable tags | the digest | + +Two patterns run through the table and shape how consumers are told to pin: + +- **Version numbers are single-use.** On every target above, a published version number is spent. Withdrawal removes availability, not the reservation. A fix is therefore always a new version — never a re-publish of the old one, which is the same conclusion the pipeline reaches from [build-once](design.md#the-pipeline). +- **Only content addresses are truly immutable.** Where a target offers both a name and a digest, the digest is the reference and the name is the convenience. + +## GitHub Releases — the reference target + +GitHub Releases is the reference implementation: every repository governed by this capability publishes there, and a repository with no external artifact publishes there *only*. A target-specific concern is described relative to this one. + +- **Version scheme.** A git tag `vMAJOR.MINOR.PATCH` on the release-branch commit. The tag is the artifact for Action, workflow, and source-distributed module repositories. +- **Prerelease.** The SemVer prerelease suffix, with the Release marked as a prerelease so it is excluded from *latest*. +- **Immutability.** The tag points at one commit and is not moved. Assets are uploaded once. A published version is never rewritten in place. +- **Unpublish.** A Release and its tag can be deleted, but doing so breaks consumers that resolved it, so it is reserved for a release that must not exist — a leaked secret, a legal removal — and the version number is not reused. +- **Sliding tags.** Supported as additional git tags, subject to the [sliding-tag rules](design.md#sliding-tags). +- **Release record.** The Release itself: the version as its name, the release note as its body, and the immutable reference to whatever was published elsewhere. + +Because every release produces a GitHub Release, it is also the **join point** across targets: a release published to a registry or marketplace records its reference there, so one link answers *what shipped, in what version, and where it went*. + +## Adding a target + +1. Document the six contract dimensions above, in the summary table. +2. Confirm the target's immutability and prerelease behaviour are compatible with [SemVer](https://semver.org/) ordering. Where the target's native convention differs, the mapping is stated rather than assumed. +3. Add the publish step. It receives the already-built artifact and the + already-resolved version, and it MUST be idempotent: publishing a version the + target already holds is a success only when its immutable identity matches the + artifact being retried. A different artifact at the same version is an error. +4. Include the target in the [all-or-nothing](design.md#publishing-targets) set, so a version cannot be present on some destinations and absent from others. + +The spec does not change. That is the purpose of the contract. + +## Where this connects + +- [Spec](spec.md) — the requirements this design serves. +- [Design](design.md) — the pipeline that publishes to these targets. +- [Security](../../Coding-Standards/Security.md#supply-chain) — why consumers pin to immutable references. diff --git a/src/docs/Capabilities/release-management/design.md b/src/docs/Capabilities/release-management/design.md index b24d5ee..0f225f6 100644 --- a/src/docs/Capabilities/release-management/design.md +++ b/src/docs/Capabilities/release-management/design.md @@ -37,11 +37,45 @@ release-branches: release-type: prerelease ``` +## The pipeline + +Every release runs the same four stages in order. The stage boundaries exist to +make **build-once** enforceable — each stage may only consume what the previous +stage produced. + +```mermaid +flowchart LR + resolve["Resolve
version decided"] --> build["Build
artifact created once"] + build --> test["Test
same artifact validated"] + test --> publish["Publish
same artifact released"] +``` + +| Stage | Produces | Invariant | +| --- | --- | --- | +| **Resolve** | the version | the version is known before anything is built, so it can be baked in | +| **Build** | the artifact | the artifact is created exactly **once**, carrying its version | +| **Test** | a verdict | validation runs against the built artifact, not a rebuild of its source | +| **Publish** | released versions | the artifact is transferred unchanged to every target | + +Two consequences follow, and they are the point of the model: + +- **The version is identity, not metadata.** Because Resolve precedes Build, the + version is embedded in the artifact rather than attached to it. A manifest + version, an image label, and the tag agree because they came from one decision. +- **Recovery preserves artifact identity.** Retrying validation or publication of + an unchanged, already-built artifact reuses that artifact and its resolved + version. A correction that changes the output is a new release: it resolves a + new version and builds new bytes. An artifact is never patched, re-tagged, or + rebuilt under an existing version — that would publish something other than what + was tested. + ## Version computation -- The bump comes from the PR label (`Major` / `Minor` / `Patch` / `NoRelease`), - defaulting to `Patch`. Multiple SemVer labels, or a SemVer label with - `NoRelease`, are **rejected**. For `workflow_dispatch`, the bump is an input. +- The bump comes from the PR label (`Major` / `Minor` / `Patch` / `NoRelease`). + Exactly one is required; **no default** is applied. A missing label, multiple + SemVer labels, or a SemVer label alongside `NoRelease` are all **rejected**, so + the version is always a decision someone made. For `workflow_dispatch`, the + bump is an input. - **First release** starts from a baseline (`v0.1.0` or `v1.0.0`). Pre-`1.0.0` breaking changes are `Minor` per [SemVer §4](https://semver.org/#spec-item-4); `Major` is never auto-detected pre-`1.0.0`. @@ -94,6 +128,57 @@ handed to [Downstream Release Propagation](../downstream-release-propagation/des 3. A GitHub Release whose name is the version, carrying the note and the immutable reference (digest, package version, or the tag). +## Publishing targets + +Publish is the only stage that knows where an artifact goes, and it reaches every +destination through one abstraction: a **publishing target**. A target is any +destination that accepts a versioned artifact and serves it to consumers — the +GitHub Release itself, a package registry, an extension marketplace, a container +registry. + +The release process is written against the target *contract*, never against a +specific target. Each target documents how it answers six questions — version +scheme, prerelease representation and sort order, immutability, unpublish +behaviour, sliding-tag support, and where its release record lives — in +[Publishing Targets](design-publishing-targets.md). Adding a destination means +writing that contract and a publish step; it does not change Resolve, Build, +Test, or the spec. + +Where a repository has more than one target, publishing is **all-or-nothing** for +a version: + +- Targets are attempted in a defined order, and each is idempotent — publishing + an already-published version is a success only when it identifies the same + immutable artifact. A version collision with different bytes is an error, so a + re-run completes the set rather than accepting changed output. +- A target that rejects the version fails the release. The version is not + advertised as available until every target holds it. +- A partial publication resumes Publish for the **same** artifact and the same + version. It never resolves a new version to work around a single failed target, + because the targets that already succeeded hold that immutable version. + +## Sliding tags + +Sliding tags are optional, mutable pointers published alongside the immutable +version tag, for consumers that want to track a line rather than a point: + +| Tag | Points at | Moves when | +| --- | --- | --- | +| `latest` | the newest stable version | any stable release | +| `vMAJOR` | the newest stable version in that major | a stable release within that major | +| `vMAJOR.MINOR` | the newest stable patch in that minor | a stable patch within that minor | + +Three rules keep them safe: + +- **Prereleases never move a sliding tag.** Only a stable release advances one, + so a sliding tag never points at something not promoted for adoption. +- **A sliding tag never moves backwards.** It only advances, so a consumer + following it never silently downgrades. +- **Sliding tags are conveniences, not references.** They are how a consumer + *finds* a version, not how one **pins** to it; anything requiring + reproducibility pins to the immutable version, digest, or SHA + ([supply chain](../../Coding-Standards/Security.md#supply-chain)). + ## Serialised releases Release runs for the same ref are **serialised** and **queue rather than @@ -123,11 +208,12 @@ release, and its runs are serialised like any other. | Bump label / prerelease / RC | PR label, or `workflow_dispatch` input | | Path filter | `.github/release.config.yml` | | Prerelease cleanup toggle | release config / workflow input | -| Publish target | reusable-workflow input + GitHub environment | +| Publishing targets | reusable-workflow input + GitHub environment; see [Publishing Targets](design-publishing-targets.md) | ## Where this connects - [Spec](spec.md) — the requirements this design delivers. +- [Publishing Targets](design-publishing-targets.md) — the contract each destination documents. - [Downstream Release Propagation](../downstream-release-propagation/design.md) — consumes the release note and immutable reference. - [GitHub Actions](../../Coding-Standards/GitHub-Actions.md) — how the workflow itself is authored (SHA pins, least privilege, concurrency). - [Security](../../Coding-Standards/Security.md#supply-chain) — why consumers pin to immutable references. diff --git a/src/docs/Capabilities/release-management/index.md b/src/docs/Capabilities/release-management/index.md index dd1f3d1..cc4d98a 100644 --- a/src/docs/Capabilities/release-management/index.md +++ b/src/docs/Capabilities/release-management/index.md @@ -17,5 +17,6 @@ ritual. | --- | --- | | [Spec](spec.md) | Requirements for release management — automatic, label-driven, versioned releases driven entirely on the GitHub platform. | | [Design](design.md) | How release management is built — a shared reusable workflow that reads pull-request labels, computes the SemVer bump, and cuts the release. | +| [Publishing Targets](design-publishing-targets.md) | The contract every publishing destination documents, with GitHub Releases as the reference target. | diff --git a/src/docs/Capabilities/release-management/spec.md b/src/docs/Capabilities/release-management/spec.md index 87e0029..cbeeaf8 100644 --- a/src/docs/Capabilities/release-management/spec.md +++ b/src/docs/Capabilities/release-management/spec.md @@ -31,28 +31,50 @@ this capability governs the release. If no, there is nothing to release. ## Requirements - **Semantic versioning.** Versions follow [SemVer 2.0.0](https://semver.org/) (`vMAJOR.MINOR.PATCH`), derived automatically — never written by hand. -- **Label-driven bump.** The bump level is a pull-request label — `Major` / `Minor` / `Patch` / `NoRelease` — defaulting to `Patch`. Conventional commit messages are **not** required. +- **Label-driven bump, stated explicitly.** The bump level is a pull-request label — `Major` / `Minor` / `Patch` / `NoRelease`. Exactly one bump label MUST be present, and there is **no default**: an unlabelled pull request is not releasable, and the release fails closed rather than assuming the smallest bump. Requiring the label makes the versioning decision a reviewed decision instead of an omission. Conventional commit messages are **not** required. - **A release per merge.** One merged PR to a release branch is one release, and the PR review gate is the release gate. Direct pushes and manual dispatch also release. +- **Version before build.** The version MUST be resolved before the artifact is built, so the version is part of the artifact's identity rather than a label attached afterwards. +- **Build once.** The artifact MUST be built exactly once and MUST NOT be altered after it is built. The same bytes flow through validation and publishing. Rebuilding to publish means the tested artifact and the published artifact are different artifacts. - **Stable and prerelease.** Every release is either **stable** (the latest version to adopt) or a **prerelease** (testable, not promoted to latest). A prerelease MUST be obtainable from an open pull request and/or from a prerelease branch. - **Serialised releases.** Only one release process runs against a given version of the codebase (the same ref) at a time. A release mutates shared, version-anchored state — the tag, the version counter, the published artifact — so overlapping runs on the same ref MUST NOT race, and an in-flight release is never interrupted. - **A single production authority.** Exactly one branch is in charge of the production (stable) version, so consumers get one unambiguous latest stable release and two branches can never publish competing production releases. - **Notes from the contributor's own words.** The GitHub Release name is the version; its body is assembled from material the contributor already wrote (PR title + description, or commit message, or collected history). The PR description is therefore written for consumers. - **Only artifact-affecting changes release.** A change that does not flow into the artifact (documentation, CI config) MUST NOT produce a release — though validation still runs on every merge. - **Immutable references.** Consumers pin to the most immutable reference available — a container digest or a commit SHA — never a mutable tag. +- **Publish through a target contract.** Every publishing destination is reached through the same [publishing-target contract](design-publishing-targets.md), so the release process stays one process regardless of how many destinations a repository has. Adding a destination supplies a contract and a publish step; it MUST NOT change the release process. +- **All-or-nothing across targets.** Where a repository publishes one artifact to more than one destination, a version MUST NOT end up present on some destinations and absent from others. Partial publication is a failure, reported as one, and resumed by completing the remaining destinations with the same immutable artifact and version. +- **Recovery distinguishes retries from changed output.** Retrying validation or publication of unchanged bytes MUST reuse their artifact and version. A correction that changes the bytes MUST create a new versioned artifact; an existing version is never overwritten or reused. - **Standard GitHub primitives only.** Pull requests, labels, comments, and workflow dispatch — no external tooling beyond `gh` and GitHub Actions. +### Consumer update policies + +A consumer chooses how much version movement it accepts. Selecting a policy is a **consumer-side** concern — the release capability's obligation is to publish versions that make every policy expressible: + +| Policy | Accepts | Suits | +| --- | --- | --- | +| **Latest** | any newer version, including major | consumers that track the current release and have tests to catch breakage | +| **Lock major boundary** | newer minor and patch within one major | the default for a library dependency under SemVer | +| **Lock minor boundary** | newer patch only | consumers that accept fixes but no new surface | +| **Lock specific version** | nothing; movement is an explicit change | consumers under change control | +| **Lock immutable fingerprint** | nothing; the reference is a digest or SHA | consumers that require the exact bytes to be provable | + +Because versions are semantic, immutable, and published once, a consumer can adopt any of these without the producer knowing which one it chose. + ## Success criteria - Merging a labelled PR to a release branch produces a GitHub Release, a git tag, and (where one exists) a published artifact, with no manual step. -- The version bump matches the PR's label every time; a conflicting or ambiguous label set is **rejected**, never guessed. +- The version bump matches the PR's label every time; a missing, conflicting, or ambiguous label set is **rejected**, never guessed. +- The artifact that consumers download is byte-identical to the artifact that passed validation. - A documentation-only merge produces no new version but still runs its CI checks. - Two release runs for the same ref never overlap; the second waits for the first to finish rather than racing it. - Only the single production branch ever publishes a stable release. +- A version that reaches one publishing target reaches all of them, or the release is reported as failed. - Every release is linkable and records its immutable artifact reference. ## Where this connects - [Design](design.md) — how these requirements are delivered. +- [Publishing Targets](design-publishing-targets.md) — the contract each destination documents. - [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 change-type labels that drive the bump. - [Dependency Updates](../dependency-updates/spec.md) — update PRs are artifact-affecting and release through this capability. diff --git a/src/docs/Capabilities/repository-governance/design-types.md b/src/docs/Capabilities/repository-governance/design-types.md new file mode 100644 index 0000000..665a21d --- /dev/null +++ b/src/docs/Capabilities/repository-governance/design-types.md @@ -0,0 +1,206 @@ +--- +title: Repository Types +description: The repository type catalogue — the branch-model types, the layering types, the exemption type, and the rules by which they compose. +--- + +# Repository Types + +A repository's type is the whole of its governance input. This page is the +catalogue: what each type means, what it implies, and which combinations are +valid. + +Types divide into three kinds, and the distinction is what makes them +composable: + +| Kind | Decides | How many a repository has | +| --- | --- | --- | +| **Branch-model type** | Which branches exist, which are protected, and how a change merges | Exactly one, whether declared or defaulted | +| **Layering type** | An additional obligation — a required check, an extra file, a review adjustment | Any number, including none | +| **Exemption type** | That the baseline does not apply | Alone, or not at all | + +A branch-model type answers "how does a change reach the protected branch?" A +layering type answers "what else must be true before it does?" Because those are +different questions, they MUST NOT be values in competition — which is why the +type property is multi-valued ([FR2](spec.md#classification)). + +## Branch-model types + +### Standard + +The default. One protected branch. Topic branches merge into it by any method the +organization permits. + +| | | +| --- | --- | +| Protected branches | The default branch | +| Merge methods | Any the organization permits | +| Applies to | Repositories whose history does not need to be one-commit-per-change, and which do not promote between environments | + +Standard is what a repository gets when it declares nothing. That is deliberate: +the absence of a decision MUST produce protection, not the absence of protection +([FR1](spec.md#classification)). + +### Artifact + +Artifact is a **layering type**, not a branch model. It adds a linear-history +contract to whichever branch model applies. + +| | | +| --- | --- | +| Branch model | None of its own — Standard applies by default, or Infrastructure when declared | +| Protected branches | The default branch, or the Infrastructure integration branch | +| Merge methods | Squash only where the artifact history rule applies | +| Additional rule | Linear history required | +| Applies to | Anything published under a version — packages, modules, container images, Actions, reusable workflows, extensions | + +The constraint exists because a versioned artifact's history is read backwards. A +regression is traced by bisecting releases, and a release note is assembled from +the commits between two tags. Both work when one commit is one change and fail +when a merge commit hides five. This is the history contract [release +management](../release-management/design.md#branching-model) depends on. + +### Infrastructure + +Two protected branches and a **promotion flow** between them: changes integrate +on one branch and are promoted to the other. + +| | | +| --- | --- | +| Protected branches | The integration branch (the default) and the production branch | +| Merge into the integration branch | Squash only, from topic branches | +| Merge into the production branch | Merge commit, from the integration branch only | +| Additional check | A promotion-source check that fails when the head branch is not the integration branch | +| Applies to | Repositories whose changes must be observed working in one place before reaching another | + +Two rules make the flow real rather than conventional. **The production branch +accepts only the integration branch** — enforced by a required check that reads +the head branch, because branch protection alone cannot express "from this branch +only". And **promotion merges rather than squashes**, so the individual changes +promoted stay individually visible on the production branch, rather than +collapsing into one commit that says only "promote". + +A repository on this model MAY keep a [standing promotion pull +request](../../Ways-of-Working/Branching-and-Merging.md#the-standing-promotion-pull-request) +so the difference between integrated and live is always one link away. + +## Layering types + +### Docs + +**Docs is a layering type, not a branch model.** It declares that the repository +publishes documentation, and it adds the obligation that the documentation +**builds** before a change lands. + +| | | +| --- | --- | +| Branch model | None of its own — the repository's branch-model type decides, defaulting to Standard | +| Adds | A required documentation-build check on every protected branch | +| Adds | The documentation source root and its build configuration to the required files | + +This orthogonality is the point. How documentation is published has nothing to do +with whether the repository promotes between environments, so the two MUST be +separately declarable: a repository can be an infrastructure stack whose +documentation also builds, and expressing that MUST NOT require inventing a +combined type. + +A repository that is Docs **and nothing else** — where the published site is the +product — MAY carry a weaker review gate than a repository shipping executable +code, because its build check verifies more of what could break. Whether it does +is an organization decision ([FR12](spec.md#the-review-gate)). + +### Memory + +Declares that the repository is an [agent memory +store](../agentic-development/memory-template.md): append-mostly, written by +agents in small commits, and read at the start of a session. + +| | | +| --- | --- | +| Branch model | None of its own | +| Adjusts | The pull-request requirement, which MUST NOT apply — memory is written by direct commit | + +Memory removes only that one obligation. Its protected branches still reject +deletion and force-pushes, its required checks and automated review still apply, +and merged pull-request branches still use the repository-level cleanup setting. +This targeted subtraction is why the pull-request gate is written as an exclusion +([filter by +exclusion](../../Ways-of-Working/Repository-Type-Property.md#filter-by-exclusion-not-by-inclusion)). + +## The exemption type + +### Unmanaged + +Declares that the baseline does not apply to this repository, and why. + +| | | +| --- | --- | +| Branch model | None | +| Baseline | Not applied | +| Requires | A recorded reason ([FR14](spec.md#exemption)) | +| Still requires | The files that make the repository understandable — a README, a security policy, and its agent router ([FR15](spec.md#exemption)) | + +Unmanaged exists so that "this repository is not governed" is a **statement** +rather than an oversight. Without it, the only way to express an exemption is to +leave a repository unclassified, and an unclassified repository is +indistinguishable from a forgotten one. With it, the exemptions are a list that +can be reviewed, questioned, and shortened. + +An archive, a scratch mirror, or a repository whose content is generated wholesale +by another system are the shapes this fits. A repository people actively develop +in is not. + +## Composition and precedence + +| Declared | Resulting governance | +| --- | --- | +| Nothing | Standard branch model, baseline applied | +| Standard | Standard branch model, baseline applied | +| Artifact | Standard branch model by default, plus the artifact history rule | +| Infrastructure | Promotion flow, baseline applied | +| Standard **+** Docs | Standard branch model, plus the documentation-build check | +| Artifact **+** Docs | Standard branch model by default, plus the artifact history and documentation-build rules | +| Infrastructure **+** Docs | Promotion flow, plus the documentation-build check on both protected branches | +| Infrastructure **+** Artifact | Promotion flow; the artifact history rule applies to merges into the integration branch, and the promotion merge remains a merge commit | +| Docs alone | Standard branch model by default, plus the documentation-build check | +| Memory | Baseline applied except the pull-request requirement | +| Unmanaged | No baseline; the recorded reason applies | + +Precedence, stated once: + +1. **Unmanaged wins over everything, and combines with nothing.** If it is + declared alongside another type, the declaration is contradictory, not + permissive. +2. **Exactly one branch model applies.** Standard is the default where no + branch-model value is declared; Infrastructure replaces that default when it is + declared. Artifact is a layering type, so it never competes for the branch + model. +3. **Layering types always apply.** A layering type never loses to a branch-model + type; it adds to whichever one wins. + +## Validation rules + +A declaration MUST be rejected when: + +- It contains a value outside the organization's allowed list ([FR3](spec.md#classification)). +- It contains **Unmanaged together with any other type**. Exemption is total or absent. +- It contains more than one explicit branch-model type. Standard and Infrastructure + cannot both be declared because each decides the protected-branch shape. +- It declares **Unmanaged without a reason** ([FR14](spec.md#exemption)). + +A declaration MUST be accepted when it contains one branch-model type and any set +of layering types, and when it contains only layering types — the branch model +then defaults to Standard. + +Validation belongs in [reconciliation](design.md#drift-detection-and-reconciliation) +at **Block** severity: a contradictory type does not produce weaker protection, it +produces undefined protection, and undefined protection MUST NOT be reachable by +setting a property. + +## Where this connects + +- [Spec](spec.md) — the requirements this catalogue satisfies. +- [Design](design.md) — how the types are declared and how controls read them. +- [Repository Type Property](../../Ways-of-Working/Repository-Type-Property.md) — the property mechanism, its condition pattern, and safe migration. +- [Branching and Merging](../../Ways-of-Working/Branching-and-Merging.md) — the merge models the branch-model types select. +- [Repository Standard](../../Ways-of-Working/Repository-Standard.md#required-files-by-type) — the files each type requires. +- [Release Management](../release-management/design.md) — the consumer of the Artifact history contract. diff --git a/src/docs/Capabilities/repository-governance/design.md b/src/docs/Capabilities/repository-governance/design.md new file mode 100644 index 0000000..8964dc2 --- /dev/null +++ b/src/docs/Capabilities/repository-governance/design.md @@ -0,0 +1,187 @@ +--- +title: Design +description: How repository governance is built — a multi-select type property, organization rulesets selected by type, and continuous drift detection with graded reconciliation. +--- + +# Repository Governance — Design + +Three moving parts deliver the [spec](spec.md): a **property** that carries the +declaration, **organization rulesets** whose conditions read it, and +**reconciliation** that compares the live world against what the declaration +implies. + +```mermaid +flowchart LR + decl["Type property
on the repository"] --> rs["Organization rulesets
selected by type"] + decl --> files["Required-file set
per type"] + rs --> live["Live repository
configuration"] + files --> live + live --> rec["Reconciliation"] + decl --> rec + rec --> findings["Graded findings"] +``` + +The declaration is the only per-repository input. Everything downstream of it is +organization-level configuration, which is what makes the number of things that +can drift equal to the number of types rather than the number of repositories +([NFR1](spec.md#non-functional)). + +## The declaration + +The type is a **multi-select** organization custom property, required on every +repository, defaulting to the Standard value. Multi-select rather than +single-select because branch model and layering are separate concerns +([FR2](spec.md#classification), [repository types](design-types.md)). + +The property mechanism — how conditions target it, why conditions are written as +exclusions, and how an existing condition is migrated onto it without dropping +coverage — is owned by [Repository Type +Property](../../Ways-of-Working/Repository-Type-Property.md). This design does not +restate it. + +Moving an organization from a single-select property to a multi-select `Type` +property is a schema change the platform does not perform in place. The migration +uses a uniquely named temporary multi-select property to keep every control +covered while the canonical name is recreated; the precise sequence and API +semantics are owned by [Repository Type +Property](../../Ways-of-Working/Repository-Type-Property.md#migrating-from-a-single-select-property). +Coverage is verified by asking the platform which rules apply to each repository +rather than by reasoning about condition JSON. + +## Rulesets by type + +One ruleset per governed concern, each selecting repositories by type. Rulesets +are organization-level: a repository-level branch protection would be a second +definition of the same control, and two definitions are two truths +([NFR1](spec.md#non-functional)). + +| Ruleset | Selects | Branches | Enforces | +| --- | --- | --- | --- | +| **Baseline protection** | Every type except Unmanaged | Protected branches | No deletion, no force-push, required checks | +| **Pull-request gate** | Every type except Unmanaged and Memory | Protected branches | Pull request required | +| **Artifact history** | Type includes Artifact | Default branch or Infrastructure integration branch | Squash-only merge, linear history required | +| **Promotion — integration** | Type includes Infrastructure | Integration branch | Squash-only merge | +| **Promotion — production** | Type includes Infrastructure | Production branch | Merge-commit only, promotion-source check required | +| **Documentation build** | Type includes Docs | Protected branches | Documentation-build check required | +| **Automated review** | Every type except Unmanaged | Default branch | A review is requested on every pull request; advisory, not a gate | + +Two properties of this table matter more than its contents: + +- **Conditions are exclusions, not allow-lists.** Each ruleset matches every repository and then subtracts the types that must be exempt. A type value invented later is covered by default; only a type an administrator has explicitly named ever loses coverage ([NFR5](spec.md#non-functional), [filter by exclusion](../../Ways-of-Working/Repository-Type-Property.md#filter-by-exclusion-not-by-inclusion)). +- **Rulesets layer rather than override.** A repository matching three rulesets is subject to the union of all three. Nothing needs to know what else applies, which is why a layering type can be added without touching a branch-model ruleset. + +Automatic deletion of a merged pull request's head branch is not a ruleset rule. +Reconciliation verifies the repository-level `delete_branch_on_merge` setting for +every governed repository instead. Memory therefore retains the protection, +check, review, and branch-cleanup baseline while being exempt only from the +pull-request gate; Unmanaged is the sole type that removes the baseline. + +Bypass is granted on each ruleset to a **named administrative group in +pull-request mode only** — never to individuals, and never as a blanket write +exception ([FR16](spec.md#bypass)). + +### The promotion-source check + +Branch protection can require a check; it cannot express "only from this branch". +So the constraint is implemented as a check that reads its own pull request's head +branch and fails unless it is the integration branch. Being a required check, it +inherits everything the merge gate already provides: it blocks the merge, it is +visible on the pull request, and it is bypassable only by the group the ruleset +names. + +## Required files by type + +The [Repository Standard](../../Ways-of-Working/Repository-Standard.md#required-files) +owns the file list and which type adds to it. This design owns only the +enforcement: presence is checked by reconciliation on the default branch, at +**Block** severity for the files that make a repository contributable and +**Report** severity for the rest. + +## Drift detection and reconciliation + +Rulesets prevent unwanted changes to branches. They do not prevent a repository +from being *configured* into a state its declaration does not describe — a +required check renamed, a merge method re-enabled, a required file deleted, a type +value that no longer validates. Reconciliation is the loop that closes that gap: +**compare the declared intent against the live world, continuously, and grade +every difference.** + +The loop is generic. It is a scheduled comparison plus a graded response, and it +is implementable as a workflow in an administrative repository, as an application +holding the organization's configuration, or as a policy engine. What matters is +the contract below, not the implementation that satisfies it. + +### What is compared + +| Comparison | Question | +| --- | --- | +| Type is set | Does the repository carry a type value at all? | +| Type is valid | Is every value in the organization's allowed list? | +| Type composes | Is the combination valid ([validation rules](design-types.md#validation-rules))? | +| Exemption is justified | Does an exempted repository carry a recorded reason? | +| Rulesets apply | Do the rulesets the type implies actually evaluate against this repository? | +| Branch shape matches | Do the protected branches, merge methods, and required checks match what the type declares? | +| Required files present | Does the default branch carry the governed baseline and the files its type adds, or the explicit Unmanaged discoverability minimum? | + +### Severity decides the response + +| Severity | Meaning | Response | +| --- | --- | --- | +| **Block** | The declaration is unusable or the repository is not contributable | A failing check on the change that introduces it | +| **Warn** | Live configuration diverges from the declaration but the repository still functions | A comment on the affected pull request | +| **Report** | A standing condition that needs review rather than an immediate fix | A tracking issue, opened once and updated thereafter | + +Grading is what keeps the loop usable. A single severity forces a choice between +blocking on things that do not warrant it and merely reporting things that do; the +result of either is that findings stop being read. + +### When it runs + +On repository creation, on a change to the type property, on a push to a default +branch (for the file comparisons), and on a schedule that catches configuration +changed out of band. The schedule is the one that matters most, because +out-of-band configuration change is the drift the event triggers cannot see. + +### Idempotence + +Reconciliation reports the same finding at most once. A finding is identified by +the repository, the comparison, and the specific difference; a run that +re-discovers an existing finding **updates** it rather than creating a second one, +and a finding whose condition has been resolved is closed +([FR20](spec.md#reconciliation)). Without this, the loop's output degrades into a +stream nobody can distinguish new findings in. + +### A finding names its remedy + +Every finding states what is wrong, what the declared type requires instead, and +where the rule is written down. A finding that reports only a mismatch transfers +the work of interpretation to the reader; a finding that names the remedy is +actionable by whoever receives it, human or agent +([FR21](spec.md#reconciliation)). + +Reconciliation **reports** by default. Whether it also *applies* a remedy is a +separate decision per comparison, and one that MUST be made deliberately: an +automated fix to a protection is itself a change to a control, and it belongs +under the same review as any other ([decision before +change](../../Ways-of-Working/Principles/AI-First-Development.md#decision-before-change)). + +## Configuration surface + +| Setting | Where | +| --- | --- | +| Allowed type values | Organization custom-property schema | +| Which controls a type implies | Organization rulesets | +| Required approvals per type | Organization rulesets | +| Bypass group | Organization rulesets | +| Required-file set per type | [Repository Standard](../../Ways-of-Working/Repository-Standard.md#required-files) | +| Comparison severities | Reconciliation configuration | +| Reconciliation schedule | Reconciliation configuration | + +## Where this connects + +- [Spec](spec.md) — the requirements this design delivers. +- [Repository Types](design-types.md) — the catalogue the rulesets select on. +- [Repository Type Property](../../Ways-of-Working/Repository-Type-Property.md) — the property mechanism and condition migration. +- [Repository Standard](../../Ways-of-Working/Repository-Standard.md) — the required-file sets reconciliation checks. +- [Branching and Merging](../../Ways-of-Working/Branching-and-Merging.md) — the merge gate and who may approve. +- [Automation Labels](../../Ways-of-Working/Automation-Labels.md) — the namespaced labels reconciliation and the rulesets rely on. diff --git a/src/docs/Capabilities/repository-governance/index.md b/src/docs/Capabilities/repository-governance/index.md new file mode 100644 index 0000000..447af72 --- /dev/null +++ b/src/docs/Capabilities/repository-governance/index.md @@ -0,0 +1,25 @@ +--- +title: Repository Governance +description: How every repository in an organization is classified, protected, and continuously reconciled against the controls its classification declares. +--- + +# Repository Governance + +Making "every repository is protected" true of every repository without anyone +checking: a repository declares **what kind of thing it is**, and that +declaration is the only input that decides which branch protections, required +checks, review gates, and required files apply to it. + +Classification is data on the repository. Controls are organization-level +configuration that reads that data. Nothing is configured per repository, so +there is nothing per repository to drift. + + + +| Page | Description | +| --- | --- | +| [Spec](spec.md) | Requirements for repository governance — classification-driven protection, a common baseline for every governed repository, explicit exemption, and continuous reconciliation. | +| [Design](design.md) | How repository governance is built — a multi-select type property, organization rulesets selected by type, and continuous drift detection with graded reconciliation. | +| [Repository Types](design-types.md) | The repository type catalogue — the branch-model types, the layering types, the exemption type, and the rules by which they compose. | + + diff --git a/src/docs/Capabilities/repository-governance/spec.md b/src/docs/Capabilities/repository-governance/spec.md new file mode 100644 index 0000000..4d968c3 --- /dev/null +++ b/src/docs/Capabilities/repository-governance/spec.md @@ -0,0 +1,116 @@ +--- +title: Spec +description: Requirements for repository governance — classification-driven protection, a common baseline for every governed repository, explicit exemption, and continuous reconciliation. +--- + +# Repository Governance — Spec + +## Premise + +An organization's repositories are not uniform. A published package needs a +bisectable history; an infrastructure stack needs changes to reach a lower +environment before production; a documentation site needs its build to pass +before it lands. Configuring each repository to match its own shape produces as +many configurations as there are repositories, all of them drifting, none of them +reviewable together. + +Governance MUST therefore be inverted: a repository **declares its kind**, and +the organization holds one configuration per kind. The declaration is the only +per-repository decision, and it is a value, not a configuration. Every control +follows from it, so the number of things that can drift is the number of kinds, +not the number of repositories. + +### Principles + +This capability rests on the [Principles](../../Ways-of-Working/Principles/index.md): + +- **[Everything as Code](../../Ways-of-Working/Principles/Engineering-Practices.md#everything-as-code).** Types, rulesets, and required files are version-controlled configuration, reviewable as a diff. +- **[Smart defaults, local overrides](../../Ways-of-Working/Principles/Software-Design.md#smart-defaults-local-overrides).** A repository is governed because it exists, not because someone remembered to configure it. Opting out is an explicit, recorded act. +- **[Least-privilege](../../Ways-of-Working/Principles/Purpose-and-Direction.md#least-privilege).** Bypass is granted to a named administrative group, never to individuals and never permanently. +- **[Decision before change](../../Ways-of-Working/Principles/AI-First-Development.md#decision-before-change).** Every write to a protected branch passes through a pull request that can be read, checked, and approved. +- **[Extensible by default](../../Ways-of-Working/Principles/Software-Design.md#extensible-by-default).** A new repository shape is a new type value and one ruleset, not a change to the ones that exist. + +## Scope + +**In scope.** How a repository is classified; which branch model, required +checks, review gate, merge methods, and required files each classification +implies; how classifications combine; how a repository is exempted; and how live +configuration is continuously compared against the declaration. + +**Out of scope.** What a change must contain to be accepted — that is +[Contribution Workflow](../../Ways-of-Working/Contribution-Workflow.md), [PR +Format](../../Ways-of-Working/PR-Format.md), and the coding standards. Also out of +scope: the platform's own security baseline, which layers underneath this +framework and is owned by whoever operates the platform. This framework MUST NOT +weaken that baseline and MUST NOT restate it. + +## Requirements + +### Classification + +- **FR1 — Every repository carries a type.** A repository MUST declare its kind through a repository-level property the organization defines. An undeclared repository MUST be treated as the default type rather than as ungoverned. +- **FR2 — The type is multi-valued.** The property MUST accept more than one value, because a repository's branch model and its build obligations are separate concerns. See [Repository Types](design-types.md). +- **FR3 — Only declared values are accepted.** A value outside the organization's allowed list MUST be rejected as a misconfiguration, not silently ignored. +- **FR4 — Combinations are validated.** Some combinations are contradictory. Validation rules MUST be stated once and enforced, not left to the reader ([composition rules](design-types.md#composition-and-precedence)). + +### The common baseline + +Every governed repository — every repository not explicitly exempted — MUST be +subject to the same baseline, regardless of type: + +- **FR5 — Writes go through pull requests.** A protected branch MUST NOT accept a direct push. +- **FR6 — Protected branches cannot be deleted or force-pushed.** History on a protected branch is append-only. +- **FR7 — Required checks gate the merge.** A pull request MUST NOT be mergeable until the checks its type declares have passed. +- **FR8 — A review is requested automatically.** Every pull request MUST have a review requested without the author asking. Whether that review *gates* the merge is a separate rule ([the review gate](#the-review-gate)). +- **FR9 — Merged branches are deleted.** The repository-level `delete_branch_on_merge` setting MUST delete the head branch of a merged pull request automatically. A protected branch MUST NOT be deleted by this setting. +- **FR10 — Contribution rules are present.** Every governed repository MUST carry the [required files](../../Ways-of-Working/Repository-Standard.md#required-files) its type declares, so a contributor arriving at the repository can act without leaving it. + +### The review gate + +- **FR11 — Approval comes from a different identity than the author.** Where an approving review is required, it MUST NOT be satisfiable by the identity that authored the change, nor by the workflow identity that ran its checks. See [who approves](../../Ways-of-Working/Branching-and-Merging.md#who-approves). +- **FR12 — The number of required approvals is an organization decision.** The gate's strength is set per organization and per type, and MAY be zero where the checks and the automated review are judged sufficient. It MUST be stated in configuration rather than assumed. + +### Exemption + +- **FR13 — Exemption is a declared type, not an absence.** A repository that cannot meet the baseline MUST declare that explicitly. Being unclassified MUST NOT be a way to escape governance. +- **FR14 — An exemption records its reason.** An exempted repository MUST carry a machine-readable reason for the exemption, so the set of exemptions can be reviewed as a list rather than rediscovered. +- **FR15 — Exemption is narrow.** An exempted repository MUST still be discoverable: it carries the repository files that let a reader and an agent understand it, even where the pull-request requirement does not apply. + +### Bypass + +- **FR16 — Bypass is granted to a group, never a person.** Only a named administrative group MAY bypass a protection, and the grant MUST be recorded in the same configuration as the protection itself. +- **FR17 — Bypass is attributable.** Every use of a bypass MUST be visible in the organization's audit trail, so the exception can be found after the fact. + +### Reconciliation + +- **FR18 — Declared and live configuration are compared continuously.** Automation MUST periodically compare each repository's live configuration against what its declared type requires, and report every difference. See [reconciliation](design.md#drift-detection-and-reconciliation). +- **FR19 — Findings are graded, not uniform.** Each difference MUST carry a severity that determines the response: refuse the change, warn on it, or record it for review. +- **FR20 — Reconciliation is idempotent.** Running it twice MUST produce the same result and MUST NOT create a second report for the same finding. +- **FR21 — A finding names its remedy.** A reported difference MUST state what is wrong, what the declared type requires, and where the rule is documented. A finding a reader cannot act on is noise. + +### Non-functional + +- **NFR1 — One definition per control.** A control MUST be defined once, at the organization, and MUST NOT be duplicated into repository-level configuration. Two definitions are two truths. +- **NFR2 — Self-service classification.** Changing a repository's type MUST be within the repository owner's authority and MUST take effect without an administrator editing a control. +- **NFR3 — Derivable inventory.** The set of governed repositories, their types, and their exemptions MUST be derivable from configuration and the platform API, without a maintained-by-hand list. +- **NFR4 — Auditable by diff.** Any change to which controls apply MUST be visible as a change to version-controlled configuration. +- **NFR5 — Extending is additive.** Introducing a type MUST NOT require editing the conditions of existing controls ([filter by exclusion](../../Ways-of-Working/Repository-Type-Property.md#filter-by-exclusion-not-by-inclusion)). + +## Success criteria + +- A newly created repository is governed by the baseline before anyone configures it. +- A repository's protections can be predicted from its type alone, without opening its settings. +- A contradictory type combination is rejected at declaration rather than producing undefined protection. +- The complete list of exempted repositories, each with its reason, is produced by a query. +- A repository whose live configuration no longer matches its declared type is reported without anyone noticing it first. +- Introducing a new repository shape adds one type value and one control, and changes no existing control's condition. + +## Where this connects + +- [Design](design.md) — how classification, controls, and reconciliation are built. +- [Repository Types](design-types.md) — the type catalogue, what each implies, and how types compose. +- [Repository Type Property](../../Ways-of-Working/Repository-Type-Property.md) — the property mechanism and safe migration of its conditions. +- [Repository Standard](../../Ways-of-Working/Repository-Standard.md) — the files a governed repository carries. +- [Organization Standard](../../Ways-of-Working/Organization-Standard.md) — what an organization must define centrally for this to be enforceable. +- [Branching and Merging](../../Ways-of-Working/Branching-and-Merging.md) — the merge models the types select between. +- [Repository Segmentation](../../Ways-of-Working/Repository-Segmentation.md) — why a repository has one shape to declare in the first place. diff --git a/src/docs/Coding-Standards/Dependencies.md b/src/docs/Coding-Standards/Dependencies.md index aef7a84..89dc5aa 100644 --- a/src/docs/Coding-Standards/Dependencies.md +++ b/src/docs/Coding-Standards/Dependencies.md @@ -5,7 +5,7 @@ description: How dependencies are pinned and kept current — the locking spectr # Dependencies -Every dependency we consume — a PowerShell module, a GitHub Action, a container base image, a .NET package, a Terraform provider — is both a convenience and part of our [attack surface](Security.md#supply-chain). How we depend on one is a single decision made twice: **how tightly to pin it** (how much version drift we accept) and **how it moves forward** (how new versions reach us). Get the balance wrong in either direction and it costs us. +Every consumed dependency — a PowerShell module, a GitHub Action, a container base image, a .NET package, a Terraform provider — is both a convenience and part of the [attack surface](Security.md#supply-chain). Depending on one is a single decision made twice: **how tightly to pin it** (how much version drift is acceptable) and **how it moves forward** (how new versions arrive). Getting the balance wrong in either direction has a cost. This is the ecosystem-agnostic standard; the per-tool standards apply it. [PowerShell → Version Constraints](PowerShell/Version-Constraints.md) expresses it for modules and packages, and [GitHub Actions → Pin every action to a full commit SHA](GitHub-Actions.md#pin-every-action-to-a-full-commit-sha) expresses it for Actions and images. The [Dependency Updates](../Capabilities/dependency-updates/index.md) capability is the automation that keeps pins current. @@ -13,12 +13,12 @@ This is the ecosystem-agnostic standard; the per-tool standards apply it. [Power A pin has two independent parts; keep them separate. -- **Identity** — *which* artifact, proven. A name alone can be squatted, re-tagged, or repointed at new code, so an **identity pin** binds to immutable bytes: a module `GUID`, an Action or commit **SHA**, an image **digest**. It answers "is this the exact thing I vetted?" and is orthogonal to the version. +- **Identity** — *which* artifact, proven. A name alone can be squatted, re-tagged, or repointed at new code, so an **identity pin** binds to immutable bytes: a module `GUID`, an Action or commit **SHA**, an image **digest**. It answers "is this the exact thing that was vetted?" and is orthogonal to the version. - **Version tightness** — *which versions* of that artifact are acceptable, from an exact pin to floating latest. The strongest posture combines both: a verified identity **and** a deliberate version. Identity is the integrity control; tightness is the velocity-versus-risk control below. -Before you choose a pin, decide whether you should add the dependency at all. For modules and libraries we build, the default is to **avoid introducing a new third-party dependency when the capability can reasonably be implemented with PowerShell, the .NET base class library, or code we own**. Every external DLL, package, or module adds another update stream, trust boundary, and failure mode to carry for the lifetime of the module. Spend a bit more effort up front if that keeps the shipped surface smaller and the ownership clearer. +Before choosing a pin, decide whether the dependency belongs at all. For modules and libraries built here, the default is to **avoid introducing a new third-party dependency when the capability can reasonably be implemented with PowerShell, the .NET base class library, or owned code**. Every external DLL, package, or module adds another update stream, trust boundary, and failure mode to carry for the lifetime of the module. Spend a bit more effort up front if that keeps the shipped surface smaller and the ownership clearer. ## The locking spectrum diff --git a/src/docs/Coding-Standards/Documentation.md b/src/docs/Coding-Standards/Documentation.md index b8a8cd5..f74afbc 100644 --- a/src/docs/Coding-Standards/Documentation.md +++ b/src/docs/Coding-Standards/Documentation.md @@ -16,7 +16,7 @@ Distance between a thing and its documentation is the rate at which they drift a | Why a line exists | In a comment next to the line | | How a function works | In comment-based help next to the function | | What a repo is | In the README at the repository root | -| How we work | In this org-level docs site | +| How the work is done | In this org-level docs site | | Why a decision held | In the issue that produced it | ## Self-documenting code first diff --git a/src/docs/Coding-Standards/GitHub-Actions.md b/src/docs/Coding-Standards/GitHub-Actions.md index 5c43147..3e3e8d0 100644 --- a/src/docs/Coding-Standards/GitHub-Actions.md +++ b/src/docs/Coding-Standards/GitHub-Actions.md @@ -25,7 +25,7 @@ them to point at different code. A full commit SHA is **immutable**. - **Pin every `uses:` to a full 40-character commit SHA.** Keep the human version as a trailing comment so reviewers know the intended release. -- This applies to **all** actions — third-party, first-party, and our own +- This applies to **all** actions — third-party, first-party, and internally authored internal actions alike. ```yaml @@ -36,7 +36,7 @@ them to point at different code. A full commit SHA is **immutable**. - name: Set up Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 -# Avoid — mutable tag; the referenced code can change under us +# Avoid — mutable tag; the referenced code can change without notice - name: Check out the repository uses: actions/checkout@v6 ``` diff --git a/src/docs/Coding-Standards/Naming.md b/src/docs/Coding-Standards/Naming.md index 7f4131f..9a3db9e 100644 --- a/src/docs/Coding-Standards/Naming.md +++ b/src/docs/Coding-Standards/Naming.md @@ -5,7 +5,7 @@ description: Names that reveal intent, consistently, in every language. # Naming -Names are the primary interface to code. A good name removes the need for a comment; a bad name survives every refactor and misleads for years. Naming is the cheapest documentation we have — spend on it. +Names are the primary interface to code. A good name removes the need for a comment; a bad name survives every refactor and misleads for years. Naming is the cheapest documentation available — spend on it. ## Principles diff --git a/src/docs/Coding-Standards/PowerShell/Messaging.md b/src/docs/Coding-Standards/PowerShell/Messaging.md index 7c462de..0741348 100644 --- a/src/docs/Coding-Standards/PowerShell/Messaging.md +++ b/src/docs/Coding-Standards/PowerShell/Messaging.md @@ -70,7 +70,7 @@ When writing messages, ask: - It exposes implementation details (payload, headers, internal variables, timing). - It helps answer "why did the code take this path?" or "what is the state inside this function?". -3. **Am I unsure?** Prefer `Write-Verbose` — it's safer to be slightly more verbose at the normal level than to hide troubleshooting context that operators need. +3. **Still unsure?** Prefer `Write-Verbose` — it's safer to be slightly more verbose at the normal level than to hide troubleshooting context that operators need. ## Enabling messages at runtime diff --git a/src/docs/Coding-Standards/PowerShell/Version-Constraints.md b/src/docs/Coding-Standards/PowerShell/Version-Constraints.md index 0cf0a5f..fb3d073 100644 --- a/src/docs/Coding-Standards/PowerShell/Version-Constraints.md +++ b/src/docs/Coding-Standards/PowerShell/Version-Constraints.md @@ -96,7 +96,7 @@ Here a bare `Version="16.0.0"` is a *minimum* — NuGet's own semantics — the - `RequiredVersion` cannot be combined with `ModuleVersion` or `MaximumVersion`. A range needs the floor **and** ceiling keys together; the exact pin stands alone. - `MaximumVersion` is **inclusive** and understands the `N.*` wildcard, so a major lock is `MaximumVersion = '6.*'` — every `6.x`, nothing in `7` — with no `6.999.999` sentinel. - The module `GUID` pins **identity**, orthogonal to the version: a supply-chain control, not part of the version constraint (see [Security → Supply chain](../Security.md#supply-chain)). Add it to any lock. -- The `#Requires -Version` statement and a manifest's `PowerShellVersion` are **not** package ranges — each is a single minimum engine version (we target PowerShell 7). Leave them as a bare version: `#Requires -Version 7.0`. +- The `#Requires -Version` statement and a manifest's `PowerShellVersion` are **not** package ranges — each is a single minimum engine version (the target is PowerShell 7). Leave them as a bare version: `#Requires -Version 7.0`. ## Not a version range — pin by digest diff --git a/src/docs/Coding-Standards/PowerShell/index.md b/src/docs/Coding-Standards/PowerShell/index.md index 26c7771..33e30f0 100644 --- a/src/docs/Coding-Standards/PowerShell/index.md +++ b/src/docs/Coding-Standards/PowerShell/index.md @@ -5,7 +5,7 @@ description: Cross-platform PowerShell 7 — the conventions shared by every scr # PowerShell -How PowerShell is written across the ecosystem. PowerShell is the tool for operational automation — talking to platform APIs, orchestrating cross-platform tasks, and gluing tools together. We target **PowerShell 7 LTS** (the cross-platform `pwsh`) and lean on PowerShell's advanced-function machinery rather than plain scripts. +How PowerShell is written across the ecosystem. PowerShell is the tool for operational automation — talking to platform APIs, orchestrating cross-platform tasks, and gluing tools together. The target is **PowerShell 7 LTS** (the cross-platform `pwsh`), leaning on PowerShell's advanced-function machinery rather than plain scripts. This standard builds on the [language-agnostic baseline](../index.md); where the two overlap, the baseline rules apply and the conventions here add the PowerShell specifics. PowerShell is a heavily used language, so its standard **nests**: the shared conventions live on this page, and each construct — functions, classes, scripts — has its own page with the doc requirements, formatting, and section structure for that construct. @@ -66,8 +66,8 @@ Beyond the basics, these language-specific habits keep PowerShell correct and fa - **Put `$null` on the left of a comparison** — `$null -eq $x`, never `$x -eq $null`. Against a collection the right-hand form *filters* rather than tests. Use `-contains` / `-in` for membership, never `-eq`. - **Match text with the operator built for it.** Use `-like` for wildcard patterns and `-match` for regular expressions instead of hand-rolled string surgery; both default to case-insensitive, so add the `-c` prefix (`-clike`, `-cmatch`, `-ceq`) when a comparison must be case-sensitive. - **Use the built-in intent checks for strings and wildcards.** Use `[string]::IsNullOrWhiteSpace($value)` for blank input and `[System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($pattern)` when deciding whether a value contains wildcard syntax. -- **Prefer owned code over third-party dependencies in modules.** For a module we ship, exhaust PowerShell itself, the .NET base class library, and code we already own before adding an external module, DLL, or package. A third-party dependency is a long-term trust and maintenance commitment, so take it only when the capability is large enough or specialized enough that owning it ourselves is the worse trade. -- **Reuse before you build.** Work down the [reuse order](../Functions.md#reuse-before-you-build) inside the code we control — a built-in cmdlet or operator, then an existing function (public or private), then shared code we own, then new code. Reach for a trusted external module (`#Requires -Modules` / `RequiredModules`) only after that bar is cleared, and state its acceptable versions as a [version range](Version-Constraints.md). +- **Prefer owned code over third-party dependencies in modules.** For a shipped module, exhaust PowerShell itself, the .NET base class library, and already-owned code before adding an external module, DLL, or package. A third-party dependency is a long-term trust and maintenance commitment, so take it only when the capability is large enough or specialized enough that owning it is the worse trade. +- **Reuse before you build.** Work down the [reuse order](../Functions.md#reuse-before-you-build) inside the controlled code — a built-in cmdlet or operator, then an existing function (public or private), then shared owned code, then new code. Reach for a trusted external module (`#Requires -Modules` / `RequiredModules`) only after that bar is cleared, and state its acceptable versions as a [version range](Version-Constraints.md). - **PowerShell already *is* .NET; work at that level rather than wrapping it.** Casts, type accelerators (`[datetime]`, `[int]`), the `-split` / `-replace` / `-match` operators, and member methods (`.Trim()`, `.Where()`) all resolve to the base class library — using .NET means reaching for BCL types and methods for the computation, not restating everything as `[Namespace.Type]::Method(...)`. Where idiomatic PowerShell already resolves to the same .NET call, leave it; reach for explicit .NET only where it is measurably faster or more precise, and keep cmdlets and the pipeline where you need them for glue or readability. - **Do the work in .NET when you implement it.** When you write the logic yourself — or fix an internal function that is too slow or imprecise on a hot path — call the .NET base class library directly instead of a cmdlet pipeline: `[System.IO.File]::ReadAllText($path)` over `Get-Content -Raw`, `[System.IO.Path]::Combine(...)` for paths, `[System.Text.StringBuilder]` for repeated concatenation, `[int]::TryParse(...)` for parsing. .NET methods are faster and their contracts are precise; keep cmdlets where their clarity is worth more than the speed. The next two rules are specific cases. - **Suppress unwanted output with `$null = ...`** (or `[void]` for method calls), not `| Out-Null` — the pipeline form is markedly slower on hot paths. diff --git a/src/docs/Coding-Standards/Security.md b/src/docs/Coding-Standards/Security.md index 4d9a72d..93740b6 100644 --- a/src/docs/Coding-Standards/Security.md +++ b/src/docs/Coding-Standards/Security.md @@ -5,7 +5,7 @@ description: Least privilege, secret hygiene, and the OWASP baseline. # Security -Security is a property of how we build, not a phase at the end. The cheapest vulnerability to fix is the one caught in the editor; the most expensive is the one found in production. Shift it left. +Security is a property of how software is built, not a phase at the end. The cheapest vulnerability to fix is the one caught in the editor; the most expensive is the one found in production. Shift it left. ## Least privilege, everywhere @@ -38,7 +38,7 @@ All code is written to be free of the vulnerabilities in the [OWASP Top 10](http ## Supply chain -Our dependencies are part of our attack surface. +Dependencies are part of the attack surface. - **Pin dependencies** to a known-good version. Pin GitHub Actions to a full commit SHA, not a moving tag. - **Automate updates** with a dependency bot, so patches land quickly and reviewably. diff --git a/src/docs/Coding-Standards/Testing.md b/src/docs/Coding-Standards/Testing.md index 4086ceb..a66942b 100644 --- a/src/docs/Coding-Standards/Testing.md +++ b/src/docs/Coding-Standards/Testing.md @@ -5,7 +5,7 @@ description: The executable specification — test-first, locally runnable, dete # Testing -Tests are the executable specification. They define the behavior we want, prove we built it, and catch the day it breaks. They are written as part of the work — not bolted on afterward. +Tests are the executable specification. They define the intended behavior, prove it was built, and catch the day it breaks. They are written as part of the work — not bolted on afterward. ## Test-first diff --git a/src/docs/Coding-Standards/TypeScript.md b/src/docs/Coding-Standards/TypeScript.md index c2a90d3..05dee83 100644 --- a/src/docs/Coding-Standards/TypeScript.md +++ b/src/docs/Coding-Standards/TypeScript.md @@ -5,7 +5,7 @@ description: ES modules, strict-mode typing, pinned dependencies, and the Pretti # TypeScript -How TypeScript is written across the ecosystem. TypeScript is the language for Node-based tooling, GitHub Actions written in JavaScript, and **VS Code extensions**. We target the **latest stable TypeScript** on the **Node.js Active LTS**, ship **ES modules**, and treat the compiler's strict mode as non-negotiable. +How TypeScript is written across the ecosystem. TypeScript is the language for Node-based tooling, GitHub Actions written in JavaScript, and **VS Code extensions**. The target is the **latest stable TypeScript** on the **Node.js Active LTS**, shipping **ES modules**, treating the compiler's strict mode as non-negotiable. This standard builds on the [language-agnostic baseline](index.md); where the two overlap, the baseline rules apply and the conventions below add the TypeScript specifics. diff --git a/src/docs/Coding-Standards/index.md b/src/docs/Coding-Standards/index.md index 14dae0f..069dc66 100644 --- a/src/docs/Coding-Standards/index.md +++ b/src/docs/Coding-Standards/index.md @@ -65,4 +65,4 @@ Every standard here serves that single fact. When a rule and readability disagre ## How standards evolve -Standards are evergreen, not frozen. When one stops serving us, we change it — in a pull request, against this repository, with the reasoning written down. A standard that cannot be justified is a standard that should be removed. +Standards are evergreen, not frozen. When one stops serving its purpose, it changes — in a pull request, against this repository, with the reasoning written down. A standard that cannot be justified is a standard that should be removed. diff --git a/src/docs/Dictionary/index.md b/src/docs/Dictionary/index.md index c93a066..29fad16 100644 --- a/src/docs/Dictionary/index.md +++ b/src/docs/Dictionary/index.md @@ -55,7 +55,7 @@ Every identity — human, agent, or workflow — gets only the permissions it ne ### LTS -Long-Term Support — a release line maintained with fixes for an extended period. We target current LTS runtimes rather than legacy editions. +Long-Term Support — a release line maintained with fixes for an extended period. Current LTS runtimes are the target rather than legacy editions. ### Open Knowledge Format @@ -63,15 +63,15 @@ OKF — a vendor-neutral format for representing knowledge as plain Markdown fil ### Philosophy -The most stable tier of belief — *why* we exist and what we value: easy, fast, safe. It informs the [Principles](../Ways-of-Working/Principles/index.md). +The most stable tier of belief — the *why* behind the work and what it values: easy, fast, safe. It informs the [Principles](../Ways-of-Working/Principles/index.md). ### Practice -How we habitually act on a principle — concrete and evolving, such as pinning actions to a commit SHA. See [Principles](../Ways-of-Working/Principles/index.md). +The habitual way of acting on a principle — concrete and evolving, such as pinning actions to a commit SHA. See [Principles](../Ways-of-Working/Principles/index.md). ### Principle -Something that is always true for us — rarely changing, sitting between philosophy and practice. See [Principles](../Ways-of-Working/Principles/index.md). +Something that is always true across the ecosystem — rarely changing, sitting between philosophy and practice. See [Principles](../Ways-of-Working/Principles/index.md). ### Pull request diff --git a/src/docs/Vision/index.md b/src/docs/Vision/index.md index 10ca19f..fc2ed11 100644 --- a/src/docs/Vision/index.md +++ b/src/docs/Vision/index.md @@ -13,9 +13,9 @@ This page is the **why**. It is the most stable thing on this site: products, la Work at every level is grounded in three concentric questions — the [Golden Circle](../Ways-of-Working/Principles/Purpose-and-Direction.md#start-with-why-the-golden-circle): -- **Why** — what change in the world are we trying to make? *Make the right thing the easy thing, so good software ships fast and safely.* +- **Why** — what change in the world is this trying to make? *Make the right thing the easy thing, so good software ships fast and safely.* - **How** — what approach makes that change happen? *Everything as code, context before code, deterministic automation first, AI where judgment is needed, humans in the loop.* -- **What** — what concrete thing are we delivering right now? *The frameworks, actions, modules, and tools in the [Initiatives](../Initiatives/index.md).* +- **What** — what concrete thing is being delivered right now? *The frameworks, actions, modules, and tools in the [Initiatives](../Initiatives/index.md).* The Why is constant. The How is the way of working. The What is replaceable. @@ -32,8 +32,8 @@ Every decision is filtered through **easy**, **fast**, and **safe** — in tensi | Word | What it asks of every decision | | -------- | ------------------------------------------------------------------------------------------- | | **Easy** | Is the right thing also the easy thing? Is the safe, smart choice the default choice? | -| **Fast** | Does this shorten the loop between intention and feedback? Can we ship a thinner slice now? | -| **Safe** | Is this reversible? Is it observable? Will a failure teach us something instead of hurting? | +| **Fast** | Does this shorten the loop between intention and feedback? Is there a thinner slice to ship now? | +| **Safe** | Is this reversible? Is it observable? Will a failure teach something instead of causing harm? | The words pull against each other, and that is the point. Easy without safe is reckless. Safe without fast is paralysis. Fast without easy burns people out. Holding all three at once is the discipline. @@ -41,8 +41,8 @@ The words pull against each other, and that is the point. Easy without safe is r Two beliefs sit underneath everything: -- **AI is a first-class participant.** Agents are part of how we think, build, and deliver — not a feature bolted on at the end. Every workflow and every document is designed so an agent can read it and act. -- **Determinism comes first.** A script that always produces the correct answer beats a prompt that usually does. We use AI to *build* deterministic tools, then run the tools. AI earns its place by handling what deterministic logic cannot — ambiguity, judgment, natural language, and search spaces too large for hand-written rules. +- **AI is a first-class participant.** Agents are part of how work is thought through, built, and delivered — not a feature bolted on at the end. Every workflow and every document is designed so an agent can read it and act. +- **Determinism comes first.** A script that always produces the correct answer beats a prompt that usually does. AI *builds* the deterministic tools, and then the tools run. AI earns its place by handling what deterministic logic cannot — ambiguity, judgment, natural language, and search spaces too large for hand-written rules. The result is automation for the predictable and repeatable, and intelligence for the genuinely variable. Both, always available, each used where it is strongest. The full reasoning lives in [Principles → AI-first development](../Ways-of-Working/Principles/AI-First-Development.md). @@ -51,16 +51,21 @@ The result is automation for the predictable and repeatable, and intelligence fo The vision is inherited, not copied. It is written once, here, and referenced everywhere: ```text -Vision (this site) the why — stable, evergreen -└── Ways of Working the how — workflow, principles, conventions - └── Coding Standards the how, applied to code - └── Initiatives the what — the products - └── Repositories each README is the local source of truth - └── Agents read the same docs as context before acting +Vision (this site) the why — stable, evergreen +└── Principles the beliefs every layer conforms to + └── Ways of Working the how — workflow, process, conventions + ├── Coding Standards the how, applied to code + └── Capabilities the how, applied to a named capability + │ each with its own spec (why/what) and design (how/what) + └── Initiatives the what — the products + └── Repositories each README is the local source of truth + └── Agents read the same documentation as context before acting ``` Each layer references the one above instead of restating it. A repository's README does not re-explain the principles — it links to them. An agent pointer does not embed a style guide or workflow stage — it leads into the documentation indexes. This keeps a single source of truth and lets the whole system evolve without drifting out of sync. +References point in one direction only: upward. A layer names the layer it conforms to, and never enumerates the layers that conform to it. That asymmetry is what keeps the upper layers stable — adding a capability or a repository changes nothing above it. + ## Where it comes to life A vision that stays on a page is just words. This one is meant to be *demonstrated*. Each initiative in the [Initiatives](../Initiatives/index.md) is a concrete answer to one question: diff --git a/src/docs/Ways-of-Working/Agentic-Development.md b/src/docs/Ways-of-Working/Agentic-Development.md index 4afd7dc..216a030 100644 --- a/src/docs/Ways-of-Working/Agentic-Development.md +++ b/src/docs/Ways-of-Working/Agentic-Development.md @@ -156,6 +156,7 @@ The workspace makes the *central* context present locally; the same local-first ## Where this connects - [Git Worktrees](Git-Worktrees.md) — how this framework is implemented on a local machine, so several pieces of work run in parallel. +- [Session Interactions](Session-Interactions.md) — recognised phrases that operate on the session, each defined once and referenced by every runtime. - [Documentation Model](Documentation-Model.md) — the discipline this specification follows. - [Principles](Principles/index.md) — the beliefs this specification rests on, including the three-layer agent context model. - [README-Driven Context](Readme-Driven-Context.md) — why the repository's own context comes first. diff --git a/src/docs/Ways-of-Working/Automation-Labels.md b/src/docs/Ways-of-Working/Automation-Labels.md new file mode 100644 index 0000000..fdd333d --- /dev/null +++ b/src/docs/Ways-of-Working/Automation-Labels.md @@ -0,0 +1,100 @@ +--- +title: Automation Labels +description: Why every label that drives automation belongs to exactly one owning function, how namespacing keeps label dimensions disjoint, and why automation ignores labels it does not own. +--- + +# Automation Labels + +A label is the cheapest control surface a repository has: anyone with write access +can apply one, it is visible on the issue or pull request that carries it, and it +survives in the audit trail. That makes labels the natural way for a human to tell +automation what to do — and it makes an unstructured label set a liability, because +the same word can mean different things to different automations reading the same +pull request. + +Labels that drive automation are therefore **owned**, and ownership is made visible +in the label's own name. + +## One owner per label set + +Every label an automation reads MUST belong to exactly one owning function, and +that function MUST provision the labels it reads. + +Ownership is what makes collisions structurally impossible rather than merely +unlikely. Two functions cannot disagree about what a word means if only one of them +is allowed to read it. The alternative — a shared flat vocabulary any automation may +interpret — requires every function to know about every other function's labels in +order to avoid them, and that knowledge is nowhere written down. + +Because the owning function provisions its own labels, the valid set is derivable +from that function's configuration rather than from whatever a repository's label +list happens to contain. + +## Namespacing + +A label set that could be confused with another MUST be namespaced as +`namespace:value`, where the namespace names the owning function and the value is +the instruction to it. + +```text +update:major dependencies +update:minor github-actions +update:patch containers +``` + +Namespacing is not decoration. It is what lets two functions describe the *same kind +of thing* about one pull request without either one silently reading the other's +signal. A dependency update carries both an upstream version change and a version +decision for the repository consuming it; those are two version decisions on one +pull request, and only a namespace keeps them apart +([dependency updates](../Capabilities/dependency-updates/design.md#separation-from-release-versioning)). + +## Reserved vocabularies + +Where a function's label set is unprefixed, that set MUST be **reserved**: no other +function may read, provision, or reuse those words, and the reservation MUST be +documented on the owning capability's page. + +The release bump vocabulary is the standing example. `Major`, `Minor`, `Patch`, and +`NoRelease` are read by [release management](../Capabilities/release-management/spec.md) +and by nothing else. Any other function that needs to express a version level MUST +namespace its own set instead of borrowing these, because a borrowed bump label does +not merely confuse a reader — it changes the version the repository publishes. + +Reservation is the weaker of the two mechanisms, because it depends on a documented +prohibition rather than on the label's own name. New label sets are namespaced. + +## Automation ignores what it does not own + +Automation MUST NOT respond to a label outside the set it owns. + +An ad-hoc label therefore does nothing. That is the safe failure: a label nobody +provisioned expresses an intent nobody defined, and acting on a guess about it is +worse than ignoring it. A contributor who applies an invented label and expects a +consequence gets none, and learns that from the absence of the consequence rather +than from an unexpected one. + +The corollary is that an owned label MUST be honoured everywhere its function runs. +A label that acts in one repository and is decorative in another is worse than no +label, because it teaches a contributor a rule that does not hold. + +## Why not paths, states, or free text + +Labels are chosen over the alternatives because of what each one costs: + +| Alternative | Cost | +| --- | --- | +| A file in the repository | Requires a commit to change, so it cannot express a decision about a pull request that is already open | +| A pull request comment | Free text; automation parsing prose is guessing | +| A project field | Not visible on the pull request, and not present in the repository's own audit trail | +| An unowned label | Ambiguous across functions, and indistinguishable from an ad-hoc one | + +An owned label is applied without a commit, read without parsing, visible where the +decision applies, and unambiguous about who acts on it. + +## Where this connects + +- [Release Management](../Capabilities/release-management/spec.md) — the reserved bump vocabulary and why exactly one of its values is required. +- [Dependency Updates](../Capabilities/dependency-updates/design.md#labels) — the namespaced dependency label sets and their separation from release versioning. +- [Repository Governance](../Capabilities/repository-governance/design.md) — the controls that read repository state, of which labels are one. +- [Repository Standard](Repository-Standard.md) — the repository-level requirement that labels be provisioned rather than improvised. diff --git a/src/docs/Ways-of-Working/Branching-and-Merging.md b/src/docs/Ways-of-Working/Branching-and-Merging.md index 28eeae2..2e33256 100644 --- a/src/docs/Ways-of-Working/Branching-and-Merging.md +++ b/src/docs/Ways-of-Working/Branching-and-Merging.md @@ -52,6 +52,17 @@ Two models, chosen by the repository's deployment shape: The choice follows from [repository segmentation](Repository-Segmentation.md): an app and an infrastructure stack don't share a model. +### The standing promotion pull request + +A repository on the promotion model MAY keep a **standing draft pull request** from the integration branch to the production branch, held open permanently rather than opened per promotion. The pull request is the release candidate: it always shows the exact set of changes that are integrated but not yet promoted, and its diff is the promotion under review. + +- **One long-lived pull request, not one per promotion.** Merging it promotes; a new draft opens immediately, so the candidate view is never absent. +- **Draft is the resting state.** It is marked ready when the integrated state is deemed promotable, which is what turns review and any promotion gate on. +- **Its description is the promotion note.** Assembled from the changes it carries, it is the record of what a promotion contained, written for whoever operates the destination. +- **The bump label on it decides the production version.** Promotion is a release like any other, so the version comes from the label on this pull request, not from the versions of the changes it bundles ([release management](../Capabilities/release-management/spec.md)). + +The value is continuous visibility: at any moment, the difference between what is integrated and what is live is one link. It suits repositories where promotion is a deliberate, gated event and costs more than it returns where every merge already ships. + ## Required checks and auto-merge A branch ruleset on the protected branch defines the merge requirements every pull request must satisfy before it can land — the **required status checks**, the **required approvals**, and any others the ruleset enforces (for example conversation resolution or signed commits). These are the required steps: a pull request that has not satisfied all of them cannot land, however it was marked. The ruleset is configured on the repository (in its settings), not in these files; this section defines what it must enforce. diff --git a/src/docs/Ways-of-Working/Definition-of-Ready-and-Done.md b/src/docs/Ways-of-Working/Definition-of-Ready-and-Done.md index 7e61708..317dde1 100644 --- a/src/docs/Ways-of-Working/Definition-of-Ready-and-Done.md +++ b/src/docs/Ways-of-Working/Definition-of-Ready-and-Done.md @@ -23,7 +23,7 @@ Ready gates starting, not scope. An issue that is not ready stays in refinement ## Definition of Ready for Review -A pull request stays a **draft** until it is genuinely ready for other people to spend attention on it. "Ready for review" is not "I started" or "please take a look" — it is a deliberate signal that the change is complete and self-reviewed, and that the only thing left is another perspective before merge. It gates the hand-off in the [Contribution Workflow](Contribution-Workflow.md). +A pull request stays a **draft** until it is genuinely ready for other people to spend attention on it. "Ready for review" is not "work has started" or "please take a look" — it is a deliberate signal that the change is complete and self-reviewed, and that the only thing left is another perspective before merge. It gates the hand-off in the [Contribution Workflow](Contribution-Workflow.md). A pull request is ready for review when: diff --git a/src/docs/Ways-of-Working/DevOps-Reference.md b/src/docs/Ways-of-Working/DevOps-Reference.md index c2ab6f2..9467481 100644 --- a/src/docs/Ways-of-Working/DevOps-Reference.md +++ b/src/docs/Ways-of-Working/DevOps-Reference.md @@ -1,6 +1,6 @@ --- title: DevOps Reference -description: A curated reading list and the principles behind how we work. +description: A curated reading list and the principles behind the way of working. --- # DevOps Reference diff --git a/src/docs/Ways-of-Working/Documentation-Model.md b/src/docs/Ways-of-Working/Documentation-Model.md index 0b43d3c..a39b96d 100644 --- a/src/docs/Ways-of-Working/Documentation-Model.md +++ b/src/docs/Ways-of-Working/Documentation-Model.md @@ -18,11 +18,11 @@ Each capability the ecosystem builds is documented by two evergreen documents: | Document | Owns | Answers | Written for | | --- | --- | --- | --- | | **Spec** | Requirements, expectations, needs | **Why** it exists and **what** it must do | Whoever decides *whether* and *what* to build | -| **Design** | Implementation approach | **How** and **what** we build to deliver the spec | Whoever *builds* and maintains it | +| **Design** | Implementation approach | **How** and **what** is built to deliver the spec | Whoever *builds* and maintains it | The **spec** is the contract — the behaviour, guarantees, and success criteria a user (human or agent) can rely on. It never prescribes implementation. The -**design** is the current answer to *how we deliver that contract*: the +**design** is the current answer to *how that contract is delivered*: the mechanism, the moving parts, the configuration. Both are durable, and both evolve — neither is a one-time plan. @@ -45,9 +45,9 @@ is a folder holding its spec and design side by side: ```text Capabilities/ release-management/ - index.md # what this capability is + index.md # required navigation for this capability spec.md # the why + what - design.md # the how + what we build + design.md # the how + what is built ``` A reader opens one folder and has the whole picture — the requirement and the @@ -56,19 +56,61 @@ it documents](Principles/Engineering-Practices.md#documentation-lives-close-to-t applied to the spec–design pair; where a design maps to a repository, the same two documents live with the code. +Every capability folder requires an `index.md` for navigation. The spec and the +design are the two required **content artifacts**. A capability that outgrows +them grows downward into the optional +[artifact tiers](Spec-Driven-Development.md#the-artifact-tiers), each of which has +a fixed home in the same folder: + +```text +Capabilities/ + / + index.md # required navigation, not a content artifact + spec.md # the why + what (required content) + features/ # per-feature spec addenda + index.md + .md + design.md # the how + what is built (required content) + implementation.md # the concrete values and names + guides/ # task-oriented walkthroughs + index.md + .md + references.md # lookup tables + decisions/ # one-way-door choices, immutable + index.md + .md + research/ # point-in-time exploration + index.md + .md +``` + +A file that grows past a single page becomes a folder with an `index.md` and one +page per member — `design.md` may become `design/`, and `references.md` may +become `references/`, without changing what the tier means. The reverse also +holds: a tier that never fills up is never created. Empty scaffolding is a cost +with no reader, so a folder appears the first time it has something to hold +([concise by default](#concise-by-default)). + ## Why, what, how — a home for everything | Concern | Owned by | | --- | --- | | **Why / what** a capability must do | the capability's **spec** | -| **How / what** we build to deliver it | the capability's **design** | -| **How we work** — process, principles, conventions | [Ways of Working](index.md) | +| **How / what** is built to deliver it | the capability's **design** | +| **Which exact value or name** was chosen | the capability's **implementation** docs | +| **How to perform a task** with it | the capability's **guides** | +| **What the settings are** | the capability's **references** | +| **Which one-way-door choice** was made, and why | the capability's **decisions** | +| **What was explored** before deciding | the capability's **research** | +| **How the work is done** — process, principles, conventions | [Ways of Working](index.md) | | **How code looks** — style applied to code | [Coding Standards](../Coding-Standards/index.md) | | **How this one change is implemented** — paths, trade-offs | the Task or Bug delivery leaf and its PR; see [Issue Planning](Issues/Process/Planning.md) | Keeping implementation out of the spec is what makes the spec durable: -implementation detail rots fastest, so the spec leaves it to the design, and the -design leaves per-change detail to the issue and the PR. +implementation detail rots fastest, so the spec leaves it to the design, the +design leaves exact values to the implementation docs, and all of them leave +per-change detail to the issue and the PR. Each tier absorbs the churn of the one +below it, so the tier above stays still. ## It starts with a need @@ -78,9 +120,9 @@ then code — and loops: 1. **Need** — a request, a bug, a review observation, a platform change. 2. **Spec** — agree the next version's requirements: why it matters and what it must do. Nothing is committed to building yet. -3. **Design** — once committed to deliver, describe how and what we will build. +3. **Design** — once committed to deliver, describe how and what will be built. 4. **Build** — ready Task and Bug leaves implement the gap, evolving the design - *and* the spec as development teaches you things. + *and* the spec as development reveals more. 5. **Operate** — running the system surfaces new needs, and the loop returns. ```mermaid diff --git a/src/docs/Ways-of-Working/Engineering-Taste.md b/src/docs/Ways-of-Working/Engineering-Taste.md index d38c0e7..405d755 100644 --- a/src/docs/Ways-of-Working/Engineering-Taste.md +++ b/src/docs/Ways-of-Working/Engineering-Taste.md @@ -34,4 +34,4 @@ Standards cover the common cases. When they run out, judgment takes over. These ## When still unsure -Fall back to the three words: does this make the system **easier**, let us move **faster**, and keep us **safe**? If a choice trades one away, say so out loud and decide deliberately. +Fall back to the three words: does this make the system **easier**, does it move **faster**, and does it stay **safe**? If a choice trades one away, say so out loud and decide deliberately. diff --git a/src/docs/Ways-of-Working/Evolutionary-Development.md b/src/docs/Ways-of-Working/Evolutionary-Development.md index 501b6f4..a749d65 100644 --- a/src/docs/Ways-of-Working/Evolutionary-Development.md +++ b/src/docs/Ways-of-Working/Evolutionary-Development.md @@ -5,22 +5,22 @@ description: Grow software as bets under selection — variation, feedback, and # Evolutionary Development -Software is grown, not specified into existence. No one designs the right system up front; the hardest part of any change is deciding precisely what to build. So we treat every change as a **bet**: frame it, build the smallest thing that can be judged, expose it to real selection pressure, and keep what survives. +Software is grown, not specified into existence. No one designs the right system up front; the hardest part of any change is deciding precisely what to build. So every change is treated as a **bet**: frame it, build the smallest thing that can be judged, expose it to real selection pressure, and keep what survives. -This is Darwin's theory of evolution applied to engineering: variation, selection, and survival of the fittest. A design is not right because it was planned well; it survives because it fits the environment it has to live in — the tests, the users, the load, and the constraints. The outcome is non-deterministic: we direct the variation — the deliberate mutations — but real conditions, not our intentions, decide which design survives. It is neither blind natural selection nor a breeder's artificial selection: we choose what to try, the environment chooses what lives. +This is Darwin's theory of evolution applied to engineering: variation, selection, and survival of the fittest. A design is not right because it was planned well; it survives because it fits the environment it has to live in — the tests, the users, the load, and the constraints. The outcome is non-deterministic: the variation is directed — the mutations are deliberate — but real conditions, not intentions, decide which design survives. It is neither blind natural selection nor a breeder's artificial selection: the engineer chooses what to try, the environment chooses what lives. -Nature ran this algorithm first. We copy what it does well and set it on hyper-speed: cheap variation, ruthless feedback, and generations measured in minutes, not millennia. +Nature ran this algorithm first. This standard copies what it does well and sets it on hyper-speed: cheap variation, ruthless feedback, and generations measured in minutes, not millennia. -This standard is the *loop*, and it marries the practices we already keep. [Spec-Driven Development](Spec-Driven-Development.md) gives a bet its *shape* — the spec and its design, the *what* and *why*. Behavior-driven discovery turns that intent into Given / When / Then acceptance criteria — the definition of *fit*. Test-driven development grows the implementation against those criteria and the unit tests beneath them — the *how*, evolved in small steps. The three run as one loop. What drives it is not a particular hand but context in, criteria as the target, and feedback as the verdict — so the same loop turns whether a person runs it, an agent runs it, or agents run it end-to-end (see [How the loop is driven](#how-the-loop-is-driven)). It builds on [engineering practices](Principles/Engineering-Practices.md) (test-first where it pays, small batches, reversible decisions) and [AI-first development](Principles/AI-First-Development.md). +This standard is the *loop*, and it marries the practices already kept here. [Spec-Driven Development](Spec-Driven-Development.md) gives a bet its *shape* — the spec and its design, the *what* and *why*. Behavior-driven discovery turns that intent into Given / When / Then acceptance criteria — the definition of *fit*. Test-driven development grows the implementation against those criteria and the unit tests beneath them — the *how*, evolved in small steps. The three run as one loop. What drives it is not a particular hand but context in, criteria as the target, and feedback as the verdict — so the same loop turns whether a person runs it, an agent runs it, or agents run it end-to-end (see [How the loop is driven](#how-the-loop-is-driven)). It builds on [engineering practices](Principles/Engineering-Practices.md) (test-first where it pays, small batches, reversible decisions) and [AI-first development](Principles/AI-First-Development.md). ## Why evolve instead of plan -We state intent first and treat the plan as a living thing. Two habits carry it: +Intent is stated first, and the plan is treated as a living thing. Two habits carry it: - **Build shared understanding from concrete examples**, in rapid iterations, with documentation that is continuously checked against the system. - **Front-load the *why* and *what*** so both people and agents have the context they need, then refine continuously as understanding grows. -Neither habit assumes the first design is correct. Both optimise the *loop*, not the plan: a big up-front design is a large, hard-to-reverse bet made when we know the least; a small slice under real feedback is a cheap bet made while we can still change our minds. Prefer the cheap bet. +Neither habit assumes the first design is correct. Both optimise the *loop*, not the plan: a big up-front design is a large, hard-to-reverse bet made at the point of least knowledge; a small slice under real feedback is a cheap bet made while the decision is still open. Prefer the cheap bet. ## The loop @@ -39,7 +39,7 @@ flowchart LR retain -.-> decide ``` -- **Decide.** State the bet as a hypothesis with an explicit kill condition — the result that would make us abandon it. Size the bet to its reversibility: local, reversible moves are cheap, so make them freely; one-way doors are expensive, so slow down and write them down ([decision before change](Principles/AI-First-Development.md#decision-before-change), [engineering taste](Engineering-Taste.md)). +- **Decide.** State the bet as a hypothesis with an explicit kill condition — the result that would make the bet worth abandoning. Size the bet to its reversibility: local, reversible moves are cheap, so make them freely; one-way doors are expensive, so slow down and write them down ([decision before change](Principles/AI-First-Development.md#decision-before-change), [engineering taste](Engineering-Taste.md)). - **Build.** Make the smallest thing that can be judged — a walking skeleton or a throwaway spike — as a thin vertical slice, not a layer ([engineering practices](Principles/Engineering-Practices.md)). - **Explore.** Put it in front of reality: run it, demo it, probe its edges. Exploration is where surprises surface while they are still cheap. - **Test.** The [acceptance criteria](Spec-Driven-Development.md#acceptance-criteria) — Given / When / Then — are the coarse fitness function; the unit tests beneath them check the internals. They pass, or the bet has not survived. These automated tests become the guard-rails that stop later turns breaking earlier ones. @@ -78,14 +78,14 @@ flowchart TB build -.-> inner ``` -- **The outer loop evolves the design.** It is deliberate and coarse-grained: state the intent, agree the acceptance criteria, decide quickly, and let a slice meet reality. When feedback contradicts the intent, the spec changes first and the approach evolves — [spec-driven development](Spec-Driven-Development.md) turning. This is where early alignment, the acceptance criteria, and the one-way doors are settled. We dare to decide fast and fail fast here because the spec makes the cost of being wrong cheap to see and cheap to revise. +- **The outer loop evolves the design.** It is deliberate and coarse-grained: state the intent, agree the acceptance criteria, decide quickly, and let a slice meet reality. When feedback contradicts the intent, the spec changes first and the approach evolves — [spec-driven development](Spec-Driven-Development.md) turning. This is where early alignment, the acceptance criteria, and the one-way doors are settled. Deciding fast and failing fast belong here, because the spec makes the cost of being wrong cheap to see and cheap to revise. - **The inner loop evolves the implementation.** It nests inside the **Build** stage and runs fast and fine-grained: write a failing test, make it pass, refactor, repeat — [test-driven development](Principles/Engineering-Practices.md#test-driven-development). The tests come at two grains — fine-grained **unit tests** for the internals and the **Given / When / Then acceptance tests** that encode the behavior from discovery. Many inner turns fit inside one outer turn, fast enough that variations are generated and discarded far quicker than by hand. The **acceptance criteria** couple the two loops. Written once as Given / When / Then, they are the outer loop's definition of *fit* and the inner loop's target — one behavioral statement serving discovery, the spec, and the test. Whoever turns each loop, the criteria keep both honest to the same intent. ## Signals: the shapes of intent -A turn does not only begin when someone asks for a feature. It begins with a **signal** — a pressure from the environment that the current design no longer fits — and every signal is **intent in a particular shape**. Naming the shapes keeps us honest that a bugfix, a performance guard, and a new capability are the same kind of work: a bet, framed from a signal, judged by selection. +A turn does not only begin when someone asks for a feature. It begins with a **signal** — a pressure from the environment that the current design no longer fits — and every signal is **intent in a particular shape**. Naming the shapes keeps it honest that a bugfix, a performance guard, and a new capability are the same kind of work: a bet, framed from a signal, judged by selection. - **Need.** A request for functionality or capability the product does not have yet — a user, a stakeholder, or a dependent system pulling it forward. Intent stated outright: *make it do this.* - **Defect.** Proof that behavior and intent already disagree — a fault found in test or reported from production. An acceptance criterion that should hold does not, so the bet is to make it hold again. @@ -119,7 +119,7 @@ This is [context-first development](Principles/AI-First-Development.md#context-f > [!IMPORTANT] > Self-directed update is a standing directive, not an optional courtesy. Whoever turns the loop — a person or an agent — and learns something or hits a wrong instruction is expected to improve that instruction then and there, rather than leave it for someone else, so the next turn starts sharper than this one did. A loop that cannot improve its own instructions cannot evolve. -The standards, guides, and agent instructions that inform how we work are part of the environment the loop runs in. They are context, and context drives every turn (see [How the loop is driven](#how-the-loop-is-driven)) — so a weak instruction misdirects every turn that reads it, quietly and at scale, and a sharper one improves every turn just as widely. The instructions are under selection too: correct them when they are wrong, and refine them when a turn teaches something better. Either way, updating them is part of the work, not a separate chore. This is [self-improving agents](Principles/AI-First-Development.md#self-improving-agents) applied to the instructions themselves. +The standards, guides, and agent instructions that inform how the work is done are part of the environment the loop runs in. They are context, and context drives every turn (see [How the loop is driven](#how-the-loop-is-driven)) — so a weak instruction misdirects every turn that reads it, quietly and at scale, and a sharper one improves every turn just as widely. The instructions are under selection too: correct them when they are wrong, and refine them when a turn teaches something better. Either way, updating them is part of the work, not a separate chore. This is [self-improving agents](Principles/AI-First-Development.md#self-improving-agents) applied to the instructions themselves. When a turn exposes a defect or discovers a better way of working — whether a person hits the friction or an agent does — raise the change as its own small bet and run it through the same loop: a change to the doc, reviewed and merged through a [decision point](Principles/AI-First-Development.md#decision-before-change). Signals worth acting on: @@ -129,7 +129,7 @@ When a turn exposes a defect or discovers a better way of working — whether a - A turn goes wrong in a way a clearer instruction would have prevented. - A turn finds a better way — a sharper prompt, a cleaner sequence, a rule worth making the default — worth keeping for the next one. -Because a standard is defined once and pointed to everywhere ([Agentic Development](Agentic-Development.md)), one change propagates to every repo and every agent that reads it — improve it in one place and the whole fleet's behavior evolves. This is the method turned on itself: the same living-documentation discipline that keeps a spec true keeps the instructions true and refines them as we learn, so the way we work gets better every time a turn teaches us something worth keeping. +Because a standard is defined once and pointed to everywhere ([Agentic Development](Agentic-Development.md)), one change propagates to every repo and every agent that reads it — improve it in one place and the whole fleet's behavior evolves. This is the method turned on itself: the same living-documentation discipline that keeps a spec true keeps the instructions true and refines them as understanding grows, so the way of working gets better every time a turn teaches something worth keeping. ## Working a v1 diff --git a/src/docs/Ways-of-Working/Git-Worktrees.md b/src/docs/Ways-of-Working/Git-Worktrees.md index 0387a9c..7df3107 100644 --- a/src/docs/Ways-of-Working/Git-Worktrees.md +++ b/src/docs/Ways-of-Working/Git-Worktrees.md @@ -50,7 +50,7 @@ Every repository has exactly two remotes (or one, if it is not a fork): | Remote | Points to | Required | Purpose | | -------------- | ---------------------------- | -------- | ------------------------------------------------ | -| **`origin`** | Our copy on the server | Always | Push branches, open PRs, CI runs against this. | +| **`origin`** | The copy on the server | Always | Push branches, open PRs, CI runs against this. | | **`upstream`** | The parent repo (forks only) | Forks | Track upstream changes, sync the default branch. | No other remotes are added. This keeps the model simple and predictable for both humans and agents. @@ -58,7 +58,7 @@ No other remotes are added. This keeps the model simple and predictable for both ### How it works in practice - **Non-fork repos** — only `origin` exists. Branches are pushed to `origin`, PRs are opened against `origin`. -- **Forked repos** — `origin` is our fork, `upstream` is the original repository. The default branch tracks `upstream` for syncing; feature branches are pushed to `origin` and PRs are opened from `origin` into `upstream`. +- **Forked repos** — `origin` is the fork, `upstream` is the original repository. The default branch tracks `upstream` for syncing; feature branches are pushed to `origin` and PRs are opened from `origin` into `upstream`. ### Fetch configuration diff --git a/src/docs/Ways-of-Working/Goal-Setting.md b/src/docs/Ways-of-Working/Goal-Setting.md index d5cdbc0..a9937b7 100644 --- a/src/docs/Ways-of-Working/Goal-Setting.md +++ b/src/docs/Ways-of-Working/Goal-Setting.md @@ -17,7 +17,7 @@ MSX uses a lightweight OKR-based framework to connect strategic direction to day ## Why OKRs and not KPIs -- **Objectives** are qualitative, aspirational, and outside-in. They describe a state of the world we want to see. +- **Objectives** are qualitative, aspirational, and outside-in. They describe a desired state of the world. - **Key Results** are measurements that confirm the Objective is being met. They drive incentive in the right direction without prescribing the path. A good OKR is one that anyone — contributor, user, or agent — can read and immediately have ideas about how to contribute. See [Principles](Principles/index.md) for the full rationale. diff --git a/src/docs/Ways-of-Working/Organization-Standard.md b/src/docs/Ways-of-Working/Organization-Standard.md index 7bbf152..55af742 100644 --- a/src/docs/Ways-of-Working/Organization-Standard.md +++ b/src/docs/Ways-of-Working/Organization-Standard.md @@ -75,6 +75,16 @@ Organizations must distinguish mandatory files from optional or type-specific fi Security, contribution, conduct, support, dependency update, and license files are candidates for mandatory file sets. Linter settings, agent instructions, and workflow defaults may be global or type-specific depending on the initiative. +Which set applies to a repository is derived from its classification rather than decided per repository, so that adding a repository requires classifying it and nothing else. The classification mechanism, the per-type required-file matrix, and the composition rules for a repository that is more than one type are defined by [Repository Governance](../Capabilities/repository-governance/spec.md). + +## Enforcement is continuous + +A standard is only in force where the organization can tell whether it is being met. Writing the standard down, distributing the files once, and assuming the result persists produces an organization that believes it is aligned and cannot demonstrate it — repositories are created outside the distribution path, settings are changed by hand, and files are edited locally, none of which announces itself. + +So every organization-level standard must be paired with a way of observing its state across repositories, and every observed deviation must be either corrected or recorded as a deliberate exemption. Silent deviation is the only outcome that is not allowed, because it is indistinguishable from compliance until something depends on it. + +This is the reconciliation loop described in [Repository Governance](../Capabilities/repository-governance/design.md#drift-detection-and-reconciliation): the declared state is the source of truth, the observed state is measured against it, and the difference is a finding that names its own remedy. + ## Linter configuration ownership The written standard defines the rule. The linter configuration enforces the rule. @@ -97,6 +107,9 @@ The repository-level entry point is `AGENTS.md`, as defined by [Agentic Developm - [Repository Standard](Repository-Standard.md) — the repository-level contract every repository must satisfy. - [Repository Type Property](Repository-Type-Property.md) — the concrete `Type` custom-property mechanism that implements "repository types" and "required custom properties, branch protection" from this page. +- [Repository Governance](../Capabilities/repository-governance/index.md) — the classification, ruleset, required-file, exemption, and reconciliation machinery that puts this standard in force. +- [Repository Segmentation](Repository-Segmentation.md) — where a repository's boundary falls, which is what gets classified. +- [Automation Labels](Automation-Labels.md) — the label vocabulary organization automation reads and writes. - [Documentation Model](Documentation-Model.md) — why specs own why and what, while designs own implementation. - [Dependency Updates](../Capabilities/dependency-updates/spec.md) — the supply-chain update capability every repository inherits. - [GitHub Actions](../Coding-Standards/GitHub-Actions.md) — workflow authoring and enforcement rules. diff --git a/src/docs/Ways-of-Working/Principles/AI-First-Development.md b/src/docs/Ways-of-Working/Principles/AI-First-Development.md index 69075f0..7f07142 100644 --- a/src/docs/Ways-of-Working/Principles/AI-First-Development.md +++ b/src/docs/Ways-of-Working/Principles/AI-First-Development.md @@ -5,9 +5,9 @@ description: Agents as first-class participants, determinism before intelligence # AI-first development -We practice AI-first development. AI agents are part of how we think, build, and deliver — not an afterthought bolted on at the end. Every workflow, every process, and every piece of documentation is designed with agents as first-class participants. +Development here is AI-first. AI agents are part of how work is thought about, built, and delivered — not an afterthought bolted on at the end. Every workflow, every process, and every piece of documentation is designed with agents as first-class participants. -That said, engineering is not fully non-deterministic. Our priority order is clear: **build deterministic software first, invoke AI where determinism falls short.** A script that always produces the correct answer is better than a prompt that usually does. AI fills the gaps that deterministic logic cannot cover — ambiguity, judgment, creativity, natural language, and tasks where the search space is too large for hand-written rules. +That said, engineering is not fully non-deterministic. The priority order is clear: **build deterministic software first, invoke AI where determinism falls short.** A script that always produces the correct answer is better than a prompt that usually does. AI fills the gaps that deterministic logic cannot cover — ambiguity, judgment, creativity, natural language, and tasks where the search space is too large for hand-written rules. The result: AI is always available, always integrated, always ready — but it earns its place by handling what deterministic tools cannot. @@ -27,17 +27,17 @@ Practices: The workflow is designed for humans and agents working side by side. Agents join a good human way of working — they do not replace it or require a parallel system. The operating model is **human-first, agent-augmented**. -Agents are trained to read documentation. That is their natural skill. By keeping standards, conventions, and principles in documentation format we serve both audiences with a single artifact — no separate "agent manual" required. +Agents are trained to read documentation. That is their natural skill. Keeping standards, conventions, and principles in documentation format serves both audiences with a single artifact — no separate "agent manual" required. Agent context is delivered through three layers, in priority order: -1. **Documentation** — the primary source. Published docs at , READMEs, and issue bodies are written for humans and naturally consumable by agents. -2. **Canonical workflow** — [Workflow](../Workflow.md) owns the process and links to ordinary documentation for each [stage procedure](../Workflow-Stages/index.md). Indexes provide the default discovery path; clear task language may shortcut stage selection without creating separate instructions. -3. **Local pointer files** — each repository's `AGENTS.md` router (with the content-free client routes that reach it), which reads outward from the repository's own files to the initiative and central documentation, and to memory last. +1. **Documentation** — the primary source. Published documentation, READMEs, and issue bodies are written for humans and naturally consumable by agents. +2. **Canonical workflow** — one documented process owns the order of work and links to ordinary documentation for each stage procedure. Indexes provide the default discovery path; clear task language may shortcut stage selection without creating separate instructions. +3. **Local pointer files** — each repository's agent router, and the content-free client routes that reach it, which read outward from the repository's own files to the organization's documentation, and to memory last. ## Augmentation, not replacement -Agents amplify the team. They make us faster, more consistent, and free us from work that is mechanical. **Human in the loop** remains the default for decisions that matter. +Agents amplify the team. They make delivery faster and more consistent, and remove work that is mechanical. **Human in the loop** remains the default for decisions that matter. ## One workflow, not a swarm @@ -45,7 +45,7 @@ Treat the agent ecosystem as one teammate following one shared workflow. Stages ## Self-improving agents -Agents need feedback and a way to process it. Every workflow stage should evolve as we learn. Capture lessons in the stage descriptions and in this docs section — don't let them live only in someone's head. +Agents need feedback and a way to process it. Every workflow stage evolves as lessons accumulate. Capture those lessons in the stage descriptions and in this documentation — never let them live only in someone's head. ## Integration and sensoring @@ -60,7 +60,7 @@ Every change flows through context before it touches code: Intention of change → Update documentation → Update README → Update tests → Update code ``` -Code echoes the docs, not the other way around. The README and the docs are the **specification**. Tests validate the interface we want to see. If a change isn't reflected in context first, the code has no contract to implement against — and agents have nothing to read. +Code echoes the documentation, not the other way around. The README and the documentation are the **specification**. Tests validate the intended interface. If a change isn't reflected in context first, the code has no contract to implement against — and agents have nothing to read. This means: @@ -69,9 +69,7 @@ This means: - A refactor updates the relevant documentation **first**, then the tests, then the code follows to match. - If the docs and the code disagree, the docs are wrong — fix the docs, fix the tests, then fix the code to match. -This is what makes agentic development work at scale. Agents read context. If the context is stale or missing, the agent builds the wrong thing. Keeping context ahead of code is how we stay in control. - -See [README-Driven Context](../Readme-Driven-Context.md). +This is what makes agentic development work at scale. Agents read context. If the context is stale or missing, the agent builds the wrong thing. Keeping context ahead of code is what keeps the outcome under control. ## Context as a product @@ -82,8 +80,6 @@ The work of keeping context **right, evergreen, and declarative** runs alongside Both run continuously. Each iteration of software delivery produces context that needs maintenance; each iteration of context maintenance unblocks the next round of software work. -See [Workflow](../Workflow.md) for how these connect in practice. - ## 4-eyes (or N-eyes) principle Every change benefits from a second perspective. With AI in the loop, that can be: diff --git a/src/docs/Ways-of-Working/Principles/Engineering-Practices.md b/src/docs/Ways-of-Working/Principles/Engineering-Practices.md index 618d2d5..6c4b0e9 100644 --- a/src/docs/Ways-of-Working/Principles/Engineering-Practices.md +++ b/src/docs/Ways-of-Working/Principles/Engineering-Practices.md @@ -1,6 +1,6 @@ --- title: Engineering practices -description: Write it down, everything as code, evergreen docs, test-driven development, and shift-left quality. +description: Write it down, everything as code, evergreen documentation, test-driven development, fleet-wide change, and shift-left quality. --- # Engineering practices @@ -39,7 +39,7 @@ Distance between a thing and its documentation is the rate at which they drift a Documentation describes the system as it **is**, in the present tense — not the history of how it got there. A reader (human or agent) should be able to trust any page as the current truth without knowing what changed or when. -- Write the intended state as if it already exists. Cut hedging, status notes, and "we will" or "we did". +- Write the intended state as if it already exists. Cut hedging, status notes, and future or past framing of the work itself. - Describe behavior and intent, not the process of building them. A pull request that adds a capability documents the capability, not the act of adding it. - Keep task lists, TODOs, and change history in issues and pull requests — not in evergreen docs. - Define a thing by what it is, not by contrast with an abandoned alternative. Lead with the positive fact. @@ -69,7 +69,7 @@ This is a design constraint, not an optional addition. A solution that is untest ### Pre-commit hooks -Validation that runs automatically on `git commit` — before the change enters history and before a PR is opened. Pre-commit hooks close the gap between "I can test it locally if I choose to" and "it is always checked before it leaves my machine." +Validation that runs automatically on `git commit` — before the change enters history and before a PR is opened. Pre-commit hooks close the gap between "it can be tested locally by choice" and "it is always checked before it leaves the machine." Typical gates: linting, formatting, static analysis, secret scanning, and fast unit tests. Keep them fast enough to not interrupt flow — if a hook takes more than a few seconds, it will be bypassed. @@ -94,7 +94,23 @@ Push work as far inward as it can go. ## 1-2-Automate -If you've done a thing twice, the third time it should be automated. Sometimes you already know — go straight to automation. Extreme automation is often the right starting point. +If a thing has been done twice, the third time it is automated. Sometimes this is known in advance — go straight to automation. Extreme automation is often the right starting point. + +## Manage the fleet, not the repository + +A change that is correct for one repository is rarely needed by only one. Treat a group of repositories as a single managed population: the unit of change is the whole set that shares a property, not the individual member. + +This follows from everything as code. If configuration, workflows, and policy are expressed as files, then applying a decision across many repositories is a mechanical operation rather than a manual round of visits — and the same mechanism can verify the result afterwards. + +The obligations that follow: + +- A change intended for a class of repositories is applied by a mechanism that covers the whole class, so no member is silently skipped. +- Membership is derived from a declared property of the repository, never from a hand-maintained list that drifts as repositories are created and retired. +- The desired state is expressed once, centrally, and distributed. A member that needs to differ records the deviation locally rather than quietly diverging. +- Drift is expected, not exceptional. Because a population changes underneath any decision, conformance is re-checked continuously and reconciled, rather than assumed to hold because it once did. +- The scale of the population is a design input. A step that is acceptable once by hand is not acceptable when it repeats across every member. + +The intent is that the effort of a decision stays flat as the number of repositories grows. Where per-repository effort scales with the fleet, the mechanism is wrong. ## DevOps and SRE diff --git a/src/docs/Ways-of-Working/Principles/Planning-and-Delivery.md b/src/docs/Ways-of-Working/Principles/Planning-and-Delivery.md index 04724af..4a20929 100644 --- a/src/docs/Ways-of-Working/Principles/Planning-and-Delivery.md +++ b/src/docs/Ways-of-Working/Principles/Planning-and-Delivery.md @@ -7,7 +7,7 @@ description: Roadmapping, lean delivery, and the loops that keep iteration fast. ## Roadmapping -We plan in a 3×3 matrix: +Planning happens in a 3×3 matrix: | | Now | Next | Later | | ------------ | ------------ | -------------- | -------------- | @@ -24,18 +24,18 @@ The detail increases as work moves from Later toward Now. Start very thin. Get the team's ideas flowing. Enable more people to contribute. Don't build for tomorrow's requirements (**YAGNI**). -The iteration phases — and we move through them quickly, sometimes in parallel: +The iteration phases — moved through quickly, sometimes in parallel: 1. **Spike / Experiment** — can this thing even be built? 2. **Proof of Concept** — does the experiment survive contact with reality? -3. **MVP** — first version we can run in production. Start collecting real feedback. +3. **MVP** — the first version that can run in production. Start collecting real feedback. 4. **Improvements** — stabilize, add functionality, harden. The best feedback is the feedback from people who have seen the thing. ## Ways of working -One size does not fit all. The way we work follows the principles above, including the principle of evolving how we work. +One size does not fit all. The way work happens follows the principles above, including the principle of evolving how work happens. - **Start lean** with processes and ceremonies. Get to know each other and the work first. - **Scrum + Kanban hybrid** — dynamic cycles, no firm sprint end dates. A cycle is over when an Epic is delivered. Estimation is approximate; Epics themselves are kept lean. diff --git a/src/docs/Ways-of-Working/Principles/Purpose-and-Direction.md b/src/docs/Ways-of-Working/Principles/Purpose-and-Direction.md index bf38398..9b6e436 100644 --- a/src/docs/Ways-of-Working/Principles/Purpose-and-Direction.md +++ b/src/docs/Ways-of-Working/Principles/Purpose-and-Direction.md @@ -1,39 +1,39 @@ --- title: Purpose and direction -description: Why we build, who we build for, and the least-privilege stance under every decision. +description: The purpose behind the work, the audience it serves, and the least-privilege stance under every decision. --- # Purpose and direction ## Start with Why — the Golden Circle -Every piece of work — at every level — should be groundable in three concentric questions: +Every piece of work — at every level — is groundable in three concentric questions: -- **Why** — what change in the world are we trying to make? Vision. -- **How** — what approach do we take to make that change happen? Mission. -- **What** — what concrete thing are we delivering right now? +- **Why** — what change in the world is this trying to make? Vision. +- **How** — what approach makes that change happen? Mission. +- **What** — what concrete thing is being delivered right now? -[Goal Setting](../Goal-Setting.md) owns the strategy path from Mission through OKR and Initiative. [Issue Planning](../Issues/Process/Planning.md) applies Why, How, and What progressively from Epic and PBI aggregates to Task and Bug delivery leaves; this principle does not assign them to copied body sections. +Strategy and planning apply Why, How, and What progressively, from long-lived aggregates down to delivery leaves. This principle fixes the questions; it does not assign them to particular body sections or planning artifacts. ## Product / service mindset -We are building something for people who should **want** to use it. Without users, we are nothing. Every decision is filtered through: does this make the product more wanted, or less? +The thing being built is for people who should **want** to use it. Without users, it is nothing. Every decision is filtered through one question: does this make the product more wanted, or less? ## Build for all developers -We target all platforms and all shells. Our code, scripts, workflows, and documentation must work regardless of whether the developer is on Windows, macOS, or Linux. Line endings, path separators, shell assumptions — none of these should silently break someone's experience. Repository configuration (`.gitattributes`, CI matrices, test environments) must reflect this. +Every platform and every shell is a target. Code, scripts, workflows, and documentation MUST work regardless of whether the developer is on Windows, macOS, or Linux. Line endings, path separators, shell assumptions — none of these may silently break someone's experience. Repository configuration such as `.gitattributes`, CI matrices, and test environments MUST reflect this. ## Build for the modern engineer -We build for engineers using the latest tools and platforms. We do not support deprecated or end-of-life software. Concretely: we target current, cross-platform, actively-developed runtimes — not legacy editions frozen years ago. The same applies across the stack: latest stable releases, current LTS versions, modern APIs. If a tool has a successor, use the successor. +The audience is engineers using current tools and platforms. Deprecated and end-of-life software is not supported. Concretely: target current, cross-platform, actively-developed runtimes — not legacy editions frozen years ago. The same applies across the stack: latest stable releases, current LTS versions, modern APIs. If a tool has a successor, the successor is the target. ## Dogfooding -Be the first customer of every service we build. But avoid full self-dependency on a service before it is proven — explore and use it in non-critical contexts first, then promote it as confidence grows. +Be the first customer of every service produced here. Avoid full self-dependency on a service before it is proven — use it in non-critical contexts first, then promote it as confidence grows. ## Least-privilege -Every identity — human, agent, or workflow — gets only the permissions it needs to complete its specific task, and nothing more. This applies to GitHub tokens, workflow permissions, API scopes, and agent capabilities. +Every identity — human, agent, or workflow — gets only the permissions it needs to complete its specific task, and nothing more. This applies to tokens, workflow permissions, API scopes, and agent capabilities. Concretely: diff --git a/src/docs/Ways-of-Working/Principles/References.md b/src/docs/Ways-of-Working/Principles/References.md index dfa90ed..cc994dd 100644 --- a/src/docs/Ways-of-Working/Principles/References.md +++ b/src/docs/Ways-of-Working/Principles/References.md @@ -7,8 +7,8 @@ description: The literature behind these principles. Literature and books that inform these principles: -- **Start With Why** — Simon Sinek. The Golden Circle framework (Why → How → What) that grounds our purpose-first approach. [simonsinek.com](https://simonsinek.com/books/start-with-why/) -- **Measure What Matters** — John Doerr. The OKR framework we use for goal-setting — objectives over KPIs. [whatmatters.com](https://www.whatmatters.com/) +- **Start With Why** — Simon Sinek. The Golden Circle framework (Why → How → What) that grounds the purpose-first approach. [simonsinek.com](https://simonsinek.com/books/start-with-why/) +- **Measure What Matters** — John Doerr. The OKR framework behind goal-setting — objectives over KPIs. [whatmatters.com](https://www.whatmatters.com/) - **Getting Things Done** — David Allen. The discipline of writing things down so they can be shared, reflected on, and acted upon. [gettingthingsdone.com](https://gettingthingsdone.com/) - **Clean Code** — Robert C. Martin. Readability, naming, and structure over cleverness. - **Refactoring** — Martin Fowler. Make change easy, then make the easy change. diff --git a/src/docs/Ways-of-Working/Principles/Software-Design.md b/src/docs/Ways-of-Working/Principles/Software-Design.md index 5aad48e..a85e9bb 100644 --- a/src/docs/Ways-of-Working/Principles/Software-Design.md +++ b/src/docs/Ways-of-Working/Principles/Software-Design.md @@ -1,6 +1,6 @@ --- title: Software design -description: SOLID, extensibility, smart defaults with local overrides, DRY with judgment, and making change easy before making the change. +description: SOLID, extensibility, smart defaults with local overrides, secure by default, DRY with judgment, and making change easy before making the change. --- # Software design @@ -17,7 +17,7 @@ description: SOLID, extensibility, smart defaults with local overrides, DRY with Extend by adding, not by modifying what already works — the Open/Closed principle, applied beyond code to how the whole system grows. Ways of working and standards are the **stable core**; the tools that act on them — coding agents, runtimes, integrations — are **pluggable adapters** that slot in. Adding or swapping a tool means writing new pointers, not rewriting process knowledge. -The system stays pluggable: the docs do not change when a new agent runtime is added — only a new integration layer is written. See the [Agentic Development](../Agentic-Development.md) specification for how this plays out in practice. +The system stays pluggable: the documentation does not change when a new agent runtime is added — only a new integration layer is written. ## Smart defaults, local overrides @@ -39,7 +39,22 @@ This shape is chosen for manageability over the life of a system, and it earns t Make the wide default easy to set and the local override easy to make. When the two disagree, the more specific one wins — predictably, by its position in the hierarchy, never by special-casing. -This is [Easy and Safe](../../index.md) expressed as design: doing the right thing takes no effort because it is the default, and deviating is deliberate and contained because it is a local override. [Least-privilege](Purpose-and-Direction.md#least-privilege) and [secure by default](../../Coding-Standards/Security.md#secure-by-default) are this principle applied to permissions and security; the way [the vision cascades](../../Vision/index.md#how-the-vision-cascades) is its shape applied to knowledge. +This is easy-and-safe expressed as design: doing the right thing takes no effort because it is the default, and deviating is deliberate and contained because it is a local override. [Least-privilege](Purpose-and-Direction.md#least-privilege) and [secure by default](#secure-by-default) are this principle applied to permissions and security; the way knowledge cascades from belief to practice is its shape applied to documentation. + +## Secure by default + +The safe configuration is the one that requires no decision. Security is a property of the default, not a step someone remembers to take — anything that depends on being remembered will eventually be forgotten. + +This follows from smart defaults with local overrides, applied to risk. Where a choice exists between a convenient default and a safe one, the safe option MUST be the default, and relaxing it MUST be explicit, local, and visible in review. A setting that is safe only when someone opts in is not a secure default; it is an unsafe default with documentation. + +Concretely: + +- New surfaces start closed. Access, exposure, and permission are granted deliberately rather than removed after the fact. +- Secrets are never a fallback value. Absent configuration fails the operation rather than silently continuing with something weaker. +- A relaxation is scoped to the thing that needs it and carries the reason with it, so it can be found and revisited. +- Validation runs by default. Turning a check off is the exception that gets argued for, not the state a repository drifts into. + +The intent is that the path of least effort and the correct path are the same path. When they diverge, the default is wrong — not the person who followed it. ## DRY — with judgment diff --git a/src/docs/Ways-of-Working/Principles/index.md b/src/docs/Ways-of-Working/Principles/index.md index 9b80264..f5024e2 100644 --- a/src/docs/Ways-of-Working/Principles/index.md +++ b/src/docs/Ways-of-Working/Principles/index.md @@ -5,7 +5,7 @@ description: The foundational beliefs and product mindset behind every decision. # Principles -The ideas underneath how we work — with each other and with our agents. These are evergreen; everything else on this site refers back to them. +The ideas underneath how work happens — between people, and between people and their agents. These are evergreen; every other layer refers back to them. Principles are grouped by theme; each theme is its own page so an agent can load only the one it needs. Start here, then follow the theme that fits the task. @@ -13,10 +13,10 @@ Principles are grouped by theme; each theme is its own page so an agent can load | Page | Description | | --- | --- | -| [Purpose and direction](Purpose-and-Direction.md) | Why we build, who we build for, and the least-privilege stance under every decision. | +| [Purpose and direction](Purpose-and-Direction.md) | The purpose behind the work, the audience it serves, and the least-privilege stance under every decision. | | [AI-first development](AI-First-Development.md) | Agents as first-class participants, determinism before intelligence, and how humans and agents share the work. | -| [Software design](Software-Design.md) | SOLID, extensibility, smart defaults with local overrides, DRY with judgment, and making change easy before making the change. | -| [Engineering practices](Engineering-Practices.md) | Write it down, everything as code, evergreen docs, test-driven development, and shift-left quality. | +| [Software design](Software-Design.md) | SOLID, extensibility, smart defaults with local overrides, secure by default, DRY with judgment, and making change easy before making the change. | +| [Engineering practices](Engineering-Practices.md) | Write it down, everything as code, evergreen documentation, test-driven development, fleet-wide change, and shift-left quality. | | [Planning and delivery](Planning-and-Delivery.md) | Roadmapping, lean delivery, and the loops that keep iteration fast. | | [References](References.md) | The literature behind these principles. | @@ -24,4 +24,29 @@ Principles are grouped by theme; each theme is its own page so an agent can load ## Why these principles matter -These are the assumptions every agent decision rests on. When an agent's behaviour is unclear or contested, the answer comes from here. When something here turns out to be wrong, update this page — not just one agent. +These are the assumptions every other decision rests on. When behaviour is unclear or contested, the answer comes from here. When something here turns out to be wrong, this page changes — not one agent, one repository, or one review. + +## What a principle is + +A principle is a belief that holds across every capability, repository, and runtime. It states a position, not a procedure: it says what is always true, and leaves to a standard or a specification the question of how that is achieved in one place. + +A principle MUST be true of work that has not been imagined yet. A rule that only makes sense for one capability, one language, or one tool is not a principle — it belongs in the standard or specification that owns that ground. This is the test that keeps this layer small: if a statement would need editing when a new product is added, it was never a principle. + +## Principles do not link down + +Every layer below this one conforms to these principles and MUST NOT restate them. The direction of reference is fixed: + +- A standard, specification, or design **links up** to the principle it obeys, and never copies its wording. +- A principle **does not link down** in normative prose to the standard, + capability, or product that applies it. + +The asymmetry is deliberate. A principle that names the things that currently implement it acquires a maintenance burden it cannot carry: every new capability becomes an edit here, and every retired one leaves a dangling claim. Keeping references one-directional means this layer stays stable while everything beneath it moves. + +The generated index and other navigation links are allowed to list their direct +children: they help a reader find a page and do not claim that a child implements +or governs its parent. The prohibition applies to normative dependency links, +which would couple a stable principle to the changing things that apply it. + +The consequence for a reader is that this layer answers *why*, and never enumerates *where*. To find what applies a principle, read the layer that claims it — the standard or specification says which principle it conforms to, so the relationship is discoverable from below without being duplicated above. + +A principle MAY name another principle. Cross-references inside this layer are horizontal, not downward, and do not create the coupling this rule exists to prevent. diff --git a/src/docs/Ways-of-Working/Repository-Segmentation.md b/src/docs/Ways-of-Working/Repository-Segmentation.md index 3ed7a54..58da27d 100644 --- a/src/docs/Ways-of-Working/Repository-Segmentation.md +++ b/src/docs/Ways-of-Working/Repository-Segmentation.md @@ -33,3 +33,17 @@ What belongs in a repository, and when to split or combine. The boundary of a re - Every repository states, at its root, what it is, what it owns, and how it ships — in a README kept current with the code. See [README-Driven Context](Readme-Driven-Context.md). - Documentation and decisions live with the code they describe, so the repository boundary also bounds its own context instead of scattering it into a central store. +- The boundary is also declared in machine-readable form, so automation can determine what a repository is without inferring it from file layout. See [Repository Type Property](Repository-Type-Property.md). + +## The boundary determines the governance that applies + +- Segmentation and governance are the same decision seen twice. The lifecycle and branch-strategy seams that decide where a repository ends are the seams that decide which branch model, which required files, and which protection rules it carries. +- A repository is therefore classified at creation, and its classification follows from the same questions used to draw its boundary: what it ships, how it is versioned, and how changes reach its default branch. See [Repository Governance](../Capabilities/repository-governance/spec.md). +- A repository that cannot be classified is usually mis-segmented. When one repository needs two branch models or two release cadences, the classification is not ambiguous — the boundary is wrong, and the resolution is to split rather than to hold both models in one place. + +## Where this connects + +- [Repository Standard](Repository-Standard.md) — the contract each segmented repository must satisfy. +- [Repository Governance](../Capabilities/repository-governance/index.md) — how a repository's classification drives its rules and required files. +- [Repository Type Property](Repository-Type-Property.md) — the declaration a repository carries so its boundary is machine-readable. +- [Branching and Merging](Branching-and-Merging.md) — the branch models a boundary must choose exactly one of. diff --git a/src/docs/Ways-of-Working/Repository-Standard.md b/src/docs/Ways-of-Working/Repository-Standard.md index c64c502..628543d 100644 --- a/src/docs/Ways-of-Working/Repository-Standard.md +++ b/src/docs/Ways-of-Working/Repository-Standard.md @@ -7,7 +7,7 @@ description: The baseline files and behaviours every repository must expose so i A repository is the smallest unit of ownership in the MSX ecosystem. It must explain what it is, how to contribute, how security is handled, how dependencies are kept current, and which standards govern its automation. -The Repository Standard is the default for every repository across the MSX Enterprise, regardless of initiative, organization, or technology. It defines the baseline contract a repository must meet to be understandable, secure, and maintainable on its own. +The Repository Standard is the default for every governed repository across the MSX Enterprise, regardless of initiative, organization, or technology. It defines the baseline contract a repository must meet to be understandable, secure, and maintainable on its own. Initiative standards operate at the same altitude as this standard, not beneath it. An initiative such as PSModule adds to and adjusts these defaults for its repository types rather than merely implementing them. A repository inherits every rule this standard sets unless its initiative explicitly changes it; where an initiative standard adds or overrides a rule, the initiative standard governs that initiative's repositories. @@ -17,7 +17,9 @@ Which natural language each repository artifact is written in follows [Natural L ## Required files -Every repository must carry the files that make it understandable and governable on its own. +Every governed repository must carry the files that make it understandable and +governable on its own. An Unmanaged repository carries the explicit +discoverability minimum defined below instead. | File | Requirement | | --- | --- | @@ -28,7 +30,7 @@ Every repository must carry the files that make it understandable and governable | `SUPPORT.md` | Explains where users ask for help. | | `CODE_OF_CONDUCT.md` | Defines expected community behaviour. | | `AGENTS.md` and its client routes | Route every agent runtime from this repository's own files outward to the initiative and central documentation, then to memory. [Agentic Development](Agentic-Development.md#which-agent-files-a-repository-carries) names the files and the path each one sits at. | -| `.github/dependabot.yml` | Configures ecosystem-appropriate dependency-update pull requests. The `github-actions` ecosystem is expected in virtually every repository; add the language, package, container, or infrastructure ecosystems the repository actually develops in. | +| `.github/dependabot.yml` | Configures platform-native dependency-update pull requests for supported ecosystems. The `github-actions` ecosystem is expected in virtually every repository; an unsupported ecosystem follows the centrally managed exception path rather than a repository-local updater. | | `.github/CODEOWNERS` | Routes reviews to responsible owners. | | `.github/pull_request_template.md` | Scaffolds pull requests in the MSX [PR Format](PR-Format.md) (PR Manager) style — an icon + change-type + user-facing-outcome title, user-facing description sections, an optional technical-details block, and a related-issues block. | | `.gitattributes` | Normalizes line endings and declares text/binary handling so the repository can be developed and built consistently on Linux, macOS, and Windows. | @@ -36,6 +38,29 @@ Every repository must carry the files that make it understandable and governable Repository types may require additional files. For example, a PowerShell module may require `.github/PSModule.yml`, while a GitHub Action may require `action.yml`. +### Required files by type + +The table above is the mandatory set for every governed repository. A +[repository type](../Capabilities/repository-governance/design-types.md) adds to +that set; no governed type subtracts from it. `Unmanaged` is the explicit +full-governance exemption: it is audited only for the discoverability minimum +stated below, not for the governed baseline. + +| Type | Adds | +| --- | --- | +| **Standard** | Nothing beyond the mandatory set. | +| **Artifact** | The artifact's own manifest or metadata file — whatever declares its identity to the ecosystem it publishes into — and a changelog where the ecosystem expects one rather than reading [GitHub Releases](../Capabilities/release-management/design-publishing-targets.md). | +| **Infrastructure** | Documentation of each environment the repository deploys to and how a change reaches it, plus the promotion automation the [promotion flow](../Capabilities/repository-governance/design-types.md#infrastructure) requires. | +| **Docs** | The documentation source root and the build configuration the documentation-build check runs. | +| **Memory** | The structure documented by the [memory repository template](../Capabilities/agentic-development/memory-template.md). | +| **Unmanaged** | Nothing — but the exemption does not extend to discoverability: `README.md`, `SECURITY.md`, and the agent router remain required, because a repository nobody governs is still a repository someone will open. | + +The set a governed repository is audited against is the mandatory set plus the +additions of every type it declares; an Unmanaged repository is audited only +against its discoverability minimum. Presence is verified by +[reconciliation](../Capabilities/repository-governance/design.md#required-files-by-type), +not by review. + The agent-file row is the one entry this table does not spell out in full. [Agentic Development](Agentic-Development.md#which-agent-files-a-repository-carries) owns that set — one router at the repository root, plus a route for every client that reads a different filename — and the [agentic development spec](../Capabilities/agentic-development/spec.md) limits what a route may contain: a pointer to the router and, at most, genuinely runtime-specific configuration such as permission scopes, never a reading order, a workflow, or a standard. A repository is audited against that one list, so a second copy here would be a second list to keep in step. ## README defaults @@ -78,14 +103,21 @@ Initiative docs define the implementation: exact folder layout, publishing workf ## Dependency and supply-chain defaults -Every repository that has external dependencies must configure automated update pull requests. Dependabot is the default GitHub-native mechanism unless the initiative documents a different implementation. +Every repository that has external dependencies must have automated update coverage. +Dependabot is the default GitHub-native mechanism for its supported ecosystems; an +unsupported ecosystem uses the centrally managed exception path defined by +[Dependency Updates](../Capabilities/dependency-updates/spec.md#coverage), never a +repository-specific updater. -At minimum, repositories with GitHub Actions must include a `github-actions` ecosystem entry. Repositories with language, package, container, or infrastructure dependencies must include the relevant ecosystems too. +At minimum, repositories with GitHub Actions must include a `github-actions` +ecosystem entry. Repositories with language, package, container, or infrastructure +dependencies include their supported native ecosystems; unsupported ones are +recorded centrally for shared update coverage. Dependency update pull requests must: -- Use labels that identify the dependency category and ecosystem. -- Keep update-level labels separate from release-bump labels. +- Use namespaced labels that identify the dependency category and ecosystem, per [Automation Labels](Automation-Labels.md). +- Keep update-level labels in a namespace separate from release-bump labels. - Pass the same CI and review gates as human-authored changes. - Keep SHA-pinned actions pinned to immutable commit SHAs with a version comment when possible. - Be reviewed before merge, even when auto-merge is allowed for low-risk updates. @@ -155,6 +187,8 @@ For example, PSModule can define its module-specific managed files in `PSModule/ ## Where this connects - [Organization Standard](Organization-Standard.md) — what an initiative organization must define centrally. +- [Repository Governance](../Capabilities/repository-governance/spec.md) — how a repository's type selects the controls and the file set it is audited against. +- [Automation Labels](Automation-Labels.md) — the namespacing rule every label a repository's automation reads must follow. - [Agentic Development](Agentic-Development.md) — which agent files a repository carries and why the entry point is a pointer. - [Repository Type Property](Repository-Type-Property.md) — the `Type` custom property that classifies a repository and drives which type-specific files and controls apply. - [README-Driven Context](Readme-Driven-Context.md) — why the README is the front door. diff --git a/src/docs/Ways-of-Working/Repository-Type-Property.md b/src/docs/Ways-of-Working/Repository-Type-Property.md index edb0f4c..d8d1971 100644 --- a/src/docs/Ways-of-Working/Repository-Type-Property.md +++ b/src/docs/Ways-of-Working/Repository-Type-Property.md @@ -1,6 +1,6 @@ --- title: Repository Type Property -description: How a single "Type" custom property classifies every repository in an initiative organization and drives which org-wide controls apply to it. +description: How a multi-select "Type" custom property classifies every repository in an initiative organization and drives which org-wide controls apply to it. --- # Repository Type Property @@ -8,24 +8,73 @@ description: How a single "Type" custom property classifies every repository in [Organization Standard](Organization-Standard.md) requires every initiative to define "repository types used by the initiative" and "required custom properties, labels, branch protection, and review rules." This page is the concrete mechanism that satisfies both at -once: a single GitHub organization **custom property named `Type`**, whose value per -repository determines which org-wide rulesets and controls apply. +once: one GitHub organization **custom property named `Type`**, whose values per repository +determine which org-wide rulesets and controls apply. + +This page owns the **mechanism** — how the property is declared, how ruleset conditions +target it, and how those conditions are changed safely. What the individual values *mean* +is owned by [Repository Types](../Capabilities/repository-governance/design-types.md), and +the governance they drive by [Repository +Governance](../Capabilities/repository-governance/spec.md). ## The pattern Each initiative organization defines: -1. One `single_select` custom property named `Type`, required on every repository, with a - default value (typically `Other`). +1. One `multi_select` custom property named `Type`, required on every repository, with a + default value. 2. An allowed-values list specific to that organization's actual repository shapes (a docs org and a module-publishing org will not need the same list). 3. Org-wide rulesets (branch protection, required reviews, and similar controls) that - target repositories by their `Type` value instead of by repository name. + target repositories by their `Type` values instead of by repository name. Setting a repository's `Type` is then the single action that determines every `Type`-scoped control it inherits — no per-repository ruleset edits, no repository-name lists to keep in sync by hand. +## Why the property is multi-select + +A repository's classification answers more than one question, and the answers are +independent. *How does a change reach the protected branch?* is a branch-model question. +*What else must be true before it does?* — the documentation builds, the artifact history +stays linear — is a layering question. A repository can be an infrastructure stack whose +documentation also publishes, and a single-select property cannot express that without +inventing a combined value for every pairing that occurs. + +So `Type` is `multi_select`, and its values divide into branch-model types, layering types, +and the exemption type ([the catalogue](../Capabilities/repository-governance/design-types.md)). +A ruleset condition tests whether a repository's `Type` **includes** a value, so a layering +ruleset matches without knowing which branch model the repository also declares. + +The consequence is that combinations must be validated rather than assumed: a multi-select +property accepts any subset, including contradictory ones. The [validation +rules](../Capabilities/repository-governance/design-types.md#validation-rules) state which +subsets are meaningful, and validation is enforced by +[reconciliation](../Capabilities/repository-governance/design.md#drift-detection-and-reconciliation) +rather than by the property schema, which cannot express them. + +## Migrating from a single-select property + +The platform does not convert a property between selection modes in place, so the migration +uses a temporary, uniquely named property and recreates the canonical name: + +1. Choose a name such as `Type_Migration`, after verifying that no organization + property already uses it, and create it as `multi_select`. +2. Populate it for every repository from the current single-select `Type`, so + each new value set is a one-element set carrying the same meaning. +3. Create or update every replacement ruleset to read `Type_Migration`, then + verify its computed coverage against the existing ruleset as described below. + Both controls remain active until the coverage sets match. +4. Delete the old single-select `Type` schema only after no rule reads it, then + create the canonical `Type` schema as `multi_select` and copy each temporary + value set into it. +5. Update every ruleset from `Type_Migration` to the new canonical `Type`, verify + the coverage diff again, and delete `Type_Migration` only when nothing reads it. + +Only after the second coverage diff is verified does a repository gain a second +value. Adding values and changing the property's mode at the same time makes a +coverage diff impossible to attribute. + ## Filter by exclusion, not by inclusion When a ruleset condition targets `Type`, write it as an **exclude** list of the `Type` @@ -67,17 +116,17 @@ repository before making it live: than reasoning about condition JSON by hand, since it reflects GitHub's own evaluation). 4. Diff the two coverage sets. The only differences should be the `Type` values the migration intentionally excludes. Any other difference means the new condition is wrong. -5. Only after the diff matches expectations: delete the old ruleset, then create the final - ruleset under its real name, then delete the temporary one. This ordering means both - rulesets are briefly active together rather than there being a gap with neither active. +5. Only after the diff matches expectations, update the real ruleset with its + complete replacement representation and repeat the coverage check. Delete the + temporary replacement only after the real ruleset is confirmed active. -## A known API quirk: ruleset `PATCH` may not work +## Ruleset updates use `PUT`, not `PATCH` -`PATCH` on `/orgs/{org}/rulesets/{id}` has been observed to fail (`404`) for tokens that -otherwise have full `GET`/`POST`/`DELETE` access to the same endpoint, including on a -disposable test ruleset created solely to isolate the failure. Treat in-place ruleset edits -as unreliable and use the delete-old / create-new sequence above instead, even for small -condition changes. +Update an organization ruleset with `PUT /orgs/{org}/rulesets/{ruleset_id}` and +the complete desired ruleset representation. Do not use `PATCH`: it is not the +documented update operation and has proved unreliable for otherwise authorized +tokens. `PUT` preserves the ruleset identity while the temporary replacement and +coverage comparison make the change safe to roll out. ## Deprecating single-purpose properties @@ -89,24 +138,35 @@ rather than keeping two overlapping classification properties. `Type` is the one that should answer "what kind of repository is this," and every `Type`-scoped control should read from it. -## Worked examples +## Historical organization inventories -Both current MSX initiative organizations use this pattern: +The values below are historical inventories, recorded before the branch-model, +layering, and exemption taxonomy existed. They are **not** canonical type +examples and must not be copied into a new organization without migration: | Organization | `Type` allowed values | Notes | | --- | --- | --- | -| `MSXOrg` | `Docs`, `Memory`, `VSCodeExtension`, `Other` | Introduced from scratch, replacing a prior single-purpose `BranchStrategy` property. | -| `PSModule` | `Action`, `Archive`, `Docs`, `Framework`, `FunctionApp`, `Memory`, `Module`, `Other`, `Template`, `Workflow` | `Memory` added to an existing, already-populated `Type` property; the ruleset condition changed from a repository-name allow-list (`~ALL`) to a `Type`-based exclude. | +| `MSXOrg` | `Docs`, `Memory`, `VSCodeExtension`, `Other` | Legacy values that predate the canonical taxonomy. | +| `PSModule` | `Action`, `Archive`, `Docs`, `Framework`, `FunctionApp`, `Memory`, `Module`, `Other`, `Template`, `Workflow` | Legacy values that predate the canonical taxonomy. | + +An organization's canonical list is shaped by what it builds, but it has the +taxonomy defined by [Repository Types](../Capabilities/repository-governance/design-types.md): +one branch model (explicit or defaulted), any layering values, and `Unmanaged` as +the sole exemption. A migration maps historical values into that taxonomy before +the canonical `Type` property becomes authoritative. -In both organizations, repositories with `Type: Memory` — the +Where a historical value maps to Memory, repositories with `Type: Memory` — the [Memory Repository Template](../Capabilities/agentic-development/memory-template.md)'s no-PR, direct-commit-to-`main` repositories — are excluded from the org-wide pull-request- -required ruleset. That template's workflow only works because the ruleset stops matching -`Memory`-typed repositories; without this, direct pushes to a memory repository's `main` -are rejected the same as on any other repository. +required ruleset. The exclusion applies only to that requirement; Memory retains +the rest of the governed baseline. ## Where this connects +- [Repository Governance](../Capabilities/repository-governance/spec.md) — the framework this + property is the input to. +- [Repository Types](../Capabilities/repository-governance/design-types.md) — what each value + means, how values compose, and which combinations are invalid. - [Organization Standard](Organization-Standard.md) — the requirement this property implements: documented repository types and the custom properties, rulesets, and review rules attached to them. diff --git a/src/docs/Ways-of-Working/Review-Etiquette.md b/src/docs/Ways-of-Working/Review-Etiquette.md index 937cf9a..361e874 100644 --- a/src/docs/Ways-of-Working/Review-Etiquette.md +++ b/src/docs/Ways-of-Working/Review-Etiquette.md @@ -14,10 +14,10 @@ How to disagree well, how to keep reviews focused, and how to keep the loop conv Consider each of the following dimensions when reviewing a PR: - **Functional** — Does the code meet the stated needs? -- **Reliability** — Are we confident that the code will run failure-free? +- **Reliability** — Is there confidence that the code will run failure-free? - **Performance** — Is the code as efficient as it can be? -- **Usability** — Consider the user(s) of the service/product we provide; is the experience of high quality with this change? -- **Security** — Are we improving the security posture of the service/product with the changes to the code? Are we worsening it? +- **Usability** — Consider the users of the service or product being provided; is the experience of high quality with this change? +- **Security** — Does this change improve the security posture of the service or product? Does it worsen it? - **Maintainability** — Is the architecture good for future maintenance? - **Standards** — Is the code meeting coding standards for the project and language? diff --git a/src/docs/Ways-of-Working/Session-Interactions.md b/src/docs/Ways-of-Working/Session-Interactions.md new file mode 100644 index 0000000..d9d9baa --- /dev/null +++ b/src/docs/Ways-of-Working/Session-Interactions.md @@ -0,0 +1,141 @@ +--- +title: Session Interactions +description: Recognised phrases that steer a working session deterministically, why each is defined once as a standard rather than embedded in tool-specific files, and what an interaction may not do. +--- + +# Session Interactions + +Most of a session is driven by the work itself: an issue is refined, a change is built, a +review is answered. But some things a contributor needs mid-session are not stages of the +work — they are operations *on* the session. Closing out cleanly. Setting a tangent aside +without losing it. Handing over. + +These recur constantly and, left undefined, are performed differently every time. An +**interaction** is a recognised phrase bound to a defined procedure, so that asking for one of +these gets the same result on every occasion, in every runtime, from a human or an agent. + +## An interaction is a phrase bound to a procedure + +An interaction MUST be a short natural phrase, and it MUST resolve to a procedure defined in +documentation. + +The phrase matters because the alternative is ceremony. A contributor mid-task will not invoke +a formal command to park a tangent; they will either describe the tangent in prose and hope it +is handled, or drop it. A recognised phrase costs nothing to use, which is the only reason it +gets used at the moment it is needed rather than retrospectively. + +The binding to documentation matters because a phrase without a defined procedure is worse +than no phrase — it *looks* like a control while producing whatever the runtime improvises. + +| Property | Requirement | +| --- | --- | +| Phrase | Short, natural, and unambiguous in the context of a working session | +| Procedure | Defined once in documentation, reachable from the index | +| Scope | Operates on the session or its artifacts, not on the domain of the work | +| Result | The same outcome regardless of runtime or operator | + +## The standing vocabulary + +These interactions are recognised. Each names a procedure that already exists elsewhere; the +phrase is the route to it, not a second definition of it. + +| Phrase | What it means | What it does | +| --- | --- | --- | +| **Wrap up** | The session is ending, deliberately | Scan for work that exists but is not tracked — uncommitted changes, unpushed commits, undocumented decisions, lessons worth keeping — and land each one in its proper artifact | +| **Park** | This is real, but not now, and not here | Move a tangent out of the session into an issue in the repository that owns it, with enough context to be actionable later, and return to the original task | +| **Triage** | Decide what this is before working it | Classify the item — its type, the repository that owns it, whether it is already known — and route it, without beginning implementation | +| **Handoff** | Someone or something else continues this | Bring the artifacts to a state another participant can resume from, and record what remains as [state rather than as a message](../Capabilities/agentic-development/agent-interaction.md#handover-is-a-state-not-a-message) | + +Two properties are shared by all four, and they are what make the vocabulary worth having: + +- **None of them decide anything about the work.** Parking an item does not judge it; triage + classifies but does not implement. An interaction moves work into the right place and leaves + the decision to whoever owns it. +- **All of them end with durable state.** The value of an interaction is that nothing is left + in the session — after wrap-up or handoff, the session can be discarded without loss. + +### Wrap up + +Wrap-up exists because the end of a session is where work leaks. A contributor who has been +deep in a task holds a great deal that is not written down: a decision and its reason, a dead +end worth not repeating, a small problem noticed and not registered. Ending the session +discards all of it silently. + +So wrap-up is a **scan**, not a summary. Producing a description of what happened is not +wrap-up; landing each untracked thing in the artifact that should hold it is. Where the scan +finds a problem too large to fix in scope, it becomes an issue in the owning repository — +never a note that survives only in the session. + +### Park + +Parking is the counterpart, applied mid-session. Real work is discovered while doing other +work, and the two bad options are to follow the tangent (losing the original task) or to drop +it (losing the tangent). + +Park takes the third: the tangent becomes an issue in the repository that owns it, with the +context that makes it actionable, and the session returns to what it was doing. A parked item +MUST be legible to someone who was not in the session, because the session is exactly what +will not be available when the item is picked up. + +### Triage + +Triage separates *classifying* an item from *working* it. Something arrives — a report, a +request, an observation — and the reflex is to start on it, which commits effort before +establishing whether the item is well-formed, already known, or even owned here. + +So triage resolves those questions and stops. Its output is a routed, classified item, and +beginning implementation during triage MUST be treated as leaving the interaction. + +### Handoff + +Handoff makes continuation possible without transferring context that only exists in one +head or one session. It is defined by its result: the artifacts alone are sufficient to +resume from. + +That is why handoff is a state and not a message. A message announcing a handover leaves the +work in whatever state it happened to be in; a handoff brings the artifacts to a resumable +state first, and the announcement becomes redundant. + +## Defined once, referenced everywhere + +An interaction MUST be defined in exactly one place, and every runtime that recognises it MUST +reference that definition rather than restate it. + +This is the same rule the framework applies to +[client routes](../Capabilities/agentic-development/design.md#client-behavior) and to +[named intents](../Capabilities/agentic-development/plugin-distribution.md#an-intent-is-a-pointer), +for the same reason. An interaction embedded in a tool-specific instruction file is a copy, +and copies drift: the phrase then means one thing in one runtime and something subtly +different in another, while both appear to honour the same standard. A contributor who learns +the behaviour in one place is then wrong somewhere else, which is worse than not knowing it. + +A runtime may make an interaction *easier to invoke* — a shortcut, a command, a packaged +intent. It MUST NOT thereby define what the interaction does. + +## What an interaction is not + +The vocabulary stays useful only by staying small and staying out of the way of the process. + +- An interaction MUST NOT define or replace a [workflow stage](Workflow.md#find-the-current-stage). + Stages are how work progresses; interactions operate on the session. "Implement this issue" + enters a stage and is not an interaction. +- An interaction MUST NOT be the only way to reach a procedure. The phrase is a convenience + over documentation that stands on its own, so the procedure remains reachable through the + index by someone who has never heard the phrase. +- An interaction MUST NOT carry authority the operator does not have. Parking an item creates + an issue; it does not prioritise it. Wrap-up records a lesson; it does not change a standard. +- The vocabulary SHOULD stay small. A phrase set large enough to need its own reference is a + command language, and a command language is learned rather than recognised — which forfeits + the reason for using phrases at all. + +Adding an interaction is therefore a documentation change: define the procedure, bind a phrase +to it here, and let each runtime reference it. A phrase recognised by one runtime only is a +local convenience and MUST NOT be relied on by documentation. + +## Where this connects + +- [Workflow](Workflow.md) — the stages interactions operate alongside, and the keyword shortcuts that enter them. +- [Agent Interaction](../Capabilities/agentic-development/agent-interaction.md#handover-is-a-state-not-a-message) — why handover is expressed as artifact state. +- [Plugin Distribution](../Capabilities/agentic-development/plugin-distribution.md) — how a runtime may package an interaction as an invocable intent without redefining it. +- [Runtime Integration](../Capabilities/agentic-development/runtime-integration.md) — what a runtime supplies, and why process is never part of it. +- [Spec-Driven Development](Spec-Driven-Development.md) — where a lesson found during wrap-up is written down. diff --git a/src/docs/Ways-of-Working/Spec-Driven-Development-Templates.md b/src/docs/Ways-of-Working/Spec-Driven-Development-Templates.md new file mode 100644 index 0000000..a6b2791 --- /dev/null +++ b/src/docs/Ways-of-Working/Spec-Driven-Development-Templates.md @@ -0,0 +1,363 @@ +--- +title: Spec-Driven Development Templates +description: A copyable skeleton for every spec-driven artifact — specification, feature addendum, design, implementation doc, guide, reference, research, and decision record. +--- + +# Spec-Driven Development Templates + +One skeleton per artifact tier defined in [Spec-Driven Development](Spec-Driven-Development.md#the-artifact-tiers). Copy the one that matches the tier being written. Every section is present so nothing is forgotten; **delete a heading rather than marking it "N/A"** — empty scaffolding hides the real content. + +Each template assumes the [authoring conventions](Spec-Driven-Development.md#authoring-conventions): present tense, impersonal, normative, and free of dates and status. + +## Specification + +````markdown +--- +title: — Spec +description: +--- + +# — Spec + + + +## Problem + + + +## Outcomes and impact + +- **Outcome:** +- **DORA:** +- **Domain signal:** + +## Users and jobs + + + +## Scope + +**In scope** + +- <...> + +**Out of scope** + +- <...> + +## Non-goals + +- + +## Functional requirements + +### FR1 — { #fr1 } + +#### Behavioral scenarios + +```gherkin +Scenario: + Given + When + Then +``` + +### FR2 — <...> { #fr2 } + +## Non-functional requirements + +### NFR1 — { #nfr1 } + +#### Behavioral scenarios + +```gherkin +Scenario: + Given + When + Then +``` + +## Acceptance criteria + + + +```gherkin +# AC1 — Verifies: FR1, NFR1 +Scenario: + Given + When + Then +``` + +## Constraints and assumptions + +- **Constraint:** +- **Assumption:** + +## Dependencies + +- + +## Open questions + +- [NEEDS CLARIFICATION: ] + +## Where this connects + +- `design.md` — how these requirements are delivered. +```` + +## Feature addendum + +A [feature addendum](Spec-Driven-Development.md#core-and-feature-addenda) extends the core spec. It restates nothing and starts its own numbering at `FR1`. + +````markdown +--- +title: +description: +--- + +# + + + +## Extends + +- `../spec.md` — the core requirements this feature inherits. +- + +## Scope + +**In scope** + +- <...> + +**Out of scope** + +- <...> + +## Functional requirements + +### FR1 — { #fr1 } + +#### Behavioral scenarios + +```gherkin +Scenario: <...> + Given <...> + When <...> + Then <...> +``` + +## Non-functional requirements + +### NFR1 — { #nfr1 } +```` + +## Design + +````markdown +--- +title: — Design +description: +--- + +# — Design + + + +## Specification + + + +## Approach + + + +## Alternatives considered + +| Option | Trade-offs | Verdict | +|---|---|---| +| { #nfr1 } - -### NFR2 — <...> { #nfr2 } - -## Acceptance criteria +A design MAY be a single `design.md` or a `design/` folder when it grows past one page. The name stays singular either way. -```gherkin -Scenario: - Given - When - Then -``` +## What implementation docs are -## Constraints and assumptions +An implementation doc holds the concrete details a design deliberately leaves out: exact configuration values, element and resource names, taxonomies, field-by-field mappings from a requirement to the thing that satisfies it, and the scripts that apply them. -- **Constraint:** -- **Assumption:** +Implementation docs exist so the design stays at the logical altitude. A design that has started listing settings has outgrown itself: the settings move down, and the design keeps the explanation and a link. -## Dependencies +An implementation doc is **optional**. A capability whose design carries no concrete detail does not have one. -- +## What a guide is -## Open questions +A guide is an operational how-to for a shipped capability: the step-by-step procedure a person or an agent carries out to get a task done. It is distinct from the specification (why and what) and the design (how it is built). -- [NEEDS CLARIFICATION: ] +A guide states the steps and nothing else. It links to the spec for context and to a [reference](#what-a-reference-is) for values; it never restates either. Guides live in a `guides/` folder beside the spec, one task per page, named for the task. -## Decisions +## What a reference is - -```` +A reference is a page for fast lookup of stable facts — schemas, endpoints, parameters, labels, identifiers, supported values. It is uniform and neutral: tables over prose, no narrative, no steps, no rationale. -### Design template +Each fact lives on exactly **one** reference page, and everything else links to it. A reference is not owned by a single design, because more than one design may depend on the same fact. -````markdown -# — Design +## What a decision record is - +A decision record captures one choice that constrains everything built after it: the context that forced a choice, the options weighed, the option taken, and the consequences accepted. It is written once, at the moment of the decision. -## Specification +A decision record is required when a choice is a **one-way door** — when reversing it later would cost materially more than making it differently now. Public contracts, data formats that outlive a release, identity and permission models, and anything a consumer will depend on all qualify. A choice that can be changed in an afternoon does not; it belongs in the design. - +A decision record is **immutable**. It is not edited when the decision is revisited — a later decision is a new record that supersedes it, and the superseded record says so. This is what makes the reasoning of a past choice recoverable instead of overwritten. Decision records live in a `decisions/` folder beside the spec of the scope they constrain, one decision per page, named for the choice made and following [Decision Before Change](Principles/AI-First-Development.md#decision-before-change). -## Approach +The design states what is built; the decision record states what was rejected and why. A design that has started arguing with alternatives has a decision record hiding inside it. - +## What research is -## Alternatives considered +Research captures the exploration and findings that informed a capability's decisions — what was investigated, what was tried, and what was found. -| Option | Trade-offs | Verdict | -|---|---|---| -|