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:
106
CONTRIBUTING.md
106
CONTRIBUTING.md
@@ -1,11 +1,11 @@
|
||||
# Contributing to GSD Core
|
||||
# Contributing to MSD Core
|
||||
|
||||
## Getting Started
|
||||
|
||||
```bash
|
||||
# Clone the repo
|
||||
git clone https://github.com/open-gsd/gsd-core.git
|
||||
cd gsd-core
|
||||
git clone https://github.com/golem15com/msd-core.git
|
||||
cd msd-core
|
||||
|
||||
# Activate the pinned Node version from .nvmrc
|
||||
nvm use
|
||||
@@ -31,7 +31,7 @@ environment validator, daily commands, and troubleshooting.
|
||||
|
||||
## Types of Contributions
|
||||
|
||||
GSD accepts three types of contributions. Each type has a different process and a different bar for acceptance. **Read this section before opening anything.**
|
||||
MSD accepts three types of contributions. Each type has a different process and a different bar for acceptance. **Read this section before opening anything.**
|
||||
|
||||
### 🐛 Fix (Bug Report)
|
||||
|
||||
@@ -72,11 +72,11 @@ A feature adds something new — a new command, a new workflow, a new concept, a
|
||||
**Process:**
|
||||
1. **Discuss first** — check [Discussions](https://github.com/open-gsd/gsd-core/discussions) to see if the idea has been raised. If it has and was declined, don't open a new issue.
|
||||
2. Open a [Feature Request issue](https://github.com/open-gsd/gsd-core/issues/new?template=feature_request.yml) with the complete spec. The template requires: the solo-developer problem being solved, what is being added, full scope of affected files and systems, user stories, acceptance criteria, and assessment of maintenance burden.
|
||||
3. **Wait for maintainer approval.** A maintainer must label the issue `approved-feature` before you write a single line of code. Approval is not guaranteed — GSD is intentionally lean and many valid ideas are declined because they conflict with the project's design philosophy.
|
||||
3. **Wait for maintainer approval.** A maintainer must label the issue `approved-feature` before you write a single line of code. Approval is not guaranteed — MSD is intentionally lean and many valid ideas are declined because they conflict with the project's design philosophy.
|
||||
4. Write the code. Implement exactly the approved spec. Changes to scope require re-approval.
|
||||
5. Open a PR using the [Feature PR template](.github/PULL_REQUEST_TEMPLATE/feature.md) — link the approved issue.
|
||||
|
||||
**Rejection reasons:** Issue not labeled `approved-feature`, spec is incomplete, scope exceeds what was approved, feature conflicts with GSD's solo-developer focus, maintenance burden too high.
|
||||
**Rejection reasons:** Issue not labeled `approved-feature`, spec is incomplete, scope exceeds what was approved, feature conflicts with MSD's solo-developer focus, maintenance burden too high.
|
||||
|
||||
---
|
||||
|
||||
@@ -120,7 +120,7 @@ PRs that arrive without a properly-labeled linked issue are closed automatically
|
||||
|
||||
## Where Do I Open My PR? (Branching Model)
|
||||
|
||||
GSD uses two long-lived branches: `main` (production, what's on npm `@latest`)
|
||||
MSD uses two long-lived branches: `main` (production, what's on npm `@latest`)
|
||||
and `next` (integration for the upcoming release). **Almost every PR targets
|
||||
`next`.** Full guide: [`docs/branching.md`](docs/branching.md).
|
||||
|
||||
@@ -142,7 +142,7 @@ git checkout next
|
||||
git pull --ff-only origin next
|
||||
git checkout -b fix/3187-config-corruption
|
||||
# ... commit, push
|
||||
gh pr create --base next --repo open-gsd/gsd-core
|
||||
gh pr create --base next --repo golem15com/msd-core
|
||||
```
|
||||
|
||||
If you target the wrong branch by accident, the `PR Target Validator`
|
||||
@@ -155,7 +155,7 @@ another PR to `next` lands — so in practice you rebase much less.
|
||||
|
||||
**But `next` does still require "up-to-date before merging".** Branch
|
||||
protection has `required_status_checks.strict = true`; check it yourself with
|
||||
`gh api repos/open-gsd/gsd-core/branches/next/protection --jq '.required_status_checks.strict'`.
|
||||
`gh api repos/golem15com/msd-core/branches/next/protection --jq '.required_status_checks.strict'`.
|
||||
If another PR lands while yours is open, yours goes `BEHIND` and must be
|
||||
rebased before it can merge.
|
||||
|
||||
@@ -192,7 +192,7 @@ Contributor requirements (summary):
|
||||
- **Use the correct PR template** — there are separate templates for [Fix](.github/PULL_REQUEST_TEMPLATE/fix.md), [Enhancement](.github/PULL_REQUEST_TEMPLATE/enhancement.md), and [Feature](.github/PULL_REQUEST_TEMPLATE/feature.md). Using the wrong template or using the default template for a feature is a rejection reason.
|
||||
- **Link with a closing keyword** — use `Closes #123`, `Fixes #123`, or `Resolves #123` in the PR body. The CI check will fail and the PR will be auto-closed if no valid issue reference is found.
|
||||
- **Test-only and docs-only follow-up PRs may reference without closing.** If your PR is documentation or regression coverage only — say, a repo-wide guard for a fix that already shipped — and there is no open issue for it to close, use a non-closing reference instead: `Refs #123`. `Ref`, `Refs`, `References`, `Relates to`, `Related to`, and `Follow-up to` are all accepted in that position. Do **not** write a closing keyword against an already-closed issue to satisfy the check; on merge it closes nothing, and it trains readers to treat closing keywords as decorative.
|
||||
- **Qualifying diff shape:** every changed file must be under `tests/`, under `docs/`, or a root-level `*.md` (`README.md`, `CONTRIBUTING.md`, …). This mirrors the doc-only classification the push gate already uses, and it is deliberately root-only — markdown under a subdirectory (`gsd-core/workflows/*.md`, `agents/*.md`, `commands/**/*.md`) is runtime-loaded text, not documentation, so it still requires a closing keyword. `CHANGELOG.md` is excluded too: edit it through a `.changeset/` fragment, never directly.
|
||||
- **Qualifying diff shape:** every changed file must be under `tests/`, under `docs/`, or a root-level `*.md` (`README.md`, `CONTRIBUTING.md`, …). This mirrors the doc-only classification the push gate already uses, and it is deliberately root-only — markdown under a subdirectory (`msd-core/workflows/*.md`, `agents/*.md`, `commands/**/*.md`) is runtime-loaded text, not documentation, so it still requires a closing keyword. `CHANGELOG.md` is excluded too: edit it through a `.changeset/` fragment, never directly.
|
||||
- This weaker form is accepted **only** for that diff shape. A PR touching anything else still needs a closing keyword, and a PR with no issue reference at all still fails. On a very large PR (more than 100 changed files) the check cannot confirm the diff shape and falls back to requiring a closing keyword.
|
||||
- **One concern per PR** — bug fixes, enhancements, and features must be separate PRs
|
||||
- **No drive-by formatting** — don't reformat code unrelated to your change
|
||||
@@ -206,14 +206,14 @@ Contributor requirements (summary):
|
||||
|
||||
```bash
|
||||
npm run changeset -- --type Fixed --pr <YOUR_PR_NUMBER> \
|
||||
--body "**\`/gsd-foo\` no longer drops trailing slashes** — explain the user-visible change."
|
||||
--body "**\`/msd-foo\` no longer drops trailing slashes** — explain the user-visible change."
|
||||
```
|
||||
|
||||
This writes `.changeset/<adjective>-<noun>-<noun>.md`. Three random words → concurrent PRs never collide. Allowed `type:` values follow [Keep a Changelog](https://keepachangelog.com/): `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`.
|
||||
|
||||
Fragments are consolidated into `CHANGELOG.md` at release time by the release workflow. See [`.changeset/README.md`](.changeset/README.md) for the format spec and [#2975](https://github.com/open-gsd/gsd-core/issues/2975) for the rationale.
|
||||
|
||||
**CI enforcement:** the `Changeset Required` workflow (`scripts/changeset/lint.cjs`) fails any PR that touches `bin/`, `gsd-core/`, `src/`, `agents/`, `commands/`, `hooks/`, or `sdk/src/` without a `.changeset/*.md` fragment. (`src/` is the TypeScript source of truth compiled into `gsd-core/bin/lib/*.cjs`, so editing it is a user-facing change even though the generated `.cjs` is gitignored and never appears in the diff.)
|
||||
**CI enforcement:** the `Changeset Required` workflow (`scripts/changeset/lint.cjs`) fails any PR that touches `bin/`, `msd-core/`, `src/`, `agents/`, `commands/`, `hooks/`, or `sdk/src/` without a `.changeset/*.md` fragment. (`src/` is the TypeScript source of truth compiled into `msd-core/bin/lib/*.cjs`, so editing it is a user-facing change even though the generated `.cjs` is gitignored and never appears in the diff.)
|
||||
|
||||
> **Running it locally.** The lint derives its changed-file set from `GITHUB_BASE_REF`, which only CI sets. `node scripts/changeset/lint.cjs` on a developer machine therefore does **not** evaluate your branch and can report success on a PR that CI will fail. Pass the base explicitly to reproduce the CI result:
|
||||
>
|
||||
@@ -238,7 +238,7 @@ To re-format an existing release by hand (e.g. backfilling an older release):
|
||||
|
||||
```bash
|
||||
node scripts/release-notes/format-github-release-notes.cjs \
|
||||
--tag vX.Y.Z --repo open-gsd/gsd-core --apply
|
||||
--tag vX.Y.Z --repo golem15com/msd-core --apply
|
||||
```
|
||||
|
||||
Omit `--apply` to print the reformatted body to stdout for review without
|
||||
@@ -451,16 +451,16 @@ test('does the thing', () => {
|
||||
Import helpers from `tests/helpers.cjs` instead of inlining temp directory creation:
|
||||
|
||||
```javascript
|
||||
const { createTempProject, createTempGitProject, createTempDir, cleanup, runGsdTools } = require('./helpers.cjs');
|
||||
const { createTempProject, createTempGitProject, createTempDir, cleanup, runMsdTools } = require('./helpers.cjs');
|
||||
```
|
||||
|
||||
| Helper | Creates | Use When |
|
||||
|--------|---------|----------|
|
||||
| `createTempProject(prefix?)` | tmpDir with `.planning/phases/` | Testing GSD tools that need planning structure |
|
||||
| `createTempProject(prefix?)` | tmpDir with `.planning/phases/` | Testing MSD tools that need planning structure |
|
||||
| `createTempGitProject(prefix?)` | Same + git init + initial commit | Testing git-dependent features |
|
||||
| `createTempDir(prefix?)` | Bare temp directory | Testing features that don't need `.planning/` |
|
||||
| `cleanup(tmpDir)` | Removes directory recursively | Always use in `afterEach` |
|
||||
| `runGsdTools(args, cwd, env?)` | Executes gsd-tools.cjs | Testing CLI commands |
|
||||
| `runMsdTools(args, cwd, env?)` | Executes msd-tools.cjs | Testing CLI commands |
|
||||
|
||||
### Spawning a subprocess: use the process seam
|
||||
|
||||
@@ -710,7 +710,7 @@ You do not need all twelve cases for every PR. You do need to cover the cases th
|
||||
|
||||
#### CLI and command routing
|
||||
|
||||
Changes to CLI parsing, command dispatch, query dispatch, command routers, `gsd-tools`, or `gsd-sdk` must include a negative input matrix for the affected command family.
|
||||
Changes to CLI parsing, command dispatch, query dispatch, command routers, `msd-tools`, or `msd-sdk` must include a negative input matrix for the affected command family.
|
||||
|
||||
Required cases where relevant:
|
||||
|
||||
@@ -763,7 +763,7 @@ Property-style parser tests are encouraged for high-risk parsers. They must be d
|
||||
|
||||
This is stricter than the adversarial-input rule above and exists because of it: `tests/fixtures/adversarial/` covers hostile input, but a fixture written by the parser's own author — even a deliberately "realistic" one — is still drawn from the author's mental model of the format. It can only ever confirm what the author already believed, never surface what they didn't anticipate. A property-test generator has the same failure mode one level up: seeding the generator from the writer/render function that produces the same format makes the document shape a constant, so the property can never explore a document the writer wouldn't produce (see the document-shaped vs. writer-seeded property tests in `tests/api-coverage.test.cjs` for a worked example — the writer-seeded one cannot fail against a decoy table; the document-shaped one can).
|
||||
|
||||
For a gate whose fixtures come from real user reports, put them under `tests/fixtures/representative/<gate>/` with a `MANIFEST.json` labeling each fixture's source issue and expected gate verdict, and drive them through the gate's real CLI entrypoint (gate-verdict altitude), not the parser function in isolation — see `tests/fixtures/representative/README.md` and `tests/representative-corpus.test.cjs`. If the gate is not yet fixed, do not mark the assertion `{ todo: true }` and do not skip it: this repo's test-runner (`gsd-test` / `gsd-test-runner`) has no concept of node:test's `todo` option — its JSONL result parser only recognizes `kind: "pass" | "fail"`, so a thrown todo-marked test is still counted as a real failure and blocks the push gate. Instead record BOTH the correct target verdict (`expected*`) and the exact current observed verdict (`currentBuggyOutput`) in the manifest, and assert against `currentBuggyOutput` — an honest, non-vacuous characterization of today's known-broken behavior that passes today and breaks loudly the moment the real fix changes the observed output, forcing the assertion to be flipped to `expected*`.
|
||||
For a gate whose fixtures come from real user reports, put them under `tests/fixtures/representative/<gate>/` with a `MANIFEST.json` labeling each fixture's source issue and expected gate verdict, and drive them through the gate's real CLI entrypoint (gate-verdict altitude), not the parser function in isolation — see `tests/fixtures/representative/README.md` and `tests/representative-corpus.test.cjs`. If the gate is not yet fixed, do not mark the assertion `{ todo: true }` and do not skip it: this repo's test-runner (`msd-test` / `msd-test-runner`) has no concept of node:test's `todo` option — its JSONL result parser only recognizes `kind: "pass" | "fail"`, so a thrown todo-marked test is still counted as a real failure and blocks the push gate. Instead record BOTH the correct target verdict (`expected*`) and the exact current observed verdict (`currentBuggyOutput`) in the manifest, and assert against `currentBuggyOutput` — an honest, non-vacuous characterization of today's known-broken behavior that passes today and breaks loudly the moment the real fix changes the observed output, forcing the assertion to be flipped to `expected*`.
|
||||
|
||||
#### Filesystem writes and installers
|
||||
|
||||
@@ -825,7 +825,7 @@ Generator tests should run in temp fixtures and assert atomic output behavior. D
|
||||
```javascript
|
||||
// BAD — source-grep theater
|
||||
const configSrc = fs.readFileSync(
|
||||
path.join(GSD_ROOT, 'gsd-core', 'bin', 'lib', 'config-schema.cjs'), 'utf-8'
|
||||
path.join(MSD_ROOT, 'msd-core', 'bin', 'lib', 'config-schema.cjs'), 'utf-8'
|
||||
);
|
||||
assert.ok(
|
||||
configSrc.includes("'workflow.plan_bounce'"),
|
||||
@@ -843,7 +843,7 @@ test('config-set accepts workflow.plan_bounce', (t) => {
|
||||
const tmpDir = createTempProject();
|
||||
t.after(() => cleanup(tmpDir));
|
||||
|
||||
const result = runGsdTools('config-set workflow.plan_bounce true', tmpDir);
|
||||
const result = runMsdTools('config-set workflow.plan_bounce true', tmpDir);
|
||||
assert.ok(result.success, `config-set should accept workflow.plan_bounce: ${result.error}`);
|
||||
|
||||
const configPath = path.join(tmpDir, '.planning', 'config.json');
|
||||
@@ -869,7 +869,7 @@ Some tests legitimately read source files. There are six recognized categories:
|
||||
| `structural-regression-guard` | A specific code pattern must (or must not) exist to prevent a class of bug (e.g., regex global-state misuse). Behavioral tests cannot distinguish which pattern was used. |
|
||||
| `docs-parity` | A reference doc must stay in sync with source-defined constants (e.g., `CONFIG_DEFAULTS`). The source is the canonical list; there is no runtime API to enumerate it. |
|
||||
| `integration-test-input` | A source file is used as a real fixture input to a transformation function under test — the file is not inspected for strings but passed as data. |
|
||||
| `structural-implementation-guard` | A feature's interception or wiring point is not reachable end-to-end via `runGsdTools`. Used temporarily until a behavioral path exists. |
|
||||
| `structural-implementation-guard` | A feature's interception or wiring point is not reachable end-to-end via `runMsdTools`. Used temporarily until a behavioral path exists. |
|
||||
| `pending-migration-to-typed-ir` | **Tracked for correction, not exempted.** Test was identified by the lint as carrying a raw-text-matching pattern that contradicts the rule above. Each annotated file MUST cite the open migration issue (e.g. `// allow-test-rule: pending-migration-to-typed-ir [#NNNN]`) so the tracking is auditable. New tests cannot use this category — they must refactor production to expose typed IR. The annotation is removed when the test is corrected. |
|
||||
|
||||
**Suppression is site-scoped, not file-wide.** A marker suppresses only the violation it sits next
|
||||
@@ -924,7 +924,7 @@ The following are all violations of the same rule:
|
||||
|
||||
```javascript
|
||||
// BAD — substring match on text written by the code under test
|
||||
const cmdContent = fs.readFileSync(path.join(tmpDir, 'gsd-sdk.cmd'), 'utf8');
|
||||
const cmdContent = fs.readFileSync(path.join(tmpDir, 'msd-sdk.cmd'), 'utf8');
|
||||
assert.ok(cmdContent.includes(`@node ${jsonQuoted} %*`), '.cmd embeds shim path');
|
||||
|
||||
// BAD — regex match on a child process's human-readable stdout formatter
|
||||
@@ -959,7 +959,7 @@ Concretely: for any system-under-test that produces text output (a file renderer
|
||||
|
||||
#### Concrete example from this repo
|
||||
|
||||
`gsd-core/bin/verify-reapply-patches.cjs` exposes a frozen `REASON` enum and emits it through `--json`. Tests assert `report.results[0].reason === REASON.FAIL_USER_LINES_MISSING` rather than regex-matching the human-readable prose. The human formatter exists for operator console output only — tests must not depend on it. Adding a new reason code requires updating the `REASON` enum, the `--json` output, AND the test that locks `Object.keys(REASON).sort()` — three coordinated changes that keep the code surface from drifting from the test surface. A pure builder that returns the IR (no I/O) and a writer that consumes it — `fs.statSync(target).size === Buffer.byteLength(render())` to prove the writer writes what the renderer produces, **without comparing content** — is the same pattern applied to rendered files.
|
||||
`msd-core/bin/verify-reapply-patches.cjs` exposes a frozen `REASON` enum and emits it through `--json`. Tests assert `report.results[0].reason === REASON.FAIL_USER_LINES_MISSING` rather than regex-matching the human-readable prose. The human formatter exists for operator console output only — tests must not depend on it. Adding a new reason code requires updating the `REASON` enum, the `--json` output, AND the test that locks `Object.keys(REASON).sort()` — three coordinated changes that keep the code surface from drifting from the test surface. A pure builder that returns the IR (no I/O) and a writer that consumes it — `fs.statSync(target).size === Buffer.byteLength(render())` to prove the writer writes what the renderer produces, **without comparing content** — is the same pattern applied to rendered files.
|
||||
|
||||
#### Hiding grep behind a function is still grep
|
||||
|
||||
@@ -1031,7 +1031,7 @@ For examples of required negative matrices, parser fixtures, filesystem fault in
|
||||
|
||||
When you can, run the local test bench harness before opening a PR — especially for Windows-sensitive changes.
|
||||
|
||||
- Setup guide: [gsd-test-runner getting started](https://github.com/open-gsd/gsd-test-runner/blob/main/docs/getting-started.md)
|
||||
- Setup guide: [msd-test-runner getting started](https://github.com/open-gsd/gsd-test-runner/blob/main/docs/getting-started.md)
|
||||
- Preferred PR evidence: include the bench results summary (or artifact link) in your PR body.
|
||||
|
||||
This gives maintainers a faster, higher-confidence signal than CI-only validation.
|
||||
@@ -1045,15 +1045,15 @@ sources it feeds, run:
|
||||
npm run check:alias-drift
|
||||
```
|
||||
|
||||
This verifies the built alias artifacts under `gsd-core/bin/lib/` agree with their
|
||||
This verifies the built alias artifacts under `msd-core/bin/lib/` agree with their
|
||||
source of truth — each family's `*_SUBCOMMANDS` list must match the `subcommand`
|
||||
values derived from its `*_COMMAND_ALIASES` table, in order, and each router must
|
||||
reference its own list. The surface is enumerated once in
|
||||
`scripts/lib/alias-drift-families.cjs`.
|
||||
|
||||
### Editing shipped content (gsd-core/workflows, references, templates, contexts, agents/, commands/gsd/)
|
||||
### Editing shipped content (msd-core/workflows, references, templates, contexts, agents/, commands/msd/)
|
||||
|
||||
Editing the content of a copied shipped file — a `gsd-core/workflows/*.md`, an agent, a
|
||||
Editing the content of a copied shipped file — a `msd-core/workflows/*.md`, an agent, a
|
||||
command definition — requires **zero manual fixture regeneration**. There is no
|
||||
committed path→hash manifest or per-file size baseline to update by hand; the
|
||||
differential attribution check (`tests/emitted-attribution.test.cjs`, ADR-2719) computes
|
||||
@@ -1065,12 +1065,12 @@ a converter change, for example — go through a **commit trailer on one of your
|
||||
commits** (ADR-3942; name the key, say why):
|
||||
|
||||
```
|
||||
Emitted-Drift-Ack-Hash: skills/gsd-add-tests/SKILL.md — the converter rewrote every skill header
|
||||
Emitted-Drift-Ack-Hash: skills/msd-add-tests/SKILL.md — the converter rewrote every skill header
|
||||
Emitted-Drift-Ack-Growth: explore.md — new dispatch section, reasoning ships with the block
|
||||
```
|
||||
|
||||
See `CONTEXT.md`'s `### Emitted Artifact Provenance` entry for the full model. Growth in a
|
||||
`gsd-core/workflows/*.md` or `agents/gsd-*.md` file is reported with its exact byte delta
|
||||
`msd-core/workflows/*.md` or `agents/msd-*.md` file is reported with its exact byte delta
|
||||
and needs the same acknowledgment; the outer tier hard caps in
|
||||
`tests/workflow-size-budget.test.cjs` / `tests/agent-size-budget.test.cjs` are unaffected
|
||||
and still apply.
|
||||
@@ -1096,8 +1096,8 @@ You do not need to memorize any of this. **The failure output names its own reme
|
||||
tells you which key to add and prints a minimal trailer line you can paste onto one of your
|
||||
commits. Note the two key spaces, because the message says which one applies: an
|
||||
unattributable **hash** ripple is keyed on the emitted path
|
||||
(`skills/gsd-add-tests/SKILL.md`), while **growth** is keyed on the bare filename as it
|
||||
appears under `gsd-core/workflows/` or `agents/` (`explore.md`). The two spaces are
|
||||
(`skills/msd-add-tests/SKILL.md`), while **growth** is keyed on the bare filename as it
|
||||
appears under `msd-core/workflows/` or `agents/` (`explore.md`). The two spaces are
|
||||
structurally distinct — a `Growth` trailer never excuses a `Hash` ripple, even when the key
|
||||
text happens to match.
|
||||
|
||||
@@ -1119,7 +1119,7 @@ reports an ordinary (non-protected) line that moved from a spine into its own de
|
||||
without a declaration, add:
|
||||
|
||||
```
|
||||
Boundary-Move-Declared: gsd-core/workflows/plan-phase.md — condensed the filesystem-fallback banner into one summary paragraph
|
||||
Boundary-Move-Declared: msd-core/workflows/plan-phase.md — condensed the filesystem-fallback banner into one summary paragraph
|
||||
```
|
||||
|
||||
Same range (`git log $(git merge-base <base> HEAD)..HEAD`), same fail-closed behavior on an
|
||||
@@ -1174,7 +1174,7 @@ hooks does not enable this check:
|
||||
|
||||
```bash
|
||||
# set locally in your shell profile (example)
|
||||
export GSD_BLOCKED_AUTHOR_REGEX='@example-corp\.com$'
|
||||
export MSD_BLOCKED_AUTHOR_REGEX='@example-corp\.com$'
|
||||
```
|
||||
|
||||
With that exported, a push carrying a commit whose author email matches is blocked,
|
||||
@@ -1182,8 +1182,8 @@ and the hook names the offending commits. Unset the variable to disable it.
|
||||
|
||||
### Every `commit` invocation in shipped content must declare `--files`
|
||||
|
||||
`tests/commit-files-pathspec.test.cjs` scans every `.md` under `gsd-core/workflows/`,
|
||||
`gsd-core/references/`, `agents/`, `commands/`, `skills/` and `docs/` for invocations of
|
||||
`tests/commit-files-pathspec.test.cjs` scans every `.md` under `msd-core/workflows/`,
|
||||
`msd-core/references/`, `agents/`, `commands/`, `skills/` and `docs/` for invocations of
|
||||
the `commit` seam, and fails if any of them reaches the runtime without a `--files`
|
||||
scope. An unscoped invocation lands on the blanket-stage default and sweeps the whole
|
||||
`.planning/` index into a commit whose message names one artifact — that is [#2269](https://github.com/open-gsd/gsd-core/issues/2269),
|
||||
@@ -1197,7 +1197,7 @@ would blind the guard to every site the issue was filed about. Two consequences
|
||||
hit while editing shipped content, and the failure output names both:
|
||||
|
||||
**A prose mention that runs into its sentence is flagged.** Nothing distinguishes
|
||||
`gsd_run query commit` followed by ordinary words from an invocation with arguments
|
||||
`msd_run query commit` followed by ordinary words from an invocation with arguments
|
||||
without guessing at English, so the scan does not try. Write the command reference in
|
||||
backticks — the repo's own convention — and it is correctly read as a mention.
|
||||
|
||||
@@ -1207,7 +1207,7 @@ stand in for your intent. Declare it on the invocation's own line, in shell-comm
|
||||
position:
|
||||
|
||||
```
|
||||
gsd_run query commit "docs: message" # gsd-scan-ignore: #2269 counter-example for the docs
|
||||
msd_run query commit "docs: message" # msd-scan-ignore: #2269 counter-example for the docs
|
||||
```
|
||||
|
||||
That block is a live example of itself: the invocation above really is unscoped, and it
|
||||
@@ -1223,8 +1223,8 @@ means the runtime executed the line.
|
||||
|
||||
### Every `git add` in shipped content that can reach `.planning/` must sit inside an *executable* `commit_docs` check
|
||||
|
||||
`tests/commit-docs-bypass.test.cjs` scans every `.md` under `gsd-core/workflows/`,
|
||||
`gsd-core/references/`, `agents/`, `commands/` and `skills/` and fails if a `git add` that could
|
||||
`tests/commit-docs-bypass.test.cjs` scans every `.md` under `msd-core/workflows/`,
|
||||
`msd-core/references/`, `agents/`, `commands/` and `skills/` and fails if a `git add` that could
|
||||
stage `.planning/` is not enclosed by a `commit_docs` check that actually runs.
|
||||
|
||||
This is the sibling of the `--files` guard above, and it exists for the complementary hole.
|
||||
@@ -1244,24 +1244,24 @@ git add "${EVAL_REVIEW_FILE}" ← runs unconditionally; the bold line i
|
||||
````
|
||||
|
||||
The bash block executes whatever the sentence above it says. Write the check in shell, which is
|
||||
the form `gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md` already uses:
|
||||
the form `msd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md` already uses:
|
||||
|
||||
```bash
|
||||
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
|
||||
COMMIT_DOCS=$(msd_run query config-get commit_docs 2>/dev/null || echo "true")
|
||||
if [ "$COMMIT_DOCS" != "false" ]; then
|
||||
git add "${ARTIFACT}"
|
||||
fi
|
||||
```
|
||||
|
||||
Both polarities are accepted (`!= "false"` and `= "true"`). The `|| echo "true"` fallback is
|
||||
deliberate: a tooling failure must fail *open*, or a broken `gsd-tools` silently stops committing
|
||||
deliberate: a tooling failure must fail *open*, or a broken `msd-tools` silently stops committing
|
||||
planning docs for someone who wants them.
|
||||
|
||||
Three consequences worth knowing before you edit shipped content:
|
||||
|
||||
**Guard state does not cross a fenced block.** Each fenced block is its own shell, so an `if`
|
||||
opened in one block does not protect a `git add` in the next — the same reason
|
||||
`new-milestone.md` warns that a `GSD_WS` guard set in an earlier step reads as unset later. Put
|
||||
`new-milestone.md` warns that a `MSD_WS` guard set in an earlier step reads as unset later. Put
|
||||
the check and the `git add` in the same block.
|
||||
|
||||
**An unresolvable path is treated as reaching `.planning/`.** `git add "${ARTIFACT}"` is flagged,
|
||||
@@ -1272,7 +1272,7 @@ trigger it. If your `git add` genuinely cannot touch `.planning/`, name the path
|
||||
**Only fenced lines are scanned.** Inline-backtick prose — including the anti-pattern
|
||||
documentation that tells you never to run `git add -A` — is not executable and is not flagged.
|
||||
|
||||
The same `# gsd-scan-ignore: #NNN` declaration as the `--files` guard exempts a deliberate
|
||||
The same `# msd-scan-ignore: #NNN` declaration as the `--files` guard exempts a deliberate
|
||||
counter-example, on the invocation's own line, in shell-comment position, with a reason naming a
|
||||
tracking issue or URL. Both guards share one tokenizer and one marker implementation
|
||||
(`tests/helpers/shipped-command-scan.cjs`) so the two conventions can never drift into two rules
|
||||
@@ -1281,7 +1281,7 @@ wearing one name.
|
||||
**Known limits.** The scan (`tests/helpers/planning-add-guard.cjs`) is a token-oriented text scan,
|
||||
not a shell interpreter, and it targets accidental reintroduction of an unguarded stage by a
|
||||
contributor editing shipped content — not a determined bypass. Four shapes are confirmed (#3585)
|
||||
to stage `.planning/` at runtime while scoring zero offenders, and none is a shape GSD content
|
||||
to stage `.planning/` at runtime while scoring zero offenders, and none is a shape MSD content
|
||||
actually uses: `eval "git add -A"`, `find .planning -type f | xargs git add`, a one-line shell
|
||||
function body (`f() { git add -A; }`), and a backslash line-continuation split across two physical
|
||||
lines. The scan also only models `git add` and `git commit -a`/`--all` as staging commands — it
|
||||
@@ -1327,7 +1327,7 @@ The following checks run on every PR in addition to the test suite:
|
||||
|
||||
| Job | What it checks | How to pass |
|
||||
|-----|----------------|-------------|
|
||||
| `Lint — ESLint` | No source-grep tests (see above), via the `local/no-source-grep` rule | Replace with `runGsdTools()` behavioral tests, or add `// allow-test-rule: <reason>` |
|
||||
| `Lint — ESLint` | No source-grep tests (see above), via the `local/no-source-grep` rule | Replace with `runMsdTools()` behavioral tests, or add `// allow-test-rule: <reason>` |
|
||||
| `Lint — cross-platform portability` | Windows-portability defects in tests, via `local/no-path-literal-in-assert` (more rules land per [ADR-1703](docs/adr/1703-portability-enforcement-architecture.md)) — e.g. a path-returning call asserted against a hardcoded `/`-literal | Normalize the actual: `String(pathFn(...)).replace(/\\/g, '/')`, or structure platform-specific code behind a `process.platform !== 'win32'` guard. **No `eslint-disable`** — see [cross-platform-portability-rules.md](docs/contributing/cross-platform-portability-rules.md) |
|
||||
| `lint-docs-guard-registration.cjs` (via `npm run lint:ci`) | A test that reads shipped `docs/` content must be registered so it runs on the PR that changes those docs — otherwise it can only fail after merge | Register it in `scripts/docs-guard-registry.cjs`, mapping the test to the docs paths it reads, or mark it `// docs-guard-exempt: <reason>` and list it in `scripts/lint-docs-guard-registration.exempt-baseline.cjs` — see [docs-guard-registration.md](docs/contributing/docs-guard-registration.md) |
|
||||
| `lint-response-language-coverage.cjs` (via `npm run lint:ci`) | Every workflow file instructs the model to honour `response_language` in user-facing prose, and the directive names inter-tool narration rather than questions alone — a directive that omits the narration class leaves running commentary in English beside translated answers (#2529) | Give the file one of the four coverage forms: the eager `@`-reference, its own inline directive, the pinned line, or proven inheritance from the parent that dispatches it — see [response-language-coverage.md](docs/contributing/response-language-coverage.md) |
|
||||
@@ -1374,14 +1374,14 @@ Defensive normalization at trust boundaries must validate both the value's type
|
||||
## Code Style
|
||||
|
||||
- **CommonJS** (`.cjs`) — the project uses `require()`, not ESM `import`
|
||||
- **No external dependencies in core** — `gsd-tools.cjs` and all lib files use only Node.js built-ins
|
||||
- **Conventional commits** — `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `ci:`. The full grammar is `<type>(<scope>): <subject>` (enforced by `hooks/gsd-validate-commit.sh`; subject ≤72 chars, lowercase, imperative mood, no trailing period). When the work resolves a tracked issue, put the issue number in the scope: `fix(#1520): randomize mktemp temp paths on BSD/macOS`. The same convention applies to PR titles — release notes are grouped by the title's type prefix (`feat` → Feature, `fix` → Fix, non-user-facing types omitted, everything else → Enhancement).
|
||||
- **No external dependencies in core** — `msd-tools.cjs` and all lib files use only Node.js built-ins
|
||||
- **Conventional commits** — `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `ci:`. The full grammar is `<type>(<scope>): <subject>` (enforced by `hooks/msd-validate-commit.sh`; subject ≤72 chars, lowercase, imperative mood, no trailing period). When the work resolves a tracked issue, put the issue number in the scope: `fix(#1520): randomize mktemp temp paths on BSD/macOS`. The same convention applies to PR titles — release notes are grouped by the title's type prefix (`feat` → Feature, `fix` → Fix, non-user-facing types omitted, everything else → Enhancement).
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
bin/install.js — Installer (multi-runtime)
|
||||
gsd-core/
|
||||
msd-core/
|
||||
bin/lib/ — Core library modules (.cjs)
|
||||
workflows/ — Workflow definitions (.md)
|
||||
Large workflows split per progressive-disclosure
|
||||
@@ -1397,14 +1397,14 @@ gsd-core/
|
||||
requires an Emitted-Drift-Ack-Growth commit trailer
|
||||
(ADR-3942), no committed snapshot to regenerate. Loose tier
|
||||
hard caps remain in tests/workflow-size-budget.test.cjs.
|
||||
The same applies to agent files (agents/gsd-*.md,
|
||||
The same applies to agent files (agents/msd-*.md,
|
||||
tests/agent-size-budget.test.cjs). Full how-to +
|
||||
reference in docs/TESTING-SUITES.md (Workflow &
|
||||
agent size budget); see issue #1074.
|
||||
references/ — Reference documentation (.md)
|
||||
templates/ — File templates
|
||||
agents/ — Agent definitions (.md) — CANONICAL SOURCE
|
||||
commands/gsd/ — Slash command definitions (.md)
|
||||
commands/msd/ — Slash command definitions (.md)
|
||||
tests/ — Test files (.test.cjs)
|
||||
helpers.cjs — Shared test utilities
|
||||
docs/ — User-facing documentation
|
||||
@@ -1412,13 +1412,13 @@ docs/ — User-facing documentation
|
||||
|
||||
### Source of truth for agents
|
||||
|
||||
Only `agents/` at the repo root is tracked by git. The following directories may exist on a developer machine with GSD installed and **must not be edited** — they are install-sync outputs and will be overwritten:
|
||||
Only `agents/` at the repo root is tracked by git. The following directories may exist on a developer machine with MSD installed and **must not be edited** — they are install-sync outputs and will be overwritten:
|
||||
|
||||
| Path | Gitignored | What it is |
|
||||
|------|-----------|------------|
|
||||
| `.claude/agents/` | Yes (`.gitignore:9`) | Local Claude Code runtime sync |
|
||||
| `.cursor/agents/` | Yes (`.gitignore:12`) | Local Cursor IDE bundle |
|
||||
| `.github/agents/gsd-*` | Yes (`.gitignore:37`) | Local CI-surface bundle |
|
||||
| `.github/agents/msd-*` | Yes (`.gitignore:37`) | Local CI-surface bundle |
|
||||
|
||||
If you find that `.claude/agents/` has drifted from `agents/` (e.g., after a branch change), re-run `bin/install.js` to re-sync from the canonical source. Always edit `agents/` — never the derivative directories.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user