refactor: hard-fork GSD -> MSD (Make Software Done)

Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
This commit is contained in:
Jakub Zych
2026-10-06 01:47:40 +02:00
parent fe069b2a56
commit a9a7a328e6
2763 changed files with 78465 additions and 78434 deletions

View File

@@ -1,6 +1,6 @@
# How overlay capabilities compose
> **Explanation** — This document describes *why* GSD composes first-party and
> **Explanation** — This document describes *why* MSD composes first-party and
> third-party capabilities the way it does, and *what the precedence and conflict
> rules are*. It is not a step-by-step guide; for the consumer lifecycle see
> [Install your first capability](../tutorials/install-your-first-capability.md),
@@ -15,9 +15,9 @@
## The central idea: the registry is a module, not a data file
GSD's capabilities — first-party and third-party alike — are described by a single
MSD's capabilities — first-party and third-party alike — are described by a single
**capability registry**: a composed object that every consumer (the loop resolver,
the config loader, the surface command, `gsd capability list`) reads to learn which
the config loader, the surface command, `msd capability list`) reads to learn which
skills, agents, config keys, and loop hooks exist.
The first-party registry is *frozen and generated*: it is built at release time from
@@ -41,7 +41,7 @@ second-class citizen: once it composes cleanly, it participates in the loop exac
as a shipped one does.
The interesting question is everything that can go wrong while composing two sources
that were authored independently — and what GSD does about each case. That is the rest
that were authored independently — and what MSD does about each case. That is the rest
of this document.
---
@@ -58,28 +58,28 @@ before surface and config**, not after them.
The capability now exists *on disk*.
2. **Load / compose (with the project-scope consent gate)** is what `loadRegistry`
does. As it composes each overlay it applies the composition gates — id/skill/agent/
config/family collisions, the `engines.gsd` re-check, and, for a *project-scoped*
config/family collisions, the `engines.msd` re-check, and, for a *project-scoped*
overlay, **the project-scope consent gate**. That gate runs *inside* `loadRegistry`,
before any of the overlay's fragments are even materialised: a project overlay is
inert (discovered-but-inactive) until a matching record exists in your user-owned
consent store. This is the security gate described in
[the trust model](capability-trust-model.md#the-project-scope-trust-boundary). A
capability that fails any composition gate — consent included — never enters the
registry the rest of GSD reads, so it cannot reach the later stages at all.
registry the rest of MSD reads, so it cannot reach the later stages at all.
3. **Surface** decides which of the *composed* registry's skills are projected into the
host runtime. This is the install-profile and `/gsd-surface` layer — a capability's
host runtime. This is the install-profile and `/msd-surface` layer — a capability's
skills can be on the surface or held back without uninstalling it. It only ever sees
capabilities that already cleared composition.
4. **Config activation** decides, per loop hook, whether it fires. A hook's `when`
key (a dotted config key) gates it: a `step` or `gate` whose key is falsy does not
run. This is the `gsd capability set <id> --gate <key>=<bool>` and `/gsd-settings`
run. This is the `msd capability set <id> --gate <key>=<bool>` and `/msd-settings`
layer — again, only for capabilities that survived composition.
This document is about what `loadRegistry` does at the moment of composition — stage 2,
which sits between install and the later surface/config stages and contains the consent
gate. A capability that is installed but skipped at composition (including for missing
consent) never reaches the surface or config stages, because it is not in the registry
the rest of GSD reads.
the rest of MSD reads.
---
@@ -87,20 +87,20 @@ the rest of GSD reads.
`loadRegistry` scans two install roots, in this order:
- **Global** — `$GSD_HOME/.gsd/capabilities/<id>/` (where `GSD_HOME` defaults to your
- **Global** — `$MSD_HOME/.msd/capabilities/<id>/` (where `MSD_HOME` defaults to your
home directory). This is under your own control and is trusted without a per-project
record.
- **Project** — `<projectRoot>/.gsd/capabilities/<id>/`. This lives inside a repository
- **Project** — `<projectRoot>/.msd/capabilities/<id>/`. This lives inside a repository
and is therefore only as trustworthy as the repository; it is gated by the consent
store.
The roots are deduplicated by their *canonical* (symlink-resolved) physical path, so a
single directory is never scanned twice — and, crucially, so a symlinked `GSD_HOME`
single directory is never scanned twice — and, crucially, so a symlinked `MSD_HOME`
that physically *is* the project root cannot smuggle an in-repo bundle into the trusted
global slot. When the global and project roots resolve to the same physical directory
(or distinctness cannot be proven), the surviving scope escalates to the more
restrictive `project` — consent-required. This is a deliberately conservative choice:
when GSD cannot prove a global root is distinct from your project tree, it treats it as
when MSD cannot prove a global root is distinct from your project tree, it treats it as
project-scoped rather than risk granting trusted-global activation to repo-plantable
content.
@@ -129,9 +129,9 @@ overlay is rejected if it collides on any of:
Two further rules protect the first-party namespace directly:
- **Reserved prefixes.** The `gsd-`, `gsd-core-`, and `anthropic-` id prefixes are
- **Reserved prefixes.** The `msd-`, `msd-core-`, and `anthropic-` id prefixes are
reserved. An overlay whose id begins with one is rejected outright — a third party
cannot publish `gsd-security` and borrow the implicit trust of the GSD namespace.
cannot publish `msd-security` and borrow the implicit trust of the MSD namespace.
- **Cross-capability invariants.** Each candidate overlay is added to the merged
capability map and the *full* cross-capability validation suite (contract roles,
`consumes`-satisfiability, owner uniqueness, config-key exclusivity, `requires`
@@ -142,12 +142,12 @@ Two further rules protect the first-party namespace directly:
The asymmetry is intentional and follows directly from the trust model's central
thesis — *artifact parity is not trust parity*. A third-party capability is allowed to
ship the same kinds of artifacts as GSD Core, but first-party capabilities carry an
authority third-party ones do not: their provenance is the GSD release process itself.
ship the same kinds of artifacts as MSD Core, but first-party capabilities carry an
authority third-party ones do not: their provenance is the MSD release process itself.
If a collision could let an overlay shadow a first-party skill, agent, or command, then
installing a capability could silently *replace* a shipped behaviour — the install would
be the attack. By making first-party unconditionally win every collision, GSD
guarantees that no installed capability can ever redefine what GSD Core does. An overlay
be the attack. By making first-party unconditionally win every collision, MSD
guarantees that no installed capability can ever redefine what MSD Core does. An overlay
can only *add*; it can never *override*.
---
@@ -167,8 +167,8 @@ for any of these reasons:
- it fails structural or cross-capability validation;
- it collides with first-party or an already-accepted overlay (the precedence rule
above);
- its `engines.gsd` range does not satisfy the running GSD version (the load-time
re-gate, which mirrors the install-time gate so an upgrade of GSD itself can retire an
- its `engines.msd` range does not satisfy the running MSD version (the load-time
re-gate, which mirrors the install-time gate so an upgrade of MSD itself can retire an
incompatible overlay);
- it carries an in-flight `_pending` install/upgrade marker (deferred until
reconciliation completes);
@@ -204,14 +204,14 @@ to the operator at all.
So, per the maintainer decision on [#2009](https://github.com/open-gsd/gsd-core/issues/2009),
composition treats gates like steps and contributions for control flow — the loop always
proceeds — but never silently. When a capability that declares a gate is skipped, GSD
proceeds — but never silently. When a capability that declares a gate is skipped, MSD
records its gate points in `_overlay.incompatibleGateCapIds` and `_overlay.blockedGates`,
and the loop resolver **injects no gate** at each of those extension points. The loop
**fails open**, but loudly: it emits a warning through two channels — stderr (the channel
host workflows/agents see when they run `gsd_run loop render-hooks <point>`) and the
host workflows/agents see when they run `msd_run loop render-hooks <point>`) and the
`loop render-hooks` JSON envelope's top-level `warnings` array. The warning names the
skipped capability, why it could not be loaded (for example, an incompatible
`engines.gsd` range), and the exact remediation — `gsd capability remove <id>` — so the
`engines.msd` range), and the exact remediation — `msd capability remove <id>` — so the
operator sees the missing control on every pass through the loop until they act on it,
instead of the loop halting project-wide over a single incompatible overlay.
@@ -229,7 +229,7 @@ canonical builder (`buildRegistry`) materialises the merged registry — a topol
cycle that only appears across the combined set, a config-slice shape problem, a format
mismatch. An unguarded failure there would crash every consumer of the registry.
The fallback is uncompromising: if the whole-set build fails, GSD **discards every
The fallback is uncompromising: if the whole-set build fails, MSD **discards every
overlay** and returns the frozen first-party registry, plus a warning recording why. The
loop keeps running with exactly the shipped capabilities and none of the overlays. Two
details make this safe rather than merely convenient:
@@ -241,7 +241,7 @@ details make this safe rather than merely convenient:
still **surfaces a loud warning** (stderr + envelope `warnings`) at its gate points
rather than vanishing silently (#2009).
The principle is the same at every layer: when GSD cannot compose an overlay, it removes
The principle is the same at every layer: when MSD cannot compose an overlay, it removes
the overlay's *additions* but never silences a *control* — a missing gate always
surfaces, even though, per #2009, it no longer blocks the loop.
@@ -251,14 +251,14 @@ surfaces, even though, per #2009, it no longer blocks the loop.
A subtle but important design choice: the merged registry is materialised by the **same**
`buildRegistry` function that produces the first-party registry, run over a map of
first-party capabilities *plus* the accepted overlays. GSD does not have one code path
first-party capabilities *plus* the accepted overlays. MSD does not have one code path
that builds the first-party views and a separate path that bolts overlay views on.
The reason is drift. Every derived view — `bySkill`, `byLoopPoint`, the config schema,
the cluster map, profile membership — is a projection of the capability set. If overlays
were projected by a different builder, those projections could diverge from the
first-party ones in subtle ways, and an overlay capability might behave *almost* like a
first-party one but not quite. By forcing both through the single canonical builder, GSD
first-party one but not quite. By forcing both through the single canonical builder, MSD
guarantees that an accepted overlay is indistinguishable from a first-party capability in
every derived view — which is exactly the artifact-parity promise the platform makes.
@@ -276,7 +276,7 @@ The overlay model rests on a few rules applied consistently:
per [#2009](https://github.com/open-gsd/gsd-core/issues/2009), **fails open** for gates
too (a missing control, no gate injected) — but loudly, via a warning (stderr + the
envelope's `warnings` array) that names the load failure and its
`gsd capability remove <id>` remediation.
`msd capability remove <id>` remediation.
- A whole-set compose failure **falls back to first-party**, clearing command roots and
still surfacing dropped gates as loud warnings.
- One canonical builder materialises both first-party and overlay views, so an accepted
@@ -295,5 +295,5 @@ single incompatible overlay.
- [The capability trust model](capability-trust-model.md) — the security side of the same boundary
- [Capability Overlay (Configuration)](../CONFIGURATION.md#capability-overlay-installed-third-party-capabilities) — the operator-facing view of the same rules
- [Capability manifest reference](../reference/capability-manifest.md) — the field-level conformance invariants
- [`gsd capability` command reference](../reference/gsd-capability-command.md)
- [`msd capability` command reference](../reference/msd-capability-command.md)
- [Install your first capability](../tutorials/install-your-first-capability.md)

View File

@@ -1,6 +1,6 @@
# The capability trust model
> **Explanation** — This document describes *why* GSD draws its trust
> **Explanation** — This document describes *why* MSD draws its trust
> boundaries where it does, and *what the trade-offs are*. It is not a
> step-by-step guide to installing capabilities; for that, see the how-to
> guides for [importing a capability](../how-to/import-a-capability-from-a-url.md) and
@@ -13,9 +13,9 @@
## The central thesis: artifact parity is not trust parity
GSD 1.6.0 opens the capability platform to third-party authors with **full
MSD 1.6.0 opens the capability platform to third-party authors with **full
artifact parity**: a third-party capability may ship the same executable
surfaces that GSD Core ships — hooks, MCP servers, command modules, and
surfaces that MSD Core ships — hooks, MCP servers, command modules, and
reviewer lanes. This is a deliberate product choice, and it carries real
security weight.
@@ -31,9 +31,9 @@ line between two things that are often conflated:
- **Trust posture** — the evidence and consent required before that capability
*executes* on your machine.
GSD grants full artifact parity. It does not grant symmetric trust. First-party
MSD grants full artifact parity. It does not grant symmetric trust. First-party
capabilities are implicitly trusted because they *are* the shipped package —
their provenance is the GSD Core release process itself. Third-party
their provenance is the MSD Core release process itself. Third-party
capabilities require explicit, informed, revocable consent plus SHA-pinned
integrity before any executable surface is activated. These two things are
structurally separate, and keeping them separate is what makes full parity
@@ -43,7 +43,7 @@ defensible.
## What the ecosystem learnt the hard way
GSD's trust model is not designed in isolation. It is informed by failures in
MSD's trust model is not designed in isolation. It is informed by failures in
four ecosystems that tackled the same problem — and each one paid tuition.
### VS Code: auto-update + stolen publisher credentials
@@ -57,13 +57,13 @@ a liability when credentials can be compromised, because the user's last
explicit act of trust was for *version N* — not for whatever version N+1
contains.
GSD's response: auto-update is **off by default** for third-party capabilities.
MSD's response: auto-update is **off by default** for third-party capabilities.
When it is enabled, a change to the *executable set* (the set of hooks, MCP
servers, command modules, or reviewer lanes the capability declares) triggers a
re-consent prompt before the update applies. Updating a non-executable
capability (documentation, agents, skills) does not require re-consent.
VS Code also has no signature check on VSIX packages. GSD requires an
VS Code also has no signature check on VSIX packages. MSD requires an
`integrity` SHA-512 pin in the ledger, verified before extraction.
### npm: the supply-chain attack surface
@@ -74,7 +74,7 @@ attackers have exploited in the s1ngularity attack class (a malicious package
is published under a name a legitimate package depends on). npm's own
recommendation for sensitive environments is `--ignore-scripts`.
GSD takes a stronger position: **install never executes capability code**,
MSD takes a stronger position: **install never executes capability code**,
full stop. Installation is a copy-only staging operation. There is no
`postinstall`-equivalent. A capability's hooks, MCP server, command
modules, and reviewer lanes are not invoked during install; they are first
@@ -85,13 +85,13 @@ has a window between install and first use to verify what they consented to.
### The reviewer lane: the one surface that *receives* data
Three of the four disclosure classes are about code the capability gets to
**run**. A reviewer lane — one external CLI or model endpoint that `/gsd-review`
**run**. A reviewer lane — one external CLI or model endpoint that `/msd-review`
hands a plan to — is different in kind, and the difference is the reason it is
disclosed at all.
A lane is piped the plan text, the requirements, the research findings, and the
`CONTEXT.md` decisions, and its output is read back into `REVIEWS.md`. That is an
**egress channel for the most sensitive artifacts GSD produces**. Making lanes
**egress channel for the most sensitive artifacts MSD produces**. Making lanes
pluggable without a disclosure class would have opened a data-exfiltration path
behind a manifest field, which is why the trust work gates the feature rather
than following it.
@@ -130,10 +130,10 @@ destination is detectable rather than silent.
SLSA provenance (the `provenance` field in `capability.json`) provides a
machine-checkable link from a capability bundle back to a specific commit in a
specific source repository. GSD emits provenance for first-party capabilities
specific source repository. MSD emits provenance for first-party capabilities
in CI and recommends it for curated capabilities; whether to require it for
community-listed third-party capabilities is an open question tied to whether
GSD operates a central registry (see the PRD).
MSD operates a central registry (see the PRD).
### Obsidian: no sandbox, stated honestly
@@ -144,17 +144,17 @@ on by default — no community plugins run until the user deliberately disables
restricted mode — plus a human-curated plugin directory that requires a
maintainer review PR for each new plugin.
GSD borrows two things from Obsidian. First, the honesty: **there is no
MSD borrows two things from Obsidian. First, the honesty: **there is no
sandbox**, and this document says so directly rather than implying one. Second,
the principle that explicit opt-in per capability is better than a blanket "all
community plugins are safe" message. GSD does not use restricted mode, but its
community plugins are safe" message. MSD does not use restricted mode, but its
consent gate at install serves the same function: executable surfaces are
disclosed and consented to before they activate, not discovered after the fact.
GSD does not borrow Obsidian's centralised review model. Requiring a
MSD does not borrow Obsidian's centralised review model. Requiring a
maintainer-review PR for every third-party capability is the bottleneck that
makes the Obsidian system painful for authors and creates a PR-queue burden
for maintainers. GSD ships decentralised URL import precisely to avoid that.
for maintainers. MSD ships decentralised URL import precisely to avoid that.
### Claude Code: trust prompt + marketplace
@@ -163,9 +163,9 @@ elevated trust, lists the permissions the extension requests, and maintains a
`strictKnownMarketplaces` allowlist for managed environments where only
reviewed sources are permitted. Claude Code's SHA-pinning mechanic (pinning to
a specific version hash rather than floating on `latest`) is the direct model
for GSD's integrity field.
for MSD's integrity field.
GSD mirrors the allowlist mechanic as `strictKnownRegistries`, and mirrors the
MSD mirrors the allowlist mechanic as `strictKnownRegistries`, and mirrors the
SHA-pin as the `integrity` field in `capability.json` and the capability
ledger.
@@ -175,7 +175,7 @@ ledger.
### Install never runs code
The most powerful thing GSD can say to a user about a third-party capability
The most powerful thing MSD can say to a user about a third-party capability
is: "downloading and staging this capability will not execute any of its code."
That guarantee makes the consent step meaningful. If install could run code, a
malicious capability could bypass consent entirely — the install step would be
@@ -194,7 +194,7 @@ the point at which a hook would fire for the first time is not a prompt
opportunity; it is already inside a running tool invocation. This means the
consent window is install, not first use.
GSD presents a pre-install summary that names every executable surface the
MSD presents a pre-install summary that names every executable surface the
capability declares (hooks, MCP servers, command modules), their kinds (`step`,
`contribution`, `gate`), and the loop extension points they register into. For
each MCP server the summary also shows the `env` it would be spawned with (each
@@ -285,7 +285,7 @@ Two things worth stating so you do not infer them:
### Integrity pinning
An `integrity` field in `capability.json` carries a `sha512-<base64>` digest
of the capability bundle. When present, GSD verifies this digest before
of the capability bundle. When present, MSD verifies this digest before
extracting any files. A mismatch aborts the install.
When NO pin is supplied, the consent prompt says so plainly: a
@@ -360,11 +360,11 @@ code it does not own — whether by accident or by design.
### Reserved namespace
The `gsd-`, `gsd-core-`, and `anthropic-` id prefixes are reserved for
The `msd-`, `msd-core-`, and `anthropic-` id prefixes are reserved for
first-party use. A third-party capability that claims one of these prefixes is
rejected at the conformance gate. This prevents impersonation: a malicious
actor cannot publish a capability called `gsd-security` and exploit a user's
implicit trust in the GSD namespace.
actor cannot publish a capability called `msd-security` and exploit a user's
implicit trust in the MSD namespace.
### `capabilities.strict_known_registries` for managed environments
@@ -389,7 +389,7 @@ with the consent prompt as the default barrier and lockdown one config key away.
### Command dispatch: where third-party code runs (1.6.0)
A capability may declare a **command family** (`commands: [{ family, module,
router }]`); `gsd-tools <family>` dispatches it by `require()`-ing the router.
router }]`); `msd-tools <family>` dispatches it by `require()`-ing the router.
This is the one place a third-party capability's own code executes, so it is
gated twice. **Consent:** a third-party family is dispatchable only if the
capability is *active* under the activation gate below — for a project-scoped
@@ -403,20 +403,20 @@ command can never be shadowed by a third-party one.
#### The project-scope trust boundary
Capabilities install **globally** (`$GSD_HOME/.gsd/capabilities/`) or
**project-scoped** (`<projectRoot>/.gsd/capabilities/`). The authoritative
Capabilities install **globally** (`$MSD_HOME/.msd/capabilities/`) or
**project-scoped** (`<projectRoot>/.msd/capabilities/`). The authoritative
consent signal is **not** the in-repo ledger but a **user-owned consent store**
that lives **outside any repository**, at
`${GSD_HOME||homedir()}/.gsd/consent.json`. Each project-scope consent record is
`${MSD_HOME||homedir()}/.msd/consent.json`. Each project-scope consent record is
keyed by `(realpath(projectRoot), capability id)` and binds the bundle's
`integrity` and its disclosure signature; GSD writes one only when *you* install
`integrity` and its disclosure signature; MSD writes one only when *you* install
or upgrade that project-scoped capability through the lifecycle on this machine,
and removes it when you uninstall.
Before activating a project-scoped overlay — for **both** its declarative loop
surfaces (steps, gates, contributions, federated config) **and** its command
dispatch — the loader requires a matching record in this store. With no match the
capability is *discovered but inactive*: it shows up in `gsd capability list`
capability is *discovered but inactive*: it shows up in `msd capability list`
with `status: inactive` and a reason, but contributes nothing and runs nothing.
This closes the previous bypass: a repo you check out could ship a capability
@@ -426,14 +426,14 @@ you consent on this machine — and because the consent binds the integrity and
the disclosure signature, tampering with the bundle (including changing an MCP
server's `env` or `cwd`) deactivates it until you re-consent. A **global**
install (under your own home) is trusted without a per-project record, as before.
You can audit and revoke project consents with `gsd capability trust list` and
`gsd capability trust revoke <id>`.
You can audit and revoke project consents with `msd capability trust list` and
`msd capability trust revoke <id>`.
---
## The honest limitation: there is no sandbox
GSD does not sandbox third-party capability code. The honest reason: Node-level
MSD does not sandbox third-party capability code. The honest reason: Node-level
sandboxing that meaningfully restricts a `require()`'d module — limiting
filesystem access, network access, subprocess spawning — would require either
a separate process with IPC overhead or a VM context that strips the Node
@@ -442,7 +442,7 @@ network for MCP, subprocess for hook shell commands). Full artifact parity and
meaningful sandboxing are in tension. The maintainer chose full parity.
What this means in practice: a third-party capability, once consented to and
installed, runs with the same permissions GSD Core itself runs with. It is not
installed, runs with the same permissions MSD Core itself runs with. It is not
isolated. A capability that wants to exfiltrate data, or modify files outside
its declared scope, can — exactly as a malicious npm package can.
@@ -452,7 +452,7 @@ The barrier is not a technical wall. It is:
capability declares before they ran.
2. **Integrity** — the bundle you consented to is the bundle that ran (SHA
verified).
3. **Reversibility** — `gsd capability remove <id>` removes exactly what the
3. **Reversibility** — `msd capability remove <id>` removes exactly what the
ledger recorded, including entries in shared config files, leaving no
orphaned state.
@@ -475,24 +475,24 @@ instructions from the agent's context. What there is no sandbox for is not only
The safer alternative considered in ADR-1244 was declarative-only third-party
capabilities: skills, agents, and workflow files, but no hooks, MCP servers, or
command modules. A third-party author could extend *what GSD describes* but not
command modules. A third-party author could extend *what MSD describes* but not
*what it executes*.
The maintainer rejected this. A deploy gate capability, a house-style
verification step, a domain-specific planning contribution — all of these
require hook registration to have any effect on the loop. Declarative-only
third-party capabilities would be second-class citizens, unable to participate
in the parts of GSD where participation matters most. Full parity was the
in the parts of MSD where participation matters most. Full parity was the
explicit scope.
The cost of that choice is a permanently elevated security responsibility: URL
import with executable surfaces is the highest-maintenance, highest-risk part
of GSD. The trust model is a forever commitment, not a one-time effort.
of MSD. The trust model is a forever commitment, not a one-time effort.
### Centralised-registry-only distribution
The alternative to decentralised URL/git import is requiring all third-party
capabilities to go through a GSD-operated curated registry — one PR per
capabilities to go through a MSD-operated curated registry — one PR per
capability, reviewed by the maintainer before listing.
This would meaningfully reduce supply-chain risk (a human reviews every listed
@@ -503,7 +503,7 @@ capabilities, and replicates exactly the bottleneck that makes Obsidian's
plugin system painful.
The compromise: URL/git/npm/tarball import ships in 1.6.0 without a curated
registry. Whether GSD later operates or advertises a community registry is an
registry. Whether MSD later operates or advertises a community registry is an
open question (PRD-1244 §8). If it does, the intent is to separate "official"
(curated) from "community" (consented-but-not-reviewed) tiers, mirroring the
split Claude Code uses for its marketplace.
@@ -514,10 +514,10 @@ split Claude Code uses for its marketplace.
The capability trust model rests on a single conceptual move: separating
artifact parity from trust posture. Because those two things are kept separate,
GSD can offer authors the full power of the platform while making users'
MSD can offer authors the full power of the platform while making users'
security obligations clear and auditable. You consent to executable surfaces
before they run, you can verify the bundle's integrity, and you can remove a
capability completely. GSD does not pretend this is the same as not running
capability completely. MSD does not pretend this is the same as not running
the code at all.
---

View File

@@ -6,16 +6,16 @@
## The problem
GSD's `execute-phase` is wave-based: plans carry a wave number, waves run
MSD's `execute-phase` is wave-based: plans carry a wave number, waves run
sequentially, and plans *within* a wave run in parallel when their
`files_modified` sets don't overlap. On most runtimes GSD realizes that by
fanning out one backgrounded `gsd-executor` agent (in a worktree) per plan.
`files_modified` sets don't overlap. On most runtimes MSD realizes that by
fanning out one backgrounded `msd-executor` agent (in a worktree) per plan.
On **Claude Code** that fan-out degrades. Backgrounded agents on Claude Code have
no `Agent`/`Task` tool, so they cannot nest subagents ([#853]). The autonomous
loop therefore falls back to **inline sequential execution** — and with it
silently drops wave parallelism, the plan-checker, and the verifier — on the one
runtime most GSD users run.
runtime most MSD users run.
Claude Code ships an orchestration primitive that sidesteps exactly this: the
**Workflow tool** (the engine behind `/effort ultracode`, Agent SDK ≥ v0.3.149).
@@ -30,7 +30,7 @@ invoking the tool.
`claude-orchestration` is a **default-off, BETA, claude-only** capability that
adopts the Workflow tool as an optional, runtime-gated parallel-execution
backend, and folds the existing `gsd-ultraplan-phase` plan-offload under the same
backend, and folds the existing `msd-ultraplan-phase` plan-offload under the same
gate. It is blocked-on-nothing now that the ADR-857 capability system is released.
- **`role: feature`**, `runtimeCompat.supported: ["claude"]`, `tier: full`.
@@ -70,17 +70,17 @@ Workflow backend activates only when *every* gate passes; any miss degrades to
`emitWorkflowScript` maps the phase's wave/plan model onto Workflow primitives:
| GSD concept | Workflow primitive |
| MSD concept | Workflow primitive |
|---|---|
| Wave | `parallel()` stage barrier |
| Plan (`use_worktree` not `false`) | `agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })` |
| Plan (`use_worktree: false`) | `agent(brief, { agentType: 'gsd-executor' })` (no isolation) |
| Plan (`use_worktree` not `false`) | `agent(brief, { agentType: 'msd-executor', isolation: 'worktree' })` |
| Plan (`use_worktree: false`) | `agent(brief, { agentType: 'msd-executor' })` (no isolation) |
| `files_modified` overlap | forces the plans into separate sequential stages |
| Wave | a `phase("Wave <id>")` group, matching a `meta.phases` entry |
| Phase run id | `summary.resumeRunId` → pass as the Workflow tool's `resumeFromRunId` **input** |
| Phase token cap | recorded in `summary.budgetTokens`; `budget` is read-only in a script |
Because the emitted script composes the **same** `gsd-executor` agent the
Because the emitted script composes the **same** `msd-executor` agent the
inline path uses, with worktree isolation applied **per plan** from the
manifest's `use_worktree` field, it produces the same `SUMMARY.md` artifacts
and commits — the only difference is the execution vehicle. `use_worktree`
@@ -102,7 +102,7 @@ The first slice ships **detection + emission + declarative ultraplan ownership**
The emitter is exercised at the contract level (structure, overlap splitting,
resume, budget, anti-injection). End-to-end execution through the Workflow tool
is verifiable only inside Claude Code with the tool present. Full install-profile
migration of the `gsd-ultraplan-phase` skill into the capability's `skills[]`
migration of the `msd-ultraplan-phase` skill into the capability's `skills[]`
array is a follow-up (it touches the cluster/profile machinery); for v1 the
manifest *declares* ultraplan ownership at `plan:post` and the existing skill's
own runtime gate continues to no-op on non-Claude runtimes.

View File

@@ -1,6 +1,6 @@
# Context engineering
> Why GSD Core exists, and the problem it is designed to solve.
> Why MSD Core exists, and the problem it is designed to solve.
---
@@ -23,15 +23,15 @@ The naive response is to `/clear` and start over. But that loses continuity. You
---
## GSD Core's answer: fresh-context subagents
## MSD Core's answer: fresh-context subagents
GSD Core's central insight is that *most* of the work in a coding session does not need to happen in the main context at all. Research, planning, code writing, and verification are each discrete, bounded tasks. Each can be handed to a specialised subagent that starts with a clean, carefully scoped context window — and reports its result back to a thin orchestrator that stays lean.
MSD Core's central insight is that *most* of the work in a coding session does not need to happen in the main context at all. Research, planning, code writing, and verification are each discrete, bounded tasks. Each can be handed to a specialised subagent that starts with a clean, carefully scoped context window — and reports its result back to a thin orchestrator that stays lean.
This is not a workaround for context rot. It is a structural solution.
The orchestrator — your main session — never touches source files. It spawns agents, collects their results, updates shared state, and routes to the next step. Because it does very little itself, its context window grows slowly and predictably. The heavy work happens in agents that each start fresh, receive exactly the context they need for their task, and terminate when done.
Consider what this means in practice. When you run `/gsd-plan-phase`, the orchestrator:
Consider what this means in practice. When you run `/msd-plan-phase`, the orchestrator:
1. Loads a compact JSON context payload (project summary, phase goal, relevant config).
2. Spawns a researcher agent with a 200k-token clean window.
@@ -44,11 +44,11 @@ Each agent operates at full capacity, unencumbered by the accumulated history of
## Spec-driven development and meta-prompting
Context engineering alone is not enough. If an agent starts fresh but receives vague instructions, it will produce vague output. GSD Core pairs fresh-context subagents with two complementary disciplines:
Context engineering alone is not enough. If an agent starts fresh but receives vague instructions, it will produce vague output. MSD Core pairs fresh-context subagents with two complementary disciplines:
**Spec-driven development** means that every phase produces structured artefacts before execution begins. A `CONTEXT.md` captures implementation decisions from the Discuss step. A `RESEARCH.md` records what the researcher found. A `PLAN.md` breaks work into discrete, dependency-ordered tasks with explicit acceptance criteria. By the time an executor agent touches a file, it has a precise specification to work from — not a re-interpretation of a long conversation.
**Meta-prompting** means the agent definitions themselves are carefully engineered prompts, not ad-hoc instructions. The files in `gsd-core/workflows/` and `agents/` encode hard-won knowledge about how to scope tasks, what to verify, and when to escalate to a human checkpoint. The user does not need to re-explain this knowledge in every session; it is baked into the system's own prompts.
**Meta-prompting** means the agent definitions themselves are carefully engineered prompts, not ad-hoc instructions. The files in `msd-core/workflows/` and `agents/` encode hard-won knowledge about how to scope tasks, what to verify, and when to escalate to a human checkpoint. The user does not need to re-explain this knowledge in every session; it is baked into the system's own prompts.
The combination is deliberate. Fresh context ensures each agent reasons clearly. Spec-driven artefacts ensure each agent reasons about the *right* thing. Meta-prompting ensures each agent knows *how* to reason about it well.
@@ -56,7 +56,7 @@ The combination is deliberate. Fresh context ensures each agent reasons clearly.
## The role of `.planning/`
Context engineering requires that knowledge survive context resets. GSD Core uses the file system for this. Every meaningful output is written to `.planning/` as human-readable Markdown or JSON. This means:
Context engineering requires that knowledge survive context resets. MSD Core uses the file system for this. Every meaningful output is written to `.planning/` as human-readable Markdown or JSON. This means:
- Restarting your session (or the model crashing) does not lose work.
- Any subsequent agent can read prior artefacts directly, without depending on a shared conversation history.
@@ -70,16 +70,16 @@ Context engineering requires that knowledge survive context resets. GSD Core use
The fresh-context subagent model protects each spawned agent from accumulating noise. But there is a subtler problem: the *orchestrating session itself* fills up over time. A long-running orchestration silently consumes its own context window — loading payloads, reading status output, routing between phases. Without any signal about how much headroom remains, the session can quietly degrade or, worse, trigger an automatic compaction that silently discards planning state the orchestrator was relying on.
Since GSD 1.4.0, this is addressed by registering runtime lifecycle hooks. Rather than leaving headroom invisible, these hooks give GSD a per-turn signal — a moment to inspect how much context has been consumed and emit a warning before the window is exhausted. The hooks run inside the runtime itself, so the measurement is as close to authoritative as possible: GSD is not guessing from the outside.
Since MSD 1.4.0, this is addressed by registering runtime lifecycle hooks. Rather than leaving headroom invisible, these hooks give MSD a per-turn signal — a moment to inspect how much context has been consumed and emit a warning before the window is exhausted. The hooks run inside the runtime itself, so the measurement is as close to authoritative as possible: MSD is not guessing from the outside.
### One idea, many runtime vocabularies
Each AI runtime exposes lifecycle events in its own vocabulary, but the purpose is the same across all of them: fire at boundaries that correspond to context pressure or turn transitions, so GSD can observe and react.
Each AI runtime exposes lifecycle events in its own vocabulary, but the purpose is the same across all of them: fire at boundaries that correspond to context pressure or turn transitions, so MSD can observe and react.
- **Claude Code** fires `PreCompact` when a compaction is about to occur, `Stop` when a session turn ends, and `SubagentStop` when a spawned subagent completes. Together these bracket the moments when context has grown or a context-consuming task has just finished.
- **Qwen** exposes `SubagentStop`, `Stop`, and `PreCompact`, mirroring Claude Code's shape in its own event system.
Think of these as the same concept — "notify GSD at context boundaries" — expressed in each runtime's native event vocabulary. This is the multi-runtime philosophy applied at the observability layer: GSD registers the semantically equivalent hook wherever each runtime exposes it, rather than demanding every runtime adopt a single event schema.
Think of these as the same concept — "notify MSD at context boundaries" — expressed in each runtime's native event vocabulary. This is the multi-runtime philosophy applied at the observability layer: MSD registers the semantically equivalent hook wherever each runtime exposes it, rather than demanding every runtime adopt a single event schema.
For the per-runtime event matrix, see [FEATURES.md](../FEATURES.md) under Multi-Runtime Support. For how to enable hooks on your specific runtime, see [Install on your runtime](../how-to/install-on-your-runtime.md).
@@ -87,13 +87,13 @@ For the per-runtime event matrix, see [FEATURES.md](../FEATURES.md) under Multi-
Claude Code exposes a `FileChanged` event in addition to session-lifecycle hooks. Claude Code's `FileChanged` hook watches for changes to `config.json` and hot-reloads the project's `.planning/config.json` into the session. The practical reason is straightforward: configuration changes should take effect without forcing the user to clear and rebuild the session.
Requiring a `/clear` to pick up a config edit would destroy the very continuity the context-engineering design is trying to protect. By watching for `FileChanged` on `config.json`, GSD can reload configuration mid-session — adjusting model profiles, context-window thresholds, or routing preferences — without the user losing their place. The working context survives; the configuration updates beneath it.
Requiring a `/clear` to pick up a config edit would destroy the very continuity the context-engineering design is trying to protect. By watching for `FileChanged` on `config.json`, MSD can reload configuration mid-session — adjusting model profiles, context-window thresholds, or routing preferences — without the user losing their place. The working context survives; the configuration updates beneath it.
### Effort signals for heavy and light skills
Beyond passive monitoring, GSD uses `effort:` frontmatter to signal the token budget appropriate for each skill. Heavy orchestrator skills (`plan-phase`, `execute-phase`, `autonomous`) declare `effort: max`; quick-status skills (`progress`, `stats`) declare `effort: low`.
Beyond passive monitoring, MSD uses `effort:` frontmatter to signal the token budget appropriate for each skill. Heavy orchestrator skills (`plan-phase`, `execute-phase`, `autonomous`) declare `effort: max`; quick-status skills (`progress`, `stats`) declare `effort: low`.
Note: an earlier version of GSD also applied `context: fork` to these three heavy skills to protect the main session's context budget. This was removed (#921) because `plan-phase`, `execute-phase`, and `autonomous` are **spawning orchestrators** — their core function is to spawn subagents (`gsd-planner`, `gsd-executor`, etc.), and a forked subagent context does not have the `Agent` tool. Context isolation for these skills comes from the subagents they spawn, not from forking the orchestrator itself.
Note: an earlier version of MSD also applied `context: fork` to these three heavy skills to protect the main session's context budget. This was removed (#921) because `plan-phase`, `execute-phase`, and `autonomous` are **spawning orchestrators** — their core function is to spawn subagents (`msd-planner`, `msd-executor`, etc.), and a forked subagent context does not have the `Agent` tool. Context isolation for these skills comes from the subagents they spawn, not from forking the orchestrator itself.
Complementing this, quick-status skills explicitly declare low effort in their definitions. This is a budget-conscious signal in the opposite direction: these skills read minimal state and return concise output, keeping their own footprint small by design.
@@ -101,9 +101,9 @@ Complementing this, quick-status skills explicitly declare low effort in their d
This machinery is worth being honest about.
**Hooks add maintenance surface.** Every runtime GSD supports must have its hooks registered, tested, and kept in sync with that runtime's event API. When a runtime changes its event names or firing semantics, GSD's hook registration needs updating. This is the cost of per-runtime observability rather than a single shared mechanism.
**Hooks add maintenance surface.** Every runtime MSD supports must have its hooks registered, tested, and kept in sync with that runtime's event API. When a runtime changes its event names or firing semantics, MSD's hook registration needs updating. This is the cost of per-runtime observability rather than a single shared mechanism.
**Headroom tracking is a heuristic.** The hooks give GSD a signal, not a guarantee. A single model call can consume tokens unpredictably depending on the response length, tool use, and caching behaviour. GSD uses headroom estimates to warn and steer, not to make hard guarantees about what will fit.
**Headroom tracking is a heuristic.** The hooks give MSD a signal, not a guarantee. A single model call can consume tokens unpredictably depending on the response length, tool use, and caching behaviour. MSD uses headroom estimates to warn and steer, not to make hard guarantees about what will fit.
**Subagents are isolated.** A spawned subagent cannot see uncommitted state in the orchestrating session. This is not a bug — it is necessary for independence — but it means anything the subagent needs must be on disk before it is spawned. This is precisely why `.planning/` exists as the shared substrate: plan files, `STATE.md`, `CONTEXT.md`, and `config.json` are all durable, file-system artefacts that any context — orchestrator or subagent — can read. The context-engineering design is self-consistent: the same principle that makes fresh-context subagents work (shared state lives in files, not in a conversation) is what makes the multi-agent architecture viable. See also [Multi-agent orchestration](multi-agent-orchestration.md) for how `.planning/` serves the same role across the orchestrator → agent boundary.
@@ -113,11 +113,11 @@ This machinery is worth being honest about.
Honesty about trade-offs matters here.
**Overhead.** The phase loop introduces real friction. Running `/gsd-discuss-phase`, `/gsd-plan-phase`, and `/gsd-execute-phase` as separate steps takes more elapsed time than typing "write this feature" into a plain session. For a small, well-understood change, that overhead is not justified.
**Overhead.** The phase loop introduces real friction. Running `/msd-discuss-phase`, `/msd-plan-phase`, and `/msd-execute-phase` as separate steps takes more elapsed time than typing "write this feature" into a plain session. For a small, well-understood change, that overhead is not justified.
**Latency.** Spawning multiple subagents with fresh context is slower than a single in-context edit. Research, planning, and execution each incur round-trip costs.
**Ceremony for simple tasks.** If you need to rename a variable, fix a typo, or add a missing import, the phase loop is overkill. GSD Core provides `/gsd-quick` and `/gsd-fast` for ad-hoc work that does not warrant a full phase. See [Handle quick and fast tasks](../how-to/handle-quick-and-fast-tasks.md).
**Ceremony for simple tasks.** If you need to rename a variable, fix a typo, or add a missing import, the phase loop is overkill. MSD Core provides `/msd-quick` and `/msd-fast` for ad-hoc work that does not warrant a full phase. See [Handle quick and fast tasks](../how-to/handle-quick-and-fast-tasks.md).
The phase loop pays for itself when the work is complex enough that context rot is a real risk — multi-file features, cross-cutting refactors, work that spans hours or sessions. For everything else, reach for the lighter primitive.

View File

@@ -1,6 +1,6 @@
# The Embeddable Orchestration System (EoS)
> **Explanation** — This document describes *why* GSD is built around one
> **Explanation** — This document describes *why* MSD is built around one
> versioned interface for embedding inside many different host applications,
> and *how* the interface points, negotiated axes, and adapter shapes fit
> together. It is not a how-to; for field-level detail see the
@@ -12,7 +12,7 @@
## The problem it solves
GSD is a filesystem-native orchestration engine, not a standalone
MSD is a filesystem-native orchestration engine, not a standalone
application. Almost all of the useful work it does — running a loop,
dispatching an agent, resolving a model, persisting state — happens *inside*
some other program: a CLI, an IDE, or an agentic desktop app. Each of those
@@ -21,16 +21,16 @@ model call gets routed, and its own storage model. There is no shared
substrate a priori.
Before 1.7.0, every host integration was wired bespoke: a runtime-specific
adapter that reached into GSD's internals however it needed to, and exposed
adapter that reached into MSD's internals however it needed to, and exposed
whatever surface that host happened to support. That does not scale. Each new
host is a fresh bespoke integration to write and maintain, drift between
hosts accumulates silently over time, and no third party can build a host
integration without reverse-engineering GSD's internals from source.
integration without reverse-engineering MSD's internals from source.
The **Embeddable Orchestration System (EoS)** is the answer: one public, versioned
contract — the ADR-1239 Host-Integration Interface — that every host
integration is expressed against, first-party and third-party alike (Phase A,
#1690). A host does not reach into GSD's internals; it declares which
#1690). A host does not reach into MSD's internals; it declares which
interface points it binds and which values it supports for each negotiated
axis, and the engine tells it, deterministically, what it gets.
@@ -38,8 +38,8 @@ axis, and the engine tells it, deterministically, what it gets.
The interface has three moving parts.
**Six interface points** are the places a host can bind to GSD: `command`
(how a user invokes a GSD command), `dispatch` (how that invocation reaches
**Six interface points** are the places a host can bind to MSD: `command`
(how a user invokes a MSD command), `dispatch` (how that invocation reaches
the orchestration loop), `model` (how model calls are routed), `hooks` (how
lifecycle events fire), `state` (how `.planning/` state is read and
written), and `artifact` (how generated files are produced). A host does not
@@ -68,7 +68,7 @@ usable, is the subject of its own document:
[Interface versioning and deprecation policy](interface-versioning-policy.md).
Underpinning all of it is the `undocumented` sentinel: the permanent,
fail-closed fallback for an axis a host says nothing about. GSD never
fail-closed fallback for an axis a host says nothing about. MSD never
*guesses* a host's capability from context — a host that omits an axis gets
the safe default for that axis, never an assumed one.
@@ -77,9 +77,9 @@ the safe default for that axis, never an assumed one.
The single most useful mental model for a given host integration is which of
two adapter shapes it uses, set by the `embeddingMode` axis.
**Imperative** hosts can run GSD's own shell preamble or programmatic
**Imperative** hosts can run MSD's own shell preamble or programmatic
dispatch directly at invocation time (`embeddingMode: imperative`). The host
hands control to GSD's runtime launcher and GSD does the rest, live, on every
hands control to MSD's runtime launcher and MSD does the rest, live, on every
invocation. Most CLI-style and IDE-embedded hosts work this way — OpenCode,
Cursor, Cline, Hermes, Qwen, Kilo, Trae, Kimi, Antigravity, and Augment are
all imperative integrations.
@@ -92,15 +92,15 @@ host.
The consequence of that split is concrete, not academic: a declarative
host's model configuration is fixed at install time, because there is no
live dispatch step at which GSD could re-resolve it. If the model
live dispatch step at which MSD could re-resolve it. If the model
configuration changes after install, a declarative host is silently stale
until the next reinstall — which is why GSD warns when a declarative host's
until the next reinstall — which is why MSD warns when a declarative host's
model configuration changes without a matching reinstall (#1688). An
imperative host has no equivalent gap, because it re-runs GSD's dispatch
imperative host has no equivalent gap, because it re-runs MSD's dispatch
logic on every invocation.
Three **host-capability profiles** — `programmatic-cli`, `declarative-cli`,
and `ide` — give the axis combinations for the reference cases GSD actually
and `ide` — give the axis combinations for the reference cases MSD actually
targets: a baseline imperative CLI, a baseline declarative CLI, and a
baseline IDE (active model mode, engine-owned hook bus, sandboxed storage).
See `PROFILE_BASELINES` in the reference for the exact axis values each
@@ -119,29 +119,29 @@ driven entirely through the adapter layer (#2103). Gemini CLI was retired in
favor of its successor, Antigravity, which shares its underlying
infrastructure (#1928).
A companion `gsd-mcp-server` (#1681) gives hosts that prefer an MCP
A companion `msd-mcp-server` (#1681) gives hosts that prefer an MCP
transport a way to reach interface points 1 and 5 (`command` and `state`)
without implementing the shell-preamble dispatch path themselves — a second
transport onto the same contract, not a second contract.
The clearest evidence that the contract is doing its job: because every host
integration is now expressed as data — a descriptor, not bespoke code —
`/gsd-surface` can reproduce a given runtime's generated agent output
`/msd-surface` can reproduce a given runtime's generated agent output
byte-for-byte from the same descriptors the installer itself consumes
(#1575). Runtime output can no longer drift from what the installer
produces, because there is only one source of truth for it.
## Where EoS ends and Capabilities begin
EoS is easy to conflate with GSD's other extensibility axis, Capabilities
EoS is easy to conflate with MSD's other extensibility axis, Capabilities
(ADR-857, ADR-1244), because both are commonly described as "third parties
extending GSD." They answer different questions, and the distinction matters
extending MSD." They answer different questions, and the distinction matters
for anyone building against either surface.
**EoS is about *where* GSD runs** — which host application embeds the
**EoS is about *where* MSD runs** — which host application embeds the
orchestration engine, and how that host's command surface, model routing,
hook bus, and storage bind to the engine. **Capabilities are about *what*
GSD does** — feature plug-ins that attach at GSD's Loop Extension Points
MSD does** — feature plug-ins that attach at MSD's Loop Extension Points
inside the loop that is already running. A host integration and a capability
are orthogonal axes: the same capability behaves identically regardless of
which host is running the loop, and the same host runs any composed set of
@@ -151,7 +151,7 @@ Each has its own non-endorsing discoverability registry (#2182): the **EoS
Registry** lists third-party host integrations, and the **Community
Capability Registry** lists third-party capabilities. Both share one entry
schema shape, one non-endorsement stance, and one submission process — see
[GSD Registries](../registries/README.md) for the full specification of
[MSD Registries](../registries/README.md) for the full specification of
both.
## Why a published interface — and what it costs
@@ -166,7 +166,7 @@ separate, disciplined document rather than an informal understanding.
The benefit is the reason 1.7.0's fourteen-runtime migration and three new
hosts were tractable at all: a new host is additive descriptor work against
a published contract, not a fork of GSD's engine internals. A third-party
a published contract, not a fork of MSD's engine internals. A third-party
host author can build and test an integration against the documented axis
vocabulary without waiting on, or coordinating with, the core team — the
same posture the EoS Registry's non-endorsement stance formalizes for
@@ -178,6 +178,6 @@ every new host.
- [Reference: the Host-Integration Interface](../reference/host-integration-interface.md)
- [Interface versioning and deprecation policy](interface-versioning-policy.md)
- [GSD Registries](../registries/README.md)
- [MSD Registries](../registries/README.md)
- [How overlay capabilities compose](capability-overlay-model.md)
- [What's new in 1.7.0](../whats-new-1.7.0.md)

View File

@@ -1,6 +1,6 @@
# Interface versioning and deprecation policy
This explains how the GSD Host-Integration Interface (ADR-1239) evolves over
This explains how the MSD Host-Integration Interface (ADR-1239) evolves over
time — what is an additive change, what is breaking, how `PROTOCOL_VERSION`
bumps, and what deprecation window host-plugin authors can rely on. The policy
exists because a published SDK is a long-term compatibility commitment (Hyrum's

View File

@@ -1,12 +1,12 @@
# The live-DOM UAT capability
Why GSD can drive a browser during execution, and why it does that from a purpose-built agent
Why MSD can drive a browser during execution, and why it does that from a purpose-built agent
instead of from the plan executor.
## The problem
A phase with a live-UI acceptance criterion could not be finished by the agent that executed
it. `gsd-executor` carries no browser tools, so it did the correct thing — returned a
it. `msd-executor` carries no browser tools, so it did the correct thing — returned a
`checkpoint:human-action` at the gate — even though the work was not actually human-only. An
orchestrator with a browser MCP server could do it unattended.
@@ -18,7 +18,7 @@ own `autonomous: false` marker could not distinguish **"a human must judge this"
## The shape that was not taken
The obvious fix is to add browser globs to `agents/gsd-executor.md`'s `tools:` line. It is one
The obvious fix is to add browser globs to `agents/msd-executor.md`'s `tools:` line. It is one
line, and an absent MCP server simply means the tool is not offered, so it is inert for anyone
without one.
@@ -37,7 +37,7 @@ is the only control that exists:
integrity + reversibility are the barrier."* There is no domain allowlist and no gate that
inspects what a browser call fetched.
The codebase already reflected this instinct. `gsd-ui-auditor` — the one subagent that produces
The codebase already reflected this instinct. `msd-ui-auditor` — the one subagent that produces
UI screenshots — captures via CLI rather than taking an MCP tool grant. The executor carrying
only `mcp__context7__*` while the researcher agents carry the full web-reaching set is a
deliberate separation, not an oversight.
@@ -53,14 +53,14 @@ One default-off capability owns everything:
capability's `activationKey`. With it off the capability resolves **inactive**, and
`resolveLoopHooks` is fail-closed on `state.active === true` — the hook does not render at
all. The step's own `when` guard is a second, independent gate.
- **The agent.** `gsd-dom-verifier` carries `mcp__chrome-devtools__*` and
- **The agent.** `msd-dom-verifier` carries `mcp__chrome-devtools__*` and
`mcp__claude-in-chrome__*` in **its own** `tools:` line, and carries no `Bash`. Browser reach
is confined to one agent that only exists to look at a DOM.
- **The step.** Registered at `execute:wave:post` as a `step` hook with `onError: skip`. A step
is additive by construction — it never halts the host. Blocking preconditions are `gate`s,
and this capability declares none.
`gsd-executor`'s tool surface is unchanged in every configuration. That is a tested invariant,
`msd-executor`'s tool surface is unchanged in every configuration. That is a tested invariant,
asserted as an absence, because that is the only way it is observable.
## Why the existing Playwright path was left alone
@@ -79,7 +79,7 @@ new families the same two-condition shape Playwright already possessed.
`chrome-devtools-mcp` holds an exclusive lock on its browser profile, so parallel execution
waves will collide. The tempting design is a lease or queue around the profile.
GSD cannot enforce one. `--isolated` is a flag on the **user's** MCP server registration; GSD
MSD cannot enforce one. `--isolated` is a flag on the **user's** MCP server registration; MSD
neither launches that server nor passes its arguments. Coordination over a resource you do not
own is theatre — it adds machinery, and the lock still happens.

View File

@@ -1,6 +1,6 @@
# Multi-agent orchestration in GSD Core
# Multi-agent orchestration in MSD Core
> **Explanation** — This document describes *why* GSD Core is designed around
> **Explanation** — This document describes *why* MSD Core is designed around
> multi-agent orchestration and *how the pieces fit together*. It is not a
> step-by-step guide. For configuration, see
> [Configure model profiles](../how-to/configure-model-profiles.md) and the
@@ -18,7 +18,7 @@ agent writes the fifth file in a complex task, it may have already forgotten
the constraint stated in the first message. This is sometimes called *context
rot*.
GSD Core's multi-agent design is a direct response to that problem. Instead of
MSD Core's multi-agent design is a direct response to that problem. Instead of
one long-running agent carrying the whole session, a thin orchestrator spawns
short-lived specialised agents, each with a **fresh 200 K-token context window**
and *only the artifacts it needs* to do its specific job. The orchestrator
@@ -29,17 +29,17 @@ collects the result, and updates shared state in `.planning/`.
## The orchestrator → agent pattern
Every workflow in `gsd-core/workflows/` follows the same shape:
Every workflow in `msd-core/workflows/` follows the same shape:
```text
Orchestrator (workflow .md file)
│
├── Load context
│ gsd-tools.cjs init <workflow> <phase>
│ msd-tools.cjs init <workflow> <phase>
│ → JSON: project info, config, state, phase details
│
├── Resolve model
│ gsd-tools.cjs resolve-model <agent-name>
│ msd-tools.cjs resolve-model <agent-name>
│ → opus | sonnet | haiku | inherit
│
├── Spawn specialised agent (Task/SubAgent call)
@@ -51,7 +51,7 @@ Orchestrator (workflow .md file)
├── Collect result
│
└── Update state
gsd-tools.cjs state update / state patch / state advance-plan
msd-tools.cjs state update / state patch / state advance-plan
```
The orchestrator is deliberately thin. It does not reason about the domain,
@@ -61,19 +61,19 @@ the orchestrator's context from accumulating domain noise.
### The agent roster
GSD Core's agents fall into functional categories that map onto the
MSD Core's agents fall into functional categories that map onto the
research → plan → execute → verify pipeline:
| Category | Agents | Typical parallelism |
|---|---|---|
| Researchers | `gsd-project-researcher`, `gsd-phase-researcher`, `gsd-ui-researcher`, `gsd-advisor-researcher` | 4 parallel (stack, features, architecture, pitfalls) |
| Synthesisers | `gsd-research-synthesizer` | Sequential, after researchers complete |
| Planners | `gsd-planner`, `gsd-roadmapper` | Sequential |
| Checkers | `gsd-plan-checker`, `gsd-integration-checker`, `gsd-ui-checker`, `gsd-nyquist-auditor` | Sequential, up to 3 revision iterations |
| Executors | `gsd-executor` | Parallel within a wave, sequential across waves |
| Verifiers | `gsd-verifier` | Sequential, after all executors complete |
| Mappers | `gsd-codebase-mapper` | 4 parallel sub-probes |
| Auditors | `gsd-ui-auditor`, `gsd-security-auditor` | Sequential |
| Researchers | `msd-project-researcher`, `msd-phase-researcher`, `msd-ui-researcher`, `msd-advisor-researcher` | 4 parallel (stack, features, architecture, pitfalls) |
| Synthesisers | `msd-research-synthesizer` | Sequential, after researchers complete |
| Planners | `msd-planner`, `msd-roadmapper` | Sequential |
| Checkers | `msd-plan-checker`, `msd-integration-checker`, `msd-ui-checker`, `msd-nyquist-auditor` | Sequential, up to 3 revision iterations |
| Executors | `msd-executor` | Parallel within a wave, sequential across waves |
| Verifiers | `msd-verifier` | Sequential, after all executors complete |
| Mappers | `msd-codebase-mapper` | 4 parallel sub-probes |
| Auditors | `msd-ui-auditor`, `msd-security-auditor` | Sequential |
Each agent definition (in `agents/*.md`) declares its allowed tool access,
purpose, and colour for terminal output. An agent that only needs to read files
@@ -87,7 +87,7 @@ For the complete agent roster, see [Inventory](../INVENTORY.md#agents).
## Wave-based parallel execution
The most visible expression of multi-agent design is how `/gsd-execute-phase`
The most visible expression of multi-agent design is how `/msd-execute-phase`
handles a set of plans that may depend on one another.
Before spawning any executor, the orchestrator performs a **wave analysis**:
@@ -173,7 +173,7 @@ than diluted by noise from other pipeline stages.
**Context hygiene across sessions.** Because all state lives in
`.planning/` as human-readable Markdown and JSON (not in any agent's context
window), GSD workflows survive context resets (`/clear`), tab switches, and
window), MSD workflows survive context resets (`/clear`), tab switches, and
multi-day breaks. The next agent always starts from persisted, verified
artifacts rather than from a reconstructed memory of a long conversation.
@@ -186,7 +186,7 @@ Multi-agent orchestration is not free.
**Coordination overhead.** Each agent spawn is a round-trip: the orchestrator
must format a prompt, hand off context, wait for the subagent to complete
(typically 1–5 minutes), and then parse the result. A single capable agent
working in one context would finish faster for simple tasks. GSD mitigates
working in one context would finish faster for simple tasks. MSD mitigates
this by making parallelism the default wherever dependencies permit — the
four researchers in a `plan-phase` run simultaneously, not sequentially.
@@ -199,7 +199,7 @@ expectations.
**Context stitching cost.** Packaging the right artifacts for each agent
requires the orchestrator to spend tokens assembling and transmitting context
payloads. This is the cost of isolation. The `gsd-tools.cjs init` handler
payloads. This is the cost of isolation. The `msd-tools.cjs init` handler
produces a JSON payload that balances completeness with token budget, applying
cache-friendly ordering so that the stable parts of the payload (project
definition, config) hit the cache on repeat invocations.

View File

@@ -1,8 +1,8 @@
# GSD Core security model
# MSD Core security model
> **Explanation** — This document describes *why* GSD Core has the security
> **Explanation** — This document describes *why* MSD Core has the security
> posture it does and *how the layers fit together*. It is not a reference for
> every hook parameter. For the `/gsd-secure-phase` command and its options,
> every hook parameter. For the `/msd-secure-phase` command and its options,
> see [Commands](../COMMANDS.md). For the implementation-level hook
> architecture, see [Architecture § Hook System](../ARCHITECTURE.md#hook-system).
> For the org-wide security baseline (scanner controls, incident checklists,
@@ -13,13 +13,13 @@
## Why AI-driven development needs a dedicated security posture
A conventional code editor does not execute arbitrary packages on your behalf.
GSD Core does. The research → plan → execute pipeline automates the full path
MSD Core does. The research → plan → execute pipeline automates the full path
from "name a package" to "run `npm install <package>`", from "write a
planning artifact" to "use that artifact as an LLM system prompt". Each
automation step removes a human from the loop — and each removal is a
potential attack surface.
GSD Core's security model is built around one organising principle:
MSD Core's security model is built around one organising principle:
**defence in depth**. No single control is assumed to be perfect. Several
overlapping layers each reduce a distinct class of risk, and together they
make the attack surface substantially harder to exploit without eliminating
@@ -45,7 +45,7 @@ The insidious quality of slopsquatting is that a hallucinated name that passes
`npm view` *looks legitimate*. The registry entry proves only that someone
registered the name — not that the package does what the AI said it does, not
that it has any legitimate users, and not that its install scripts are safe.
Without a gate, a hallucinated name would flow undetected through GSD's
Without a gate, a hallucinated name would flow undetected through MSD's
researcher → planner → executor pipeline and eventually run as
`npm install <attacker-package>` on your machine.
@@ -53,8 +53,8 @@ researcher → planner → executor pipeline and eventually run as
The gate operates across three pipeline stages:
**Research stage.** When `gsd-phase-researcher` recommends external packages,
it runs `gsd-tools query package-legitimacy check --ecosystem <npm|pypi|crates>
**Research stage.** When `msd-phase-researcher` recommends external packages,
it runs `msd-tools query package-legitimacy check --ecosystem <npm|pypi|crates>
<pkgs>` against each one. Verdicts (`OK|SUS|SLOP`) are computed from live
registry APIs against thresholds `{ minAgeDays: 30, minWeeklyDownloads: 1000,
requireRepo: true }`, plus terminal short-circuits for non-existence and
@@ -64,7 +64,7 @@ Legitimacy Audit` table in `RESEARCH.md`. Packages tagged `[SLOP]`
`RESEARCH.md` entirely** before the file is saved. They never reach the
planner.
**Planning stage.** `gsd-planner` reads the Audit table. For any package
**Planning stage.** `msd-planner` reads the Audit table. For any package
tagged `[SUS]` (suspicious: newly registered, low download count, no source
repository, or naming pattern close to a popular package) or `[ASSUMED]`
(sourced from WebSearch rather than direct registry verification), the planner
@@ -73,7 +73,7 @@ checkpoint includes a direct link to the registry page and specific things to
look for: maintainer history, issue-tracker activity, absence of suspicious
install scripts.
**Execution stage.** If an install fails, `gsd-executor` **surfaces a
**Execution stage.** If an install fails, `msd-executor` **surfaces a
checkpoint and stops**. It does not silently try an alternative package name —
which could itself be malicious. This is an explicit rule in the executor's
behaviour (RULE 3 in the executor agent definition).
@@ -122,7 +122,7 @@ downgrading everything to `[ASSUMED]`.
### The threat
GSD Core generates Markdown files that become LLM system prompts. The
MSD Core generates Markdown files that become LLM system prompts. The
research pipeline reads external web content; the planning pipeline
incorporates user-supplied text (`--text-file`, `--prd`); the execution
pipeline writes planning artifacts that are later re-read as agent context.
@@ -133,9 +133,9 @@ exfiltrate information.
### How the defences work
GSD Core addresses prompt injection at three levels.
MSD Core addresses prompt injection at three levels.
**Input validation (`security.cjs`).** The `gsd-core/bin/lib/security.cjs`
**Input validation (`security.cjs`).** The `msd-core/bin/lib/security.cjs`
module is the central security utility. It provides:
- Path containment: user-supplied file paths and directories are validated to
@@ -176,7 +176,7 @@ legitimately does not exist yet. A lexical check **cannot see a symlink**, so a
caller relying on one for a write-confinement guarantee must pair it with its
own symlink refusal. Three call sites use it, each for a stated reason.
The backup-restore gate in `gsd-core/bin/gsd-tools.cjs` rejects symlinks outright:
The backup-restore gate in `msd-core/bin/msd-tools.cjs` rejects symlinks outright:
the canonical predicate accepts a link whose target resolves inside the root, but
for a restore that is still wrong, because writing through the link overwrites
whatever it points at instead of materializing a regular file at the backed-up
@@ -225,9 +225,9 @@ file is still linted), leaving their hand-rolled comparisons permanently
un-ratcheted; the only remedy is a fix-forward migration, never an edit to a
shipped body.
**Runtime hook: `gsd-prompt-guard.js`.** This hook fires on every Write or
**Runtime hook: `msd-prompt-guard.js`.** This hook fires on every Write or
Edit call that targets `.planning/` files. It scans the content being written
for injection patterns shared with `gsd-read-injection-scanner.js` through
for injection patterns shared with `msd-read-injection-scanner.js` through
`hooks/lib/injection-patterns.js` — one module both hooks `require()`, so the
two surfaces cannot drift apart (#3504). The set is deliberately a subset of
`security.cjs`'s patterns: the hooks stay loadable standalone, without the
@@ -236,24 +236,24 @@ but does not block the write. The rationale is that a false-positive block on
a legitimate planning write would be more disruptive than a missed injection
in a secondary scan layer.
**Runtime hook: `gsd-read-injection-scanner.js`.** This hook fires on the
**Runtime hook: `msd-read-injection-scanner.js`.** This hook fires on the
output of every Read, WebFetch, and WebSearch tool call. It scans the *content
that was just read or fetched* for injected instructions in untrusted content —
catching cases where an attacker has embedded instructions in a file or remote
resource that GSD is about to incorporate into an agent's context. The 10
resource that MSD is about to incorporate into an agent's context. The 10
research and doc-ingest agents additionally carry a shared `<security_context>`
data/instruction boundary (defined in
`gsd-core/references/untrusted-input-boundary.md`): `gsd-project-researcher`,
`gsd-phase-researcher`, `gsd-ui-researcher`, `gsd-assumptions-analyzer`,
`gsd-advisor-researcher`, `gsd-doc-classifier`, `gsd-doc-synthesizer`,
`gsd-research-synthesizer`, `gsd-ai-researcher`, and `gsd-domain-researcher`.
`msd-core/references/untrusted-input-boundary.md`): `msd-project-researcher`,
`msd-phase-researcher`, `msd-ui-researcher`, `msd-assumptions-analyzer`,
`msd-advisor-researcher`, `msd-doc-classifier`, `msd-doc-synthesizer`,
`msd-research-synthesizer`, `msd-ai-researcher`, and `msd-domain-researcher`.
Any content fetched or read by those agents is treated as data, never as
instructions, regardless of what the content claims to be.
**Opt-in blocking (`security.injection_blocking`).** By default all injection
detections are advisory-only (logged, not blocked). Setting
`security.injection_blocking = true` in `.planning/config.json` (a registered
config key — `gsd config-set security.injection_blocking true`) upgrades
config key — `msd config-set security.injection_blocking true`) upgrades
HIGH-confidence detections to **blocking**. Be precise about what this does: the
scanner is a **PostToolUse** hook, so it runs *after* the Read/WebFetch/WebSearch
has already executed and the fetched content is already in the model's transcript.
@@ -269,15 +269,15 @@ not a redactor.
**CI scanner.** `prompt-injection-scan.security.test.cjs` scans all agent, workflow,
and command files for embedded injection vectors as part of the test suite.
This catches injection attempts in the GSD source itself — for example, a
This catches injection attempts in the MSD source itself — for example, a
supply-chain attack that modified a workflow file to add a role-override
instruction.
### Read Injection Scanner vs Prompt Guard
The two hooks cover complementary surfaces. `gsd-prompt-guard.js` watches
The two hooks cover complementary surfaces. `msd-prompt-guard.js` watches
*writes to planning artifacts* — it catches injection being planted.
`gsd-read-injection-scanner.js` watches *reads and remote fetches* — it catches
`msd-read-injection-scanner.js` watches *reads and remote fetches* — it catches
injection being ingested from external content (a dependency's README, a
third-party config file, a user-provided document, or any URL fetched via
WebFetch or WebSearch). The in-prompt `<security_context>` boundary in research
@@ -285,10 +285,10 @@ agents provides an additional containment layer: even if an injected string
reaches an agent, it is structurally separated from the instruction region.
Together these controls bracket the ingest → store → re-read lifecycle.
**Runtime hook: `gsd-workflow-guard.js` — advisory vs. blocking posture.**
**Runtime hook: `msd-workflow-guard.js` — advisory vs. blocking posture.**
This hook has two legs with two deliberately different failure postures. The
edit leg is **advisory**: when `hooks.workflow_guard` is enabled it warns on
edits made outside a GSD workflow, and on any internal error it fails open
edits made outside a MSD workflow, and on any internal error it fails open
(exit 0) — a broken advisory must never wedge a session's tool calls. The
Bash leg carries the hook's one **hard block**: `git add -f` / `git add
--force` on an `agent-*` or `worktree-agent-*` branch is blocked outright
@@ -308,7 +308,7 @@ one hard block this hook owns.
## Layer 3 — Repository and dependency integrity
Upstream of GSD's runtime behaviour, the `open-gsd` organisation enforces
Upstream of MSD's runtime behaviour, the `open-gsd` organisation enforces
controls at the repository and package level. These are documented in full in
[`docs/security/baseline.md`](../security/baseline.md) and are summarised
here for completeness.
@@ -317,7 +317,7 @@ here for completeness.
`package-lock.json` and verified against published checksums before install.
A `scripts/check-npm-integrity.cjs` gate detects invalid versions, missing
packages, and extraneous packages at CI time. This mitigates dependency
confusion and typosquatting attacks against GSD's own dependencies.
confusion and typosquatting attacks against MSD's own dependencies.
**Secret scanning.** Every commit and PR is scanned for hardcoded secrets.
Intentional test fixtures must be annotated with the project-standard
@@ -333,7 +333,7 @@ hide malicious content in diffs.
## Layer 4 — Subprocess execution
GSD starts external programs constantly: git, npm, reviewer CLIs declared by
MSD starts external programs constantly: git, npm, reviewer CLIs declared by
capabilities, and whatever a gate predicate names. Every one of those is a
place where an argument could become a command. One module owns the whole
question — `src/shell-command-projection.cts`, the single platform seam.
@@ -342,7 +342,7 @@ question — `src/shell-command-projection.cts`, the single platform seam.
the mechanism behind CVE-2024-27980: the shell re-parses the argument list, so
a value containing `&` or `|` stops being data and becomes a second command.
Node 26 additionally deprecates `shell: true` alongside an argument array
(DEP0190), because arguments are concatenated rather than escaped. GSD resolves
(DEP0190), because arguments are concatenated rather than escaped. MSD resolves
binaries explicitly instead.
**Explicit resolution, not shell lookup.** `resolveExecutableBinary` scans
@@ -407,21 +407,21 @@ creative injection that does not match known patterns, or an injection that
arrives through a channel the hooks do not cover. The previously uncovered
channel of content injected into a dependency's published README and read by a
subagent browsing documentation is now scanned at ingress by
`gsd-read-injection-scanner.js` (which covers WebFetch and WebSearch output)
`msd-read-injection-scanner.js` (which covers WebFetch and WebSearch output)
and structurally isolated in-prompt by the `<security_context>` boundary in
research agents — but novel jailbreaks and low-signal injections may still pass
undetected. Defence in depth means each layer makes the attack harder, not that
any single layer makes it impossible.
**What the UI-SPEC provenance rule does not eliminate:** `gsd-ui-checker`
**What the UI-SPEC provenance rule does not eliminate:** `msd-ui-checker`
Dimension 7 requires a component inventory to record the command that
enumerated it, and instructs the checker never to run that command — it is
text from a document, not an instruction to the agent. **That barrier is
prompt-level only.** The checker holds a `Bash` grant it genuinely needs (the
agent-skills bootstrap shells out through `gsd_run`), and tool grants here are
agent-skills bootstrap shells out through `msd_run`), and tool grants here are
not command-scoped, so nothing structurally prevents execution of a command
string lifted out of a UI-SPEC. No shipped instruction does so, and the spec is
written by `gsd-ui-researcher`, which carries the `<security_context>`
written by `msd-ui-researcher`, which carries the `<security_context>`
untrusted-input boundary for its web and MCP ingress — but this is defense by
instruction, not by capability. The same shape is older and wider in Dimension 6,
where the *researcher* is told to run `npx shadcn view {block} --registry {url}`
@@ -452,8 +452,8 @@ and disclosure policy.
## Related
- [Commands](../COMMANDS.md) — includes `/gsd-secure-phase` and
`/gsd-code-review` with security-relevant flags
- [Commands](../COMMANDS.md) — includes `/msd-secure-phase` and
`/msd-code-review` with security-relevant flags
- [Architecture § Hook System](../ARCHITECTURE.md#hook-system) —
implementation detail on every hook, its event trigger, and safety properties
- [SECURITY.md](../../SECURITY.md) — vulnerability reporting, org-wide

View File

@@ -1,12 +1,12 @@
# The phase loop
> The central mental model for how GSD Core organises work.
> The central mental model for how MSD Core organises work.
---
## What the loop is
GSD Core structures all development work as a repeating cycle:
MSD Core structures all development work as a repeating cycle:
```text
Discuss → (UI design) → Plan → Execute → Verify → Ship
@@ -30,7 +30,7 @@ The Discuss step is deliberately lightweight. It is a conversation, not a specif
### UI design (optional)
For phases with a visual component, there is an optional `/gsd-ui-phase` step between Discuss and Plan. It produces a `UI-SPEC.md` — a design contract that describes layout, interaction, and visual behaviour before any code is written. This step is worth running when the UI is complex enough that ambiguity in the design would produce divergent implementation choices. A clear design contract is far cheaper to write than to re-implement.
For phases with a visual component, there is an optional `/msd-ui-phase` step between Discuss and Plan. It produces a `UI-SPEC.md` — a design contract that describes layout, interaction, and visual behaviour before any code is written. This step is worth running when the UI is complex enough that ambiguity in the design would produce divergent implementation choices. A clear design contract is far cheaper to write than to re-implement.
### Plan
@@ -87,7 +87,7 @@ A good phase scope is one where:
- The execution can be parallelised into a handful of non-overlapping plans, not dozens.
- There is a clear, testable definition of done that a verifier can check without reading the entire codebase.
Concretely: "Add HMAC-SHA256 signature validation middleware" is a good phase scope. "Build the authentication system" usually is not — it almost always contains multiple independent concerns that would be better as separate phases. "Fix the typo in the README" is below the threshold where the loop adds value; use `/gsd-quick` instead.
Concretely: "Add HMAC-SHA256 signature validation middleware" is a good phase scope. "Build the authentication system" usually is not — it almost always contains multiple independent concerns that would be better as separate phases. "Fix the typo in the README" is below the threshold where the loop adds value; use `/msd-quick` instead.
When in doubt, split. A smaller phase completes faster, verifies more confidently, and makes it easier to course-correct if a design decision turns out to be wrong.
@@ -113,7 +113,7 @@ The loop exists because each step prevents failures that are genuinely expensive
When the loop works well, it feels like a rhythm: a cadence of focused, bounded work where each step is clear because the previous step did its job. The overhead is real, but it is front-loaded — paid in minutes of planning rather than hours of rework.
For work that falls below the threshold where the loop is warranted, GSD Core provides lighter primitives. The phase loop is one tool, not the only tool.
For work that falls below the threshold where the loop is warranted, MSD Core provides lighter primitives. The phase loop is one tool, not the only tool.
---