@therocketcode/gsd-core 1.6.2 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +14 -0
- package/agents/gsd-plan-checker.md +2 -0
- package/gemini-extension.json +1 -1
- package/gsd-core/bin/lib/legacy-cleanup.cjs +44 -14
- package/gsd-core/references/architecture-decision.md +14 -1
- package/gsd-core/references/contract-testing.md +62 -0
- package/gsd-core/references/domain-modeling.md +21 -1
- package/gsd-core/references/product-discovery.md +5 -3
- package/gsd-core/references/test-strategy.md +1 -0
- package/gsd-core/templates/domain-model.md +7 -2
- package/gsd-core/templates/product-brief.md +17 -2
- package/gsd-core/workflows/discover-product.md +3 -3
- package/gsd-core/workflows/model-domain.md +5 -0
- package/gsd-core/workflows/recommend-architecture.md +2 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-core",
|
|
3
3
|
"displayName": "GSD Core",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.7.1",
|
|
5
5
|
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "TheRocketCodeMX",
|
package/README.md
CHANGED
|
@@ -74,6 +74,20 @@ Once installed, start your first project:
|
|
|
74
74
|
|
|
75
75
|
New here? Follow [Your first project](docs/tutorials/your-first-project.md) for a guided walkthrough from install to first shipped phase.
|
|
76
76
|
|
|
77
|
+
### Installing & updating
|
|
78
|
+
|
|
79
|
+
How you get the latest version depends on what you already have installed:
|
|
80
|
+
|
|
81
|
+
- **Nothing installed yet** — run the Quickstart command once:
|
|
82
|
+
```bash
|
|
83
|
+
npx -y @therocketcode/gsd-core@latest --claude --global # or --local for a single project
|
|
84
|
+
```
|
|
85
|
+
After that you're on the self-update path below.
|
|
86
|
+
|
|
87
|
+
- **Already have this package (`@therocketcode/gsd-core`)** — just run `/gsd-update` in your session. The package coordinate is baked into the install, so a SessionStart check surfaces a banner ("GSD update available: X → Y. Run /gsd:update.") and `/gsd-update` pulls the new version. No special command, no reinstall.
|
|
88
|
+
|
|
89
|
+
- **Coming from the original upstream GSD (`@opengsd/gsd-core` / `get-shit-done`)** — its `/gsd-update` points at the upstream package and will never find this fork (different npm coordinate). Switch with a one-time install using the Quickstart command above. The installer overwrites the same-named `/gsd-*` files, re-points the baked identity at this fork (so future `/gsd-update` works), and the bundled legacy-cleanup removes superseded upstream hooks and stale update-check caches. After that, the self-update path applies.
|
|
90
|
+
|
|
77
91
|
---
|
|
78
92
|
|
|
79
93
|
## Documentation
|
|
@@ -71,11 +71,13 @@ This ensures verification checks that plans follow project-specific conventions.
|
|
|
71
71
|
| `## Decisions` | LOCKED — plans MUST implement these exactly. Flag if contradicted. |
|
|
72
72
|
| `## Claude's Discretion` | Freedom areas — planner can choose approach, don't flag. |
|
|
73
73
|
| `## Deferred Ideas` | Out of scope — plans must NOT include these. Flag if present. |
|
|
74
|
+
| `## Canonical References` | MUST-read docs (incl. any DOMAIN-MODEL.md / architecture ADR / TEST-STRATEGY.md). Read them; plans MUST follow them. |
|
|
74
75
|
|
|
75
76
|
If CONTEXT.md exists, add verification dimension: **Context Compliance**
|
|
76
77
|
- Do plans honor locked decisions?
|
|
77
78
|
- Are deferred ideas excluded?
|
|
78
79
|
- Are discretion areas handled appropriately?
|
|
80
|
+
- **Do plans honor the canonical discovery artifacts?** Flag a HIGH concern if a task contradicts the architecture ADR's per-subdomain rung (e.g. CRUD where a Domain Model is mandated), the DOMAIN-MODEL classification, or the TEST-STRATEGY's test levels (e.g. unit-mocking the DB where integration via Testcontainers is required, or float money where integer minor units are mandated).
|
|
79
81
|
</upstream_input>
|
|
80
82
|
|
|
81
83
|
<core_principle>
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "GSD Core — a meta-prompting, context engineering, and spec-driven development system for AI coding agents. Loads gsd's operating context into every Gemini CLI session.",
|
|
5
5
|
"contextFileName": "GEMINI.md"
|
|
6
6
|
}
|
|
@@ -21,12 +21,40 @@ const fs = require('fs');
|
|
|
21
21
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* Substrings that identify a code file as belonging to a superseded GSD
|
|
25
|
+
* package — either the very-old pre-rename original or the rug-pulled upstream
|
|
26
|
+
* this project forked away from. A scanned code file (.js/.cjs/.mjs/.sh under
|
|
27
|
+
* hooks/ or commands/) containing ANY of these is a genuine leftover from a
|
|
28
|
+
* package this install replaces, and is safe to flag.
|
|
29
|
+
*
|
|
30
|
+
* Each is assembled from parts so THIS source file never contains the literal
|
|
26
31
|
* as a plain substring (avoids self-flagging if the content scan were ever
|
|
27
|
-
* widened
|
|
32
|
+
* widened to include the gsd-core/ subtree). Our own shipped code contains
|
|
33
|
+
* none of these — the package-identity drift lint guards that — so flagging
|
|
34
|
+
* them can never delete a freshly-installed @therocketcode file.
|
|
35
|
+
*
|
|
36
|
+
* - 'gsd-core-cc' — the pre-rename original package.
|
|
37
|
+
* - '@opengsd/gsd-core' — the upstream npm coordinate (rug-pulled fork source).
|
|
38
|
+
* - 'open-gsd/gsd-core' — the upstream GitHub repo slug.
|
|
39
|
+
*/
|
|
40
|
+
const OLD_PACKAGE_SIGNALS = [
|
|
41
|
+
'gsd-core' + '-cc',
|
|
42
|
+
'@opengsd' + '/gsd-core',
|
|
43
|
+
'open-gsd' + '/gsd-core',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Update-check cache files written by superseded packages, removed so a stale
|
|
48
|
+
* cache can't suppress or misreport update availability after switching.
|
|
49
|
+
* NEVER include the current package's own cache
|
|
50
|
+
* (`gsd-update-check-therocketcode-gsd-core.json`).
|
|
51
|
+
* - 'gsd-update-check.json' — the old shared (pre-per-package) cache.
|
|
52
|
+
* - 'gsd-update-check-opengsd-gsd-core.json' — the upstream @opengsd per-package cache.
|
|
28
53
|
*/
|
|
29
|
-
const
|
|
54
|
+
const LEGACY_CACHE_FILENAMES = [
|
|
55
|
+
'gsd-update-check.json',
|
|
56
|
+
'gsd-update-check-opengsd-gsd-core.json',
|
|
57
|
+
];
|
|
30
58
|
|
|
31
59
|
/**
|
|
32
60
|
* Subtrees within a configDir that GSD actively scans for old-package content.
|
|
@@ -97,7 +125,7 @@ function collectFilesUnder(dir, fsMod) {
|
|
|
97
125
|
}
|
|
98
126
|
|
|
99
127
|
/**
|
|
100
|
-
* Return true if the file at `absPath` contains
|
|
128
|
+
* Return true if the file at `absPath` contains ANY superseded-package signal.
|
|
101
129
|
* Skips unreadable files (returns false on any error).
|
|
102
130
|
*
|
|
103
131
|
* @param {string} absPath
|
|
@@ -107,7 +135,7 @@ function collectFilesUnder(dir, fsMod) {
|
|
|
107
135
|
function fileContainsOldPackageSignal(absPath, fsMod) {
|
|
108
136
|
try {
|
|
109
137
|
const content = fsMod.readFileSync(absPath, 'utf8');
|
|
110
|
-
return content.includes(
|
|
138
|
+
return OLD_PACKAGE_SIGNALS.some((signal) => content.includes(signal));
|
|
111
139
|
} catch {
|
|
112
140
|
return false;
|
|
113
141
|
}
|
|
@@ -166,15 +194,17 @@ function planLegacyCleanup(configDirs, opts = {}) {
|
|
|
166
194
|
}
|
|
167
195
|
}
|
|
168
196
|
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
197
|
+
// Update-check caches written by superseded packages (never the current one)
|
|
198
|
+
for (const cacheName of LEGACY_CACHE_FILENAMES) {
|
|
199
|
+
const legacyCachePath = path.join(homeDir, '.cache', 'gsd', cacheName);
|
|
200
|
+
try {
|
|
201
|
+
const stat = fsMod.statSync(legacyCachePath);
|
|
202
|
+
if (stat.isFile()) {
|
|
203
|
+
addCandidate(legacyCachePath, 'legacy-shared-cache');
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
// absent — skip
|
|
175
207
|
}
|
|
176
|
-
} catch {
|
|
177
|
-
// absent — skip
|
|
178
208
|
}
|
|
179
209
|
|
|
180
210
|
// Sort deterministically by path
|
|
@@ -30,10 +30,23 @@ Use the core subdomain's complexity from DOMAIN-MODEL. Apply per subdomain: the
|
|
|
30
30
|
1. **Multiple independent teams** needing independent deploy cadence (Conway / Team Topologies).
|
|
31
31
|
2. **CD / monitoring / DevOps maturity** already in place.
|
|
32
32
|
3. **Bounded contexts well-understood** already (not still being discovered).
|
|
33
|
-
If **any** is "no" → recommend **modular monolith and stop**, regardless of complexity. (The "microservice premium": below a complexity+org threshold the distributed tax is pure loss.)
|
|
33
|
+
If **any** is "no" → recommend **modular monolith and stop**, regardless of complexity (deferred, not forbidden — record the promotion trigger; see *Evolving the topology* below). (The "microservice premium": below a complexity+org threshold the distributed tax is pure loss.)
|
|
34
34
|
- **Component-level split (Hard Parts):** for a specific component, score the **6 disintegrators** (low cohesion · divergent volatility · divergent scalability · fault isolation · differential security · independent extensibility) against the **4 integrators** (ACID across the data · tightly-coupled workflow · heavy shared code · tight data relationships). Net disintegrators ≫ integrators → candidate extraction; otherwise keep it in the monolith.
|
|
35
35
|
- **Distributed monolith** (services that can't deploy independently) is the failure mode — you pay the premium and get none of the autonomy. Avoid.
|
|
36
36
|
|
|
37
|
+
The "modular monolith and stop, regardless of complexity" rule is about *not splitting prematurely* — it is **not** "never split." It means the split is deferred until a gate flips, and the modular boundaries are built now so the split is cheap later (a **sacrificial / evolutionary** architecture). Record the **promotion trigger** — the concrete future signal (a second team forms, a component's scaling diverges, a bounded context stabilizes) that would justify revisiting Axis B.
|
|
38
|
+
|
|
39
|
+
## Evolving the topology — decomposition & migration (when a gate later flips)
|
|
40
|
+
|
|
41
|
+
When a promotion trigger fires and a component genuinely warrants extraction, the *data* is the hard part — splitting logic is easy, splitting a shared database is not. Recommend, in order:
|
|
42
|
+
|
|
43
|
+
- **Strangler Fig** — route new behavior to the new component while the old path keeps serving, shrinking the monolith incrementally. Never a big-bang rewrite.
|
|
44
|
+
- **Anti-Corruption Layer (ACL)** — a translation seam at the new boundary so the extracted component's model isn't polluted by the legacy/shared schema's vocabulary. The ACL is also the right tool when integrating a third-party/legacy system whose model differs from yours.
|
|
45
|
+
- **Data decomposition** — pull the component's tables behind its own schema/owner first (enforce "no cross-module DB access" as a fitness function *before* extracting), then separate the datastore. Identify the data that must move vs. the data that stays shared (and gets an API/ACL instead).
|
|
46
|
+
- **Sagas / outbox for cross-service consistency** — once a transaction spans two services you lose ACID; replace it with a **saga** (a sequence of local transactions + compensating actions) and the **transactional outbox** pattern for reliable event publishing. If a workflow genuinely needs one ACID transaction, that's an *integrator* — a reason to **keep it together**, not split it.
|
|
47
|
+
|
|
48
|
+
The same tools run in reverse for a brownfield monolith you're decomposing — strangle, wrap legacy in an ACL, decompose the data behind module boundaries first.
|
|
49
|
+
|
|
37
50
|
## Non-functional drivers (quick matrix)
|
|
38
51
|
|
|
39
52
|
| Driver | Low → | High → pushes toward |
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Contract Testing — Verifying Integrations Without the Real Thing
|
|
2
|
+
|
|
3
|
+
How-to reference for testing the boundary between two services (or your app and a 3rd party) without standing up the real dependency in every test. Read when a component integrates with an external API/service you **can't run or seed in CI**. Pairs with `test-strategy.md` (its "contract tests where a 3rd-party can't be seeded" line).
|
|
4
|
+
|
|
5
|
+
## When to use it
|
|
6
|
+
|
|
7
|
+
- A consumer depends on a provider you can't run in CI (a partner API, another team's service, a paid 3rd party).
|
|
8
|
+
- You want to catch "the provider changed and broke us" **without** slow, flaky end-to-end tests against the real provider.
|
|
9
|
+
- Microservice boundaries: each side tested independently but kept compatible.
|
|
10
|
+
|
|
11
|
+
**Not** for: pure in-process logic (unit), or a dependency you *can* run real in CI — use an integration test with Testcontainers instead (see `test-containers.md`).
|
|
12
|
+
|
|
13
|
+
## Consumer-Driven Contracts (the dominant model — Pact-style)
|
|
14
|
+
|
|
15
|
+
The **consumer** defines what it needs from the provider as a contract (example request → expected response shape). Two halves:
|
|
16
|
+
|
|
17
|
+
1. **Consumer test** — runs the consumer against a *mock* provider that replays the contract. Proves the consumer works given that response shape, and **publishes** the contract.
|
|
18
|
+
2. **Provider verification** — the *real* provider is replayed the contract's requests and its actual responses are checked against the contract. Proves the provider still satisfies what the consumer needs.
|
|
19
|
+
|
|
20
|
+
A **broker** (Pact Broker / PactFlow) stores contracts and tracks which consumer/provider versions are compatible (`can-i-deploy`).
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// Consumer side (Pact JS) — declare the interaction, test the client against the mock
|
|
24
|
+
provider.addInteraction({
|
|
25
|
+
state: 'customer 42 exists',
|
|
26
|
+
uponReceiving: 'a request for customer 42',
|
|
27
|
+
withRequest: { method: 'GET', path: '/customers/42' },
|
|
28
|
+
willRespondWith: { status: 200, body: { id: 42, email: like('a@b.com') } }, // match shape, not exact value
|
|
29
|
+
});
|
|
30
|
+
// run your client against provider.mockService.baseUrl; assert it parses the response.
|
|
31
|
+
// → writes a pact file the provider must later verify.
|
|
32
|
+
```
|
|
33
|
+
```ts
|
|
34
|
+
// Provider side — replay the published pacts against the REAL provider
|
|
35
|
+
await new Verifier({
|
|
36
|
+
provider: 'customers-api',
|
|
37
|
+
pactBrokerUrl: process.env.PACT_BROKER_URL,
|
|
38
|
+
providerBaseUrl: 'http://localhost:8080',
|
|
39
|
+
stateHandlers: { 'customer 42 exists': () => seedCustomer(42) },
|
|
40
|
+
}).verifyProvider();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Contract vs integration vs e2e — when each
|
|
44
|
+
|
|
45
|
+
- **Integration (Testcontainers):** the dependency you *can* run real → test against it.
|
|
46
|
+
- **Contract:** the dependency you *can't* run/seed → test the boundary shape on both sides independently.
|
|
47
|
+
- **E2E:** a few critical journeys end-to-end (slow; keep lean — see `e2e-tiering.md`).
|
|
48
|
+
|
|
49
|
+
Contract testing replaces the temptation to mock the 3rd party in an integration test and call it covered — a mock proves nothing about the *real* provider; a **verified contract** does.
|
|
50
|
+
|
|
51
|
+
## Schema/spec-based alternative
|
|
52
|
+
|
|
53
|
+
When both sides share a spec (REST/gRPC/events) and you control them, schema-driven checks — OpenAPI/AsyncAPI validation, JSON Schema, protobuf backward-compat, Spring Cloud Contract — are a lighter alternative to full consumer-driven contracts.
|
|
54
|
+
|
|
55
|
+
## Anti-patterns
|
|
56
|
+
|
|
57
|
+
- Mocking a 3rd party in an integration test and believing it's covered (the mock can drift from reality; a verified contract can't).
|
|
58
|
+
- Asserting the **full** response (brittle) — assert only the fields the consumer uses (Pact matchers: `like`, `eachLike`, `term`).
|
|
59
|
+
- Publishing a consumer pact but never running **provider verification** (the pact alone proves nothing about the provider).
|
|
60
|
+
- Using contract tests where a real integration test (Testcontainers) would be cheaper and stronger.
|
|
61
|
+
|
|
62
|
+
*Sources: pact.io (consumer-driven contracts, broker, can-i-deploy); Fowler "ContractTest" / "IntegrationContractTest"; Spring Cloud Contract; OpenAPI/AsyncAPI schema validation.*
|
|
@@ -51,7 +51,25 @@ Only when the domain is non-trivial. A **Big-Picture event storming** pass surfa
|
|
|
51
51
|
2. For each: *who triggers it? who reacts? what decision follows?*
|
|
52
52
|
3. Group events by actor/responsibility → each cluster is a candidate **bounded context**.
|
|
53
53
|
|
|
54
|
-
Boundaries often fall where the **language changes** (the same word means different things) or where the **rate of change** differs. If boundaries are unclear, **defer** them — say so explicitly and let planning refine them.
|
|
54
|
+
Boundaries often fall where the **language changes** (the same word means different things) or where the **rate of change** differs. If boundaries are unclear, **defer** them — say so explicitly and let planning refine them.
|
|
55
|
+
|
|
56
|
+
A **Process-level** pass is the optional middle gear between Big-Picture and Design-level storming: take one important event flow (e.g., "Order → Payment → Fulfillment") and walk its commands, policies ("whenever X, then Y"), and read-models. It sharpens *one* boundary and its hand-offs without dropping to aggregates. Use it only when a single flow's boundary is genuinely contested; otherwise stay Big-Picture. Do **not** run design-level (aggregate-level) event storming here — that is tactical and belongs to a core subdomain you've already identified.
|
|
57
|
+
|
|
58
|
+
## 4. Context mapping (only when there are ≥2 bounded contexts)
|
|
59
|
+
|
|
60
|
+
Once two contexts exist, name the **relationship** at each boundary — it dictates integration and team coupling downstream. The vocabulary (pick the one that fits; don't apply all):
|
|
61
|
+
|
|
62
|
+
| Pattern | When it applies |
|
|
63
|
+
|---|---|
|
|
64
|
+
| **Shared Kernel** | two contexts share a small agreed model; changes need both teams' consent (high coupling — keep tiny) |
|
|
65
|
+
| **Customer/Supplier** | downstream's needs are honored in the upstream's roadmap (upstream agrees to flex) |
|
|
66
|
+
| **Conformist** | downstream just accepts the upstream model as-is (no leverage to negotiate) |
|
|
67
|
+
| **Anticorruption Layer (ACL)** | downstream translates the upstream model at the seam to protect its own — the safe default against a messy/legacy/3rd-party upstream |
|
|
68
|
+
| **Open Host Service (OHS)** | upstream publishes a well-defined protocol many consumers use |
|
|
69
|
+
| **Published Language** | a shared, well-documented interchange format (e.g. a schema/spec) the integration speaks |
|
|
70
|
+
| **Separate Ways** | the cheapest answer — don't integrate at all; duplicate the little that's needed |
|
|
71
|
+
|
|
72
|
+
Capture each as `Context A —[relationship]→ Context B`. The **ACL** here is the same tool architecture uses when later extracting a service — naming it now tells planning where a translation seam belongs. Strategic only: name the relationship, don't design the translator.
|
|
55
73
|
|
|
56
74
|
## What this skill does NOT do
|
|
57
75
|
|
|
@@ -65,6 +83,8 @@ Boundaries often fall where the **language changes** (the same word means differ
|
|
|
65
83
|
- → **`recommend-architecture`**: subdomain complexity drives the domain-logic axis (Transaction Script ↔ Domain Model ↔ Hexagonal). Core+complex ⇒ richer; supporting/generic ⇒ simple.
|
|
66
84
|
- → **`testing-strategy`**: where the behavior lives drives test shape — a rich core wants more unit tests; CRUD-over-DB edges want integration tests.
|
|
67
85
|
|
|
86
|
+
**Anemic vs rich is an architecture decision, deferred — but flag it.** Whether the core's logic lives *in* the domain objects (rich) or in services over data-bags (anemic) is decided by `recommend-architecture`, not here. But if distillation found a genuinely complex, differentiating core, note it for downstream: a thin/anemic model over that core is the classic under-engineering tell. Just flag the expectation ("core 'X' is rich → expect a Domain Model, not a service-over-DTOs"); don't design the aggregates.
|
|
87
|
+
|
|
68
88
|
## When to run / when to skip
|
|
69
89
|
|
|
70
90
|
**Run** after `/gsd:new-project`, before planning, when: the domain has real business rules, multiple stakeholder vocabularies, distinct business areas, or a genuine competitive core.
|
|
@@ -14,6 +14,8 @@ Reference for `/gsd:discover-product`. An **optional** front-of-funnel step that
|
|
|
14
14
|
- **Find the narrowest wedge:** the smallest version someone would pay for this week — the hair-on-fire segment.
|
|
15
15
|
- **Frame the vision as an opportunity/outcome** (it must admit >1 solution) so it informs but doesn't over-constrain architecture.
|
|
16
16
|
- **Cover the four risks** (Cagan): value, usability, feasibility, viability.
|
|
17
|
+
- **Make outcomes measurable** (Ulwick ODI): state desired outcomes as *direction + metric + object* — "minimize the time to reconcile an invoice" — not vague goals, so "is it working?" is answerable. When real users exist, rank candidate outcomes by importance × (dis)satisfaction to find the under-served one.
|
|
18
|
+
- **Discovery is a loop, not a gate** (Torres): the brief is a *hypothesis to keep testing*, not a verdict. Its open assumptions feed an ongoing cadence (revisit after a handful of customer conversations), organized as an opportunity → solution → assumption tree.
|
|
17
19
|
|
|
18
20
|
## The forcing posture
|
|
19
21
|
|
|
@@ -22,10 +24,10 @@ The first answer is polished — push 2–3 times with concrete specifics, not s
|
|
|
22
24
|
## Distilled question set (ordered; skip any block already evidenced)
|
|
23
25
|
|
|
24
26
|
0. **Frame:** what customer behavior/metric do we want to change (not a feature)? If we skipped discovery entirely, what assumption would we be betting the whole build on?
|
|
25
|
-
1. **Job & user:** who *specifically* — and for whom is the problem most acute, frequent, expensive, unavoidable? State the job solution-free. Job story: *"When [situation], I want to [motivation], so I can [outcome]."*
|
|
27
|
+
1. **Job & user:** who *specifically* — and for whom is the problem most acute, frequent, expensive, unavoidable? State the job solution-free. Job story: *"When [situation], I want to [motivation], so I can [outcome]."* Capture 2–3 **measurable desired-outcome statements** for the job (direction + metric + object, e.g. "reduce the time to find an open class slot") — these are what "better" is measured against. Note if the job-population is heterogeneous (different segments → different outcomes; don't average them away).
|
|
26
28
|
2. **Demand vs interest:** "Tell me about the *last time* you hit this." "What are you doing about it *today*?" "What does that workaround cost (time/money)?" "What *real* evidence exists — pre-pay, LOI, pilot, converted signups?" (Never "would you use X?")
|
|
27
29
|
3. **Wedge & under-served outcome:** which single opportunity, solved, most moves the outcome? The narrowest version that fully solves it for one user? Can we imagine >1 solution? (If no — we smuggled in a solution; re-frame.)
|
|
28
|
-
4. **Four risks** (only the unvalidated ones): **value** (evidence they'll choose this over the status quo), **usability** (where they'll get stuck), **feasibility** (riskiest technical unknown), **viability** (pricing/legal/sales/brand).
|
|
30
|
+
4. **Four risks** (only the unvalidated ones): **value** (evidence they'll choose this over the status quo), **usability** (where they'll get stuck), **feasibility** (riskiest technical unknown), **viability** (pricing/legal/sales/brand). First **enumerate the leap-of-faith assumptions** behind the chosen wedge (what must be true for it to work); then run the *cheapest test on the riskiest* one — not just a single test on the least-validated risk.
|
|
29
31
|
5. **Scope & prioritization:** end-to-end journey → the thin first slice (walking skeleton). RICE on the candidate list — Reach × Impact × Confidence ÷ Effort; table-stakes/dependencies legitimately override the score.
|
|
30
32
|
6. **Success:** how will we know it worked (the outcome metric, by when)? What would make ≥40% of target users "very disappointed" to lose it? (Sean Ellis PMF proxy — necessary, not sufficient; survey only users who used the core.)
|
|
31
33
|
|
|
@@ -38,7 +40,7 @@ The first answer is polished — push 2–3 times with concrete specifics, not s
|
|
|
38
40
|
## Handoff
|
|
39
41
|
|
|
40
42
|
- Produces `PRODUCT-BRIEF.md`: outcome statement, target user + wedge, demand evidence, job story, four-risks status, prioritized scope, explicit "not in scope."
|
|
41
|
-
- Feeds `PROJECT.md` (vision / JTBD / persona / metrics) and `model-domain` (the job + journey → domain events and subdomains).
|
|
43
|
+
- Feeds `PROJECT.md` (vision / JTBD / persona / metrics) and `model-domain` (the job + journey → domain events and subdomains). Discovery is collaborative, *continuous* work (Torres) — not a serial handoff or a one-time gate; keep the vision at the outcome level so the domain and architecture stay open. (Slicing the journey into releases downstream is **story mapping** — Patton.)
|
|
42
44
|
|
|
43
45
|
## Anti-patterns
|
|
44
46
|
|
|
@@ -64,6 +64,7 @@ When the strategy calls for real-dependency integration tests, auth, or e2e, loa
|
|
|
64
64
|
- `@~/.claude/gsd-core/references/auth-in-tests.md` — authenticate-once/storageState, token minting, multi-role, JWT vs cookie, one-account-per-worker.
|
|
65
65
|
- `@~/.claude/gsd-core/references/realistic-test-data.md` — synthetic factories by default; anonymized/subset dumps only.
|
|
66
66
|
- `@~/.claude/gsd-core/references/e2e-tiering.md` — persistent smoke vs transient e2e; keep e2e lean.
|
|
67
|
+
- `@~/.claude/gsd-core/references/contract-testing.md` — for an external dependency you can't run/seed in CI: consumer-driven contracts + provider verification (a verified contract, not a mock).
|
|
67
68
|
- `@~/.claude/gsd-core/references/flaky-test-checklist.md` — fixed clock, seeded RNG, poll-don't-sleep, per-worker isolation.
|
|
68
69
|
|
|
69
70
|
## Anti-patterns
|
|
@@ -40,9 +40,14 @@ Strategic classification — drives where to invest and (downstream) the archite
|
|
|
40
40
|
|---------|----------------------|-------------------|----------|-------------------|
|
|
41
41
|
| [Name] | [What it's responsible for] | [Events] | [Other contexts] | [Where its terms differ] |
|
|
42
42
|
|
|
43
|
-
**Context map (
|
|
43
|
+
**Context map (relationships — only with ≥2 contexts):** name each boundary's relationship (Shared Kernel / Customer-Supplier / Conformist / ACL / Open Host Service / Published Language / Separate Ways). Default to an ACL against a messy/legacy/3rd-party upstream. Relationship only — no translator design.
|
|
44
|
+
|
|
45
|
+
| From | Relationship | To | Note |
|
|
46
|
+
|------|-------------|----|------|
|
|
47
|
+
| [Context A] | [ACL / OHS / Shared Kernel / …] | [Context B] | [why this relationship] |
|
|
48
|
+
|
|
44
49
|
```
|
|
45
|
-
[ASCII sketch of context
|
|
50
|
+
[Optional ASCII sketch of the context map]
|
|
46
51
|
```
|
|
47
52
|
|
|
48
53
|
## Notes for downstream phases
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# Product Brief — [PROJECT_TITLE]
|
|
2
2
|
|
|
3
3
|
**Created:** [DATE] via `/gsd:discover-product`
|
|
4
|
-
**Scope:** Product definition (what/why) — feeds `PROJECT.md` and `/gsd:model-domain`. Outcome-framed so the domain and architecture stay open.
|
|
4
|
+
**Scope:** Product definition (what/why) — feeds `PROJECT.md` and `/gsd:model-domain`. Outcome-framed so the domain and architecture stay open. **This brief is a hypothesis, not a verdict** — its open assumptions feed an ongoing discovery cadence (Torres), not a one-time gate.
|
|
5
5
|
|
|
6
6
|
## Outcome (not output)
|
|
7
7
|
|
|
8
8
|
[The customer behavior or business metric we want to change — not a feature. e.g., "Shippers get a priced, matched carrier in seconds instead of days."]
|
|
9
9
|
|
|
10
|
+
## Desired outcomes (measurable — Ulwick ODI)
|
|
11
|
+
|
|
12
|
+
State 2–3 as *direction + metric + object* so "is it working?" is answerable. If the population is heterogeneous, list per-segment — don't average them away.
|
|
13
|
+
|
|
14
|
+
| Desired outcome (direction + metric + object) | For which segment | Under-served? (importance × dissatisfaction) |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| [e.g., "minimize the time to reconcile an invoice"] | [segment] | [high / unknown] |
|
|
17
|
+
|
|
10
18
|
## Target user & job
|
|
11
19
|
|
|
12
20
|
- **Specific user:** [the actual human/role for whom this is most acute/frequent/expensive]
|
|
@@ -49,7 +57,14 @@
|
|
|
49
57
|
## Handoff notes
|
|
50
58
|
|
|
51
59
|
- **For `model-domain`:** [the job + journey steps and the key domain nouns/events to model]
|
|
52
|
-
|
|
60
|
+
|
|
61
|
+
## Assumptions to re-test (leap-of-faith)
|
|
62
|
+
|
|
63
|
+
The brief is a hypothesis. List the assumptions that must hold for the wedge to work, riskiest first, each with the cheapest next test — revisit after a handful of customer conversations (continuous discovery, not a gate).
|
|
64
|
+
|
|
65
|
+
| Leap-of-faith assumption | Riskiest? | Cheapest next test |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| [what must be true] | [yes / no] | [the test] |
|
|
53
68
|
|
|
54
69
|
---
|
|
55
70
|
*Product brief. Next: `/gsd:new-project` (if not done) → `/gsd:model-domain` → `/gsd:recommend-architecture`.*
|
|
@@ -52,16 +52,16 @@ Write a minimal PRODUCT-BRIEF.md (outcome + the prioritized list + "discovery sk
|
|
|
52
52
|
Run the ordered question set from the reference. **Posture: the first answer is polished — push 2–3 times for concrete specifics (the actual human, the actual consequence), reflect back, confirm. One thread at a time.** Ask about the PAST, never hypotheticals. Skip any block already evidenced.
|
|
53
53
|
|
|
54
54
|
- **Step 3 — Frame (outcome):** "What customer behavior or metric do we want to change — not a feature?" "If we skipped discovery, what assumption would we be betting the whole build on?"
|
|
55
|
-
- **Step 4 — Job & user:** "Who *specifically* — and for whom is this most acute, frequent, expensive, unavoidable?" Capture the solution-free job and a job story ("When … I want to … so I can …"). If after 2–3 pushes the user still can't name a specific acute role (answers "everyone"/"all X"), do NOT record a generic user — record the target user as **UNRESOLVED** and make "identify the acute user" the first open question. A non-specific user is a discovery red flag, not a finding.
|
|
55
|
+
- **Step 4 — Job & user:** "Who *specifically* — and for whom is this most acute, frequent, expensive, unavoidable?" Capture the solution-free job and a job story ("When … I want to … so I can …"). Then capture **2–3 measurable desired outcomes** for the job as *direction + metric + object* ("reduce the time to find an open class slot") — these are what "better" is measured against later. If the job-population is heterogeneous, capture outcomes **per segment** (different segments want different things — don't average them away). If after 2–3 pushes the user still can't name a specific acute role (answers "everyone"/"all X"), do NOT record a generic user — record the target user as **UNRESOLVED** and make "identify the acute user" the first open question. A non-specific user is a discovery red flag, not a finding.
|
|
56
56
|
- **Step 5 — Demand vs interest:** "Tell me about the *last time* you hit this." "What are you doing about it *today*, and what does it cost?" "What *real* evidence exists — pre-pay, LOI, pilot, converted signups?" Mark each signal strong (behavior/money) vs weak (interest). **Never** ask hypotheticals — neither "would you use X?" nor "would you pay $Y?"; redirect any "they'd pay $Y" answer to "tell me about the last time someone actually paid for a workaround."
|
|
57
57
|
- **Step 6 — Wedge:** "Which single opportunity, solved, most moves the outcome? What's the narrowest version that fully solves it for one user this week?" Check: can we imagine >1 solution? (If no, we smuggled in a solution — re-frame.)
|
|
58
|
-
- **Step 7 — Four risks** (only the unvalidated): value / usability / feasibility / viability.
|
|
58
|
+
- **Step 7 — Four risks** (only the unvalidated): value / usability / feasibility / viability. First **enumerate the leap-of-faith assumptions** behind the chosen wedge (what must be true for it to work), order them by risk, and run the *cheapest test on the riskiest* — not just one test on the least-validated risk. Do not rely on the user's self-rating — if a risk is dismissed without evidence ("it's fine," "AI can do it"), treat it as **open**. Independently name any obvious risk the user omitted (e.g., legal/consent, data privacy, platform dependency) and mark it open with a test. Record the surviving assumptions in the brief's "Assumptions to re-test" table — the brief is a hypothesis to keep testing, not a verdict.
|
|
59
59
|
- **Step 8 — Scope & prioritization:** the end-to-end journey → the thin first slice; RICE the candidate list; record explicit "not in scope."
|
|
60
60
|
- **Step 9 — Success:** the **outcome metric** (a change in customer behavior or business result) + by when; the PMF check (what would make ≥40% of core users "very disappointed"). **Reject vanity/output metrics — signups, waitlist size, downloads, page views, "launched" — and push to the behavior/result they proxy for (retained paying users, task completion, % of the target behavior achieved). A user-count is an output unless tied to retained value.**
|
|
61
61
|
|
|
62
62
|
## Step 10: Write PRODUCT-BRIEF.md
|
|
63
63
|
|
|
64
|
-
Render `@~/.claude/gsd-core/templates/product-brief.md` (fill `[DATE]` with today's date, `[PROJECT_TITLE]` from PROJECT.md or — if none exists — a short **outcome-level** working title that does NOT encode the solution, marked "(working title)"). Keep the outcome at the behavior/metric level — **do not encode a solution or architecture**. Fill the Handoff notes for `model-domain` (the job + journey + key domain nouns).
|
|
64
|
+
Render `@~/.claude/gsd-core/templates/product-brief.md` (fill `[DATE]` with today's date, `[PROJECT_TITLE]` from PROJECT.md or — if none exists — a short **outcome-level** working title that does NOT encode the solution, marked "(working title)"). Keep the outcome at the behavior/metric level — **do not encode a solution or architecture**. Fill the **measurable desired-outcomes** table (per segment if heterogeneous), the **Assumptions to re-test** table (riskiest first, each with its cheapest next test), and the Handoff notes for `model-domain` (the job + journey + key domain nouns).
|
|
65
65
|
|
|
66
66
|
Write to `.planning/PRODUCT-BRIEF.md`.
|
|
67
67
|
|
|
@@ -32,6 +32,7 @@ Exit.
|
|
|
32
32
|
## Step 2: Load context (internal grounding — do not show the user yet)
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
+
cat .planning/PRODUCT-BRIEF.md 2>/dev/null || true
|
|
35
36
|
cat .planning/PROJECT.md 2>/dev/null || true
|
|
36
37
|
cat .planning/REQUIREMENTS.md 2>/dev/null || true
|
|
37
38
|
cat .planning/ROADMAP.md 2>/dev/null || true
|
|
@@ -107,6 +108,10 @@ If set, run a **Big-Picture** pass (timeline of events, not aggregates):
|
|
|
107
108
|
3. Group events by actor/responsibility. Each cluster = a candidate bounded context. Boundaries fall where the **language changes** or the **rate of change** differs.
|
|
108
109
|
4. If boundaries are unclear, say so and **defer** them. Do NOT drill into aggregates (that's tactical, out of scope here).
|
|
109
110
|
|
|
111
|
+
**Context mapping (only if you end with ≥2 contexts):** for each boundary, name the *relationship* using the reference's vocabulary (Shared Kernel / Customer-Supplier / Conformist / **ACL** / Open Host Service / Published Language / Separate Ways) and record it as `Context A —[relationship]→ Context B`. Default to an **ACL** at any seam against a messy, legacy, or third-party upstream. Name the relationship only — do not design the translator (that's tactical/architecture).
|
|
112
|
+
|
|
113
|
+
**Process-level pass (optional, only for one genuinely contested boundary):** walk a single flow's commands → policies ("whenever X, then Y") → read-models to sharpen that one boundary and its hand-offs. Don't drop to aggregates. Skip entirely if Big-Picture already settled the boundaries.
|
|
114
|
+
|
|
110
115
|
## Step 6: Write DOMAIN-MODEL.md
|
|
111
116
|
|
|
112
117
|
Render `@~/.claude/gsd-core/templates/domain-model.md` (fill `[DATE]` with today's date and `[PROJECT_TITLE]` from PROJECT.md), filling:
|
|
@@ -72,6 +72,8 @@ If **any** is "no" → **Modular Monolith. Say so and stop here on Axis B** (not
|
|
|
72
72
|
|
|
73
73
|
If all three pass, OR a single component looks special, run the **Hard-Parts scan** on that component: score the 6 disintegrators (low cohesion · divergent volatility · divergent scaling · fault isolation · differential security · independent extensibility) vs the 4 integrators (ACID across data · tight workflow · shared code · tight data relationships). Net disintegrators ≫ integrators → extract it; otherwise keep it modular. Warn explicitly against a **distributed monolith** (services that can't deploy independently).
|
|
74
74
|
|
|
75
|
+
When recommending the monolith (a gate failed), **record the promotion trigger** — the concrete future signal that would justify revisiting Axis B (a second team forms, a component's scaling diverges, a bounded context stabilizes). The monolith is **sacrificial/evolutionary**, not permanent: note that the eventual split, if it comes, uses **Strangler Fig + an Anti-Corruption Layer + data-decomposition-behind-module-boundaries-first (+ sagas/outbox for cross-service consistency)** — never a big-bang rewrite. Enforcing "no cross-module DB access" as a fitness function now is what makes that future split cheap. (See *Evolving the topology* in the reference.)
|
|
76
|
+
|
|
75
77
|
## Step 5: Over-/under-engineering check (the meta-tell)
|
|
76
78
|
|
|
77
79
|
Run this check in **both directions** — it is a first-class gate, not a formality:
|
package/package.json
CHANGED