projectstore-claude 0.30.10 → 0.30.11
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/node_modules/projectstore/.claude-plugin/marketplace.json +1 -1
- package/node_modules/projectstore/.claude-plugin/plugin.json +1 -1
- package/node_modules/projectstore/commands/story.md +20 -5
- package/node_modules/projectstore/docs/contracts-and-tests.md +74 -3
- package/node_modules/projectstore/package.json +1 -1
- package/node_modules/projectstore/scaffold/guides/story.test.md +34 -0
- package/node_modules/projectstore/scripts/doctor.mjs +42 -3
- package/node_modules/projectstore/templates/de/story.md.tmpl +1 -0
- package/node_modules/projectstore/templates/en/story.md.tmpl +1 -0
- package/node_modules/projectstore/templates/es/story.md.tmpl +1 -0
- package/node_modules/projectstore/templates/fr/story.md.tmpl +1 -0
- package/node_modules/projectstore/templates/ru/story.md.tmpl +1 -0
- package/node_modules/projectstore/templates/zh/story.md.tmpl +1 -0
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"name": "projectstore",
|
|
13
13
|
"displayName": "projectstore",
|
|
14
14
|
"description": "📚 Your agent runs the project through a verified loop: task → artifact (ADR · spec · epic · story) → adversarial critic → backlog → planner → reviewer → done. Plain markdown in an Obsidian-friendly vault, every write approved by you — and any model can pick the project up tomorrow.",
|
|
15
|
-
"version": "0.30.
|
|
15
|
+
"version": "0.30.11",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "Evgenii Konev",
|
|
18
18
|
"email": "ekonev@smartandpoint.com",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectstore",
|
|
3
3
|
"displayName": "projectstore",
|
|
4
|
-
"version": "0.30.
|
|
4
|
+
"version": "0.30.11",
|
|
5
5
|
"description": "Your agent runs the project through a verified loop: task → artifact (ADR / spec / epic / story) → adversarial critic → backlog → planner → reviewer → done. Plain markdown in git — any model can pick the project up tomorrow.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Evgenii Konev @ SmartAndPoint",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Create a new story inside an existing epic, or run its lifecycle gates (plan / close).
|
|
3
|
-
argument-hint: <epic-id> <title> [--spec SPEC-ID] | plan <story> | close <story>
|
|
3
|
+
argument-hint: <epic-id> <title> [--spec SPEC-ID] [--variant test] | plan <story> | close <story>
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
You are managing a story: creating one, or running its lifecycle gates.
|
|
@@ -18,12 +18,21 @@ the two cannot collide).
|
|
|
18
18
|
|
|
19
19
|
2. **Validate args**: epic-id (positional 1) + title (rest). If only one word, ask for the title. An optional `--spec SPEC-ID` names the covering spec — put it into the rendered draft's `specs:` list (inline flow: `specs: ["SPEC-001"]`). Under `spec_policy: required` (vault's `.projectstore.json`), remind that a story needs a covering spec before implementation starts, or a `no_spec` reason declared at the plan gate.
|
|
20
20
|
|
|
21
|
+
An optional `--variant test` makes a **test story**: one that plans the
|
|
22
|
+
tests for a spec's contracts, written to the `story.test` guide. Only
|
|
23
|
+
`test` is defined: refuse another value and name `test`. Remove `--variant test` and `--spec SPEC-ID` with its
|
|
24
|
+
value from the arguments before any `draft.mjs` call, in step 3 and in the
|
|
25
|
+
re-run of step 6: the script reads every remaining word as the title, so a
|
|
26
|
+
flag left in would end up in the title and the slug.
|
|
27
|
+
|
|
21
28
|
2a. **Read the guide** for this kind before writing anything:
|
|
22
29
|
|
|
23
30
|
```bash
|
|
24
31
|
node "${CLAUDE_PLUGIN_ROOT}/bin/projectstore.mjs" guide story --project "${CLAUDE_PROJECT_DIR}"
|
|
25
32
|
```
|
|
26
33
|
|
|
34
|
+
For a test story read `guide story.test` instead, in the same form.
|
|
35
|
+
|
|
27
36
|
Write the draft to its `## Must`, `## Common mistakes`, `## Do not write`
|
|
28
37
|
and `## Length`: the critic checks the same `## Must` items. Exit 1 means
|
|
29
38
|
this kind has no guide; say so and go on.
|
|
@@ -31,10 +40,15 @@ the two cannot collide).
|
|
|
31
40
|
3. **Render draft**:
|
|
32
41
|
|
|
33
42
|
```bash
|
|
34
|
-
node "${CLAUDE_PLUGIN_ROOT}/scripts/draft.mjs" story "
|
|
43
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/draft.mjs" story <epic-id> "<title>"
|
|
35
44
|
```
|
|
36
45
|
|
|
37
|
-
The
|
|
46
|
+
The epic id and the title only, with the flags of step 2 removed. The
|
|
47
|
+
script fails if the epic folder does not exist. Surface the error and suggest `/projectstore:epic <id> "<title>"` first.
|
|
48
|
+
|
|
49
|
+
3a. **For a test story**, replace the draft's commented `# variant: test …`
|
|
50
|
+
line with `variant: test` before the preview, the way `--spec` fills
|
|
51
|
+
`specs:`. The critic then reviews the story against `story.test`.
|
|
38
52
|
|
|
39
53
|
4. **Preview**: path + first ~25 lines.
|
|
40
54
|
|
|
@@ -42,9 +56,10 @@ the two cannot collide).
|
|
|
42
56
|
|
|
43
57
|
When prompting "Edit", note that the story template has a `Decomposition` checklist — if the user wants to seed it with concrete tasks from the current conversation, regenerate with those tasks pre-filled in place of the empty checkboxes.
|
|
44
58
|
|
|
45
|
-
6. **Post-approval race re-check** (Layer 1): re-run `draft.mjs story "
|
|
59
|
+
6. **Post-approval race re-check** (Layer 1): re-run `draft.mjs story <epic-id> "<title>"` (the same stripped arguments as step 3) and re-read its `collision` field — an exact-name `test -e` cannot see normalized cross-era collisions (`story-006-foo.md` vs `story-foo.md`). If `collision` is non-null, surface it as a topic collision (`"<identity>" already exists as <with>`), and ask: extend the existing story, pick a different slug (`-2` is a deliberate distinct identity), or cancel. Render `warnings` entries as `⚠️` lines in the preview too.
|
|
46
60
|
|
|
47
|
-
7. **On Yes** (path free): Write file
|
|
61
|
+
7. **On Yes** (path free): Write the file you previewed, with the step-2 and
|
|
62
|
+
step-3a edits (`specs:`, `variant: test`), not the re-run's raw draft.
|
|
48
63
|
|
|
49
64
|
8. **Suggest next**: "Now decompose the work in the `Decomposition` section, or run `/projectstore:kanban` to refresh the board. Before implementation: `/projectstore:story plan <story>`."
|
|
50
65
|
|
|
@@ -4,7 +4,10 @@ A spec's behavioral contracts are numbered and testable. This page shows how
|
|
|
4
4
|
to write a contract so a test follows from it, how a test cites the contract
|
|
5
5
|
it covers, and what doctor reports. It describes Part A of the ADR *Spec
|
|
6
6
|
policy, revised: contract scenarios and test citations that doctor counts,
|
|
7
|
-
and a declared per-story opt-out from a spec
|
|
7
|
+
and a declared per-story opt-out from a spec*, and decisions 6 and 7 of the
|
|
8
|
+
ADR *One authoring guide per artifact kind, read by the author before the
|
|
9
|
+
draft and by the critic after, overridable from the vault* (edge and failure
|
|
10
|
+
marks, and test stories).
|
|
8
11
|
|
|
9
12
|
All of it is optional. A spec without scenarios, and a test without a
|
|
10
13
|
citation, stay valid.
|
|
@@ -31,8 +34,75 @@ The items are a list, so Markdown keeps one step per line. The keywords stay
|
|
|
31
34
|
in English in every language, like `— stories:`: a tool matches them
|
|
32
35
|
literally.
|
|
33
36
|
|
|
34
|
-
Doctor reads scenarios for
|
|
35
|
-
`WHEN` (or `GIVEN`), or no `THEN`, it reports an info.
|
|
37
|
+
Doctor reads scenarios for two things. If a contract's scenario has no
|
|
38
|
+
`WHEN` (or `GIVEN`), or no `THEN`, it reports an info. And it counts the
|
|
39
|
+
scenarios marked as an edge or a failure case, below.
|
|
40
|
+
|
|
41
|
+
An item may wrap: a line indented deeper than its dash, before a blank line,
|
|
42
|
+
belongs to the item above it.
|
|
43
|
+
|
|
44
|
+
## Marking an edge or a failure case
|
|
45
|
+
|
|
46
|
+
A scenario's `WHEN` or `GIVEN` item may end with `(edge)` or `(failure)`:
|
|
47
|
+
|
|
48
|
+
```markdown
|
|
49
|
+
3. **A stale index is reported.** Doctor compares each folder index with the
|
|
50
|
+
artifacts on disk and reports a row that no longer matches.
|
|
51
|
+
- GIVEN an index row whose status differs from the artifact's frontmatter
|
|
52
|
+
- WHEN doctor runs with --vault
|
|
53
|
+
- THEN it reports an index finding that names the folder
|
|
54
|
+
- GIVEN a folder whose README is missing (edge)
|
|
55
|
+
- WHEN doctor runs with --vault
|
|
56
|
+
- THEN it reports the missing index, and nothing else in that folder
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`(edge)` is for a case that comes from the input or the state: boundary
|
|
60
|
+
values, invalid input, a state transition, an upgrade from an older state.
|
|
61
|
+
`(failure)` is for a case where something else fails: a dependency, a retry
|
|
62
|
+
or a concurrent run, a partial failure, a rollback. A happy-path scenario
|
|
63
|
+
has no mark.
|
|
64
|
+
|
|
65
|
+
The mark is lowercase English in ASCII parentheses, at the very end of the
|
|
66
|
+
item (on its last line when the item wraps). A mark on an `AND` or `THEN`
|
|
67
|
+
item, in the contract's prose, inside backticks, or followed by more text
|
|
68
|
+
does not count.
|
|
69
|
+
|
|
70
|
+
For each active spec, doctor lists the contracts that carry scenarios and
|
|
71
|
+
have none marked: their scenarios show only the happy path. It counts the
|
|
72
|
+
marks and does not judge them. Whether a marked scenario is a real edge case
|
|
73
|
+
is the critic's question.
|
|
74
|
+
|
|
75
|
+
## Writing a test story
|
|
76
|
+
|
|
77
|
+
A test story plans the tests for one or more contracts. Create it with the
|
|
78
|
+
variant flag, and its draft is written to the test lead's guide, `story.test`:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
/projectstore:story PS-X "Tests for the stale-index contract" --variant test
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The guide asks the story to name the contracts it covers by token, to list
|
|
85
|
+
the cases that apply to each (boundary values, invalid input, state
|
|
86
|
+
transitions, failure of a dependency, retry and concurrency, partial failure,
|
|
87
|
+
upgrade from an older state, rollback), to say which cases it leaves untested
|
|
88
|
+
and why, and to require a citation on every test it adds. For example:
|
|
89
|
+
|
|
90
|
+
```markdown
|
|
91
|
+
## Description
|
|
92
|
+
|
|
93
|
+
Covers [example#3]. The scenario shows the happy path; the cases below are
|
|
94
|
+
the ones a production vault hits.
|
|
95
|
+
|
|
96
|
+
## Decomposition
|
|
97
|
+
|
|
98
|
+
- [ ] Boundary: an index with no rows, and one with a row per artifact.
|
|
99
|
+
- [ ] Invalid input: a row whose link points at no file.
|
|
100
|
+
- [ ] Partial failure: one folder's README unreadable, the others fine.
|
|
101
|
+
- [ ] Not tested: concurrency. Doctor is read-only, so two runs cannot race.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The story has `variant: test` in its frontmatter and follows every other
|
|
105
|
+
story rule: the plan and close gates, evidence, the board.
|
|
36
106
|
|
|
37
107
|
## A contract no test can cover
|
|
38
108
|
|
|
@@ -97,6 +167,7 @@ skipped. Tests outside the bound project, or in a submodule, are not seen.
|
|
|
97
167
|
| `citation-unknown` | warn | A token whose key is no spec's id or alias, or whose number is not a contract of that spec |
|
|
98
168
|
| `citation-superseded` | info | A token that cites a superseded spec |
|
|
99
169
|
| `scenario-shape` | info | A scenario without a WHEN/GIVEN or a THEN |
|
|
170
|
+
| `contract-happy-path-only` | info | One line per active spec: the contracts that carry scenarios and have none marked `(edge)` or `(failure)` |
|
|
100
171
|
| `spec-alias` | issue | A duplicate alias, an alias equal to another spec's id, or the reserved `example` |
|
|
101
172
|
| `evidence-citation` | warn | Under `lifecycle_gates: on`: a story's evidence names a token that no file in the project carries |
|
|
102
173
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectstore",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.11",
|
|
4
4
|
"description": "Your agent runs the project through a verified loop: task → artifact (ADR / spec / epic / story) → adversarial critic → backlog → planner → reviewer → done. Plain markdown in git — any model can pick the project up tomorrow.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"project-management",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
guide_for: story.test
|
|
3
|
+
guide_version: 1
|
|
4
|
+
default_review: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Lens
|
|
8
|
+
|
|
9
|
+
The test lead who reads a contract and asks what will break it in production. The scenarios show the happy path; this story covers the cases around it.
|
|
10
|
+
|
|
11
|
+
## Must
|
|
12
|
+
|
|
13
|
+
- Names the spec contracts it covers by citation token.
|
|
14
|
+
- Lists, for each contract, the cases that apply out of: boundary values, invalid input, state transitions, failure of a dependency, retry and concurrency, partial failure, upgrade from an older state, rollback.
|
|
15
|
+
- States which cases it deliberately leaves untested, and why.
|
|
16
|
+
- Requires every test it adds to cite the contract it covers.
|
|
17
|
+
|
|
18
|
+
## Common mistakes
|
|
19
|
+
|
|
20
|
+
- Restating the contract's scenarios as the test list. Add the cases they miss, beyond any already marked `(edge)` or `(failure)`.
|
|
21
|
+
- All eight cases under every contract. Keep the ones this contract can meet, and name the ones you dropped.
|
|
22
|
+
- A case with no observable outcome ("handles errors"). Name what is asserted: an exit code, a finding, a file left byte-identical.
|
|
23
|
+
- A citation token for a real key in prose or an example. Use the reserved key, as in `[example#3]`.
|
|
24
|
+
- Acceptance criteria that repeat the decomposition ("write the tests") instead of an outcome a reviewer can run.
|
|
25
|
+
|
|
26
|
+
## Do not write
|
|
27
|
+
|
|
28
|
+
- Test code or assertions: the story names the cases, the tests hold the asserts.
|
|
29
|
+
- A covered contract restated in full: cite it by token.
|
|
30
|
+
- A case for behaviour no covered contract promises.
|
|
31
|
+
|
|
32
|
+
## Length
|
|
33
|
+
|
|
34
|
+
A short description; one case list per covered contract, at most four contracts; four to ten criteria, each tied to a case; the plan a thin route of at most 40 lines.
|
|
@@ -2698,6 +2698,9 @@ export function parseSpecContracts(spec) {
|
|
|
2698
2698
|
const contracts = [];
|
|
2699
2699
|
let cur = null;
|
|
2700
2700
|
let inFirstPara = false;
|
|
2701
|
+
// The scenario item a continuation line belongs to (SPEC-001 contract 13):
|
|
2702
|
+
// a line indented deeper than its dash, not a bullet, before a blank line.
|
|
2703
|
+
let open = null;
|
|
2701
2704
|
for (const line of lines) {
|
|
2702
2705
|
const top = line.match(/^(\d+[′″']*)\.(\s|$)/);
|
|
2703
2706
|
if (top) {
|
|
@@ -2707,15 +2710,18 @@ export function parseSpecContracts(spec) {
|
|
|
2707
2710
|
stepIndent: null,
|
|
2708
2711
|
scenarios: [],
|
|
2709
2712
|
marker: null,
|
|
2713
|
+
marks: [],
|
|
2710
2714
|
first: line,
|
|
2711
2715
|
};
|
|
2712
2716
|
contracts.push(cur);
|
|
2713
2717
|
inFirstPara = true;
|
|
2718
|
+
open = null;
|
|
2714
2719
|
continue;
|
|
2715
2720
|
}
|
|
2716
2721
|
if (!cur) continue;
|
|
2717
2722
|
if (/^\s*$/.test(line)) {
|
|
2718
2723
|
inFirstPara = false;
|
|
2724
|
+
open = null;
|
|
2719
2725
|
continue;
|
|
2720
2726
|
}
|
|
2721
2727
|
if (inFirstPara) cur.first += " " + line.trim();
|
|
@@ -2723,14 +2729,24 @@ export function parseSpecContracts(spec) {
|
|
|
2723
2729
|
if (step) {
|
|
2724
2730
|
if (cur.stepIndent === null) cur.stepIndent = step[1].length;
|
|
2725
2731
|
if (step[1].length === cur.stepIndent) cur.steps.add(contractNumberAscii(step[2]));
|
|
2732
|
+
open = null;
|
|
2726
2733
|
continue;
|
|
2727
2734
|
}
|
|
2728
|
-
const sc = line.match(
|
|
2729
|
-
if (sc)
|
|
2735
|
+
const sc = line.match(/^(\s*)-\s+(WHEN|GIVEN|AND|THEN)\b/);
|
|
2736
|
+
if (sc) {
|
|
2737
|
+
cur.scenarios.push(line.trim());
|
|
2738
|
+
open = { i: cur.scenarios.length - 1, indent: sc[1].length };
|
|
2739
|
+
continue;
|
|
2740
|
+
}
|
|
2741
|
+
if (open && line.match(/^\s*/)[0].length > open.indent && !/^\s*[-*+]\s/.test(line))
|
|
2742
|
+
cur.scenarios[open.i] += " " + line.trim();
|
|
2743
|
+
else open = null;
|
|
2730
2744
|
}
|
|
2731
2745
|
for (const c of contracts) {
|
|
2732
2746
|
const mk = c.first.match(/\(verified\s*[::]\s*(live|manual)\)/);
|
|
2733
2747
|
c.marker = mk ? mk[1] : null;
|
|
2748
|
+
// An (edge) or (failure) mark closes a WHEN or GIVEN item; nowhere else counts.
|
|
2749
|
+
c.marks = c.scenarios.map((l) => /^-\s+(WHEN|GIVEN)\b.*\((edge|failure)\)\s*$/.exec(l)?.[2]).filter(Boolean);
|
|
2734
2750
|
delete c.first;
|
|
2735
2751
|
delete c.stepIndent;
|
|
2736
2752
|
}
|
|
@@ -2808,8 +2824,30 @@ export function checkSpecAliases(artifacts) {
|
|
|
2808
2824
|
return out;
|
|
2809
2825
|
}
|
|
2810
2826
|
|
|
2827
|
+
// Happy path only (SPEC-001 contract 13; the authoring-guides ADR, decision
|
|
2828
|
+
// 7): the contracts of an active spec that carry scenarios and have none
|
|
2829
|
+
// marked (edge) or (failure). Counted, not judged; no git.
|
|
2830
|
+
export function checkHappyPathOnly(artifacts) {
|
|
2831
|
+
const out = [];
|
|
2832
|
+
for (const s of artifacts.filter((a) => a.kind === "spec" && String(a.fm.status || "") === "active")) {
|
|
2833
|
+
const withScenarios = (parseSpecContracts(s) || []).filter((c) => c.scenarios.length);
|
|
2834
|
+
const bare = withScenarios.filter((c) => !c.marks.length).map((c) => c.n);
|
|
2835
|
+
if (!bare.length) continue;
|
|
2836
|
+
out.push(
|
|
2837
|
+
finding(
|
|
2838
|
+
"vault",
|
|
2839
|
+
"info",
|
|
2840
|
+
"contract-happy-path-only",
|
|
2841
|
+
`${bare.length} of ${withScenarios.length} contracts with scenarios have no scenario marked (edge) or (failure): ${bare.join(", ")}.`,
|
|
2842
|
+
s.rel,
|
|
2843
|
+
),
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2846
|
+
return out;
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2811
2849
|
// Scenario shape (Decision 3): a contract's scenario items need a WHEN (or
|
|
2812
|
-
// GIVEN) and a THEN.
|
|
2850
|
+
// GIVEN) and a THEN.
|
|
2813
2851
|
export function checkScenarioShape(artifacts) {
|
|
2814
2852
|
const out = [];
|
|
2815
2853
|
for (const s of artifacts.filter((a) => a.kind === "spec")) {
|
|
@@ -3850,6 +3888,7 @@ export function runVaultChecks(
|
|
|
3850
3888
|
() => checkLifecycleGates(artifacts, vaultCfg),
|
|
3851
3889
|
() => checkSpecAliases(artifacts),
|
|
3852
3890
|
() => checkScenarioShape(artifacts),
|
|
3891
|
+
() => checkHappyPathOnly(artifacts),
|
|
3853
3892
|
() => {
|
|
3854
3893
|
const scan = scanCitations(cfg, artifacts, proj, grep);
|
|
3855
3894
|
return [...checkContractCitations(artifacts, scan), ...checkEvidenceCitations(artifacts, vaultCfg, scan)];
|
|
@@ -13,6 +13,7 @@ tags: {{tags}}
|
|
|
13
13
|
code_refs: []
|
|
14
14
|
specs: []
|
|
15
15
|
# no_spec: "<por qué esta historia no necesita spec>" (se escribe en el plan gate)
|
|
16
|
+
# variant: test (una historia de pruebas; su guía es story.test)
|
|
16
17
|
started_at: null
|
|
17
18
|
closed_at: null
|
|
18
19
|
plan_updated_at: null
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectstore-claude",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.11",
|
|
4
4
|
"description": "projectstore for Claude Code: the core pinned and bundled, the harness fixed. A release artifact; the way in is npx projectstore setup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"projectstore",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"README.md"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"projectstore": "=0.30.
|
|
40
|
+
"projectstore": "=0.30.11"
|
|
41
41
|
},
|
|
42
42
|
"bundleDependencies": [
|
|
43
43
|
"projectstore"
|