@open-product-primer/cli 2.2.0 → 2.4.0
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/dist/commands/doctor.js +1 -1
- package/dist/commands/init.js +2 -2
- package/dist/commands/ovw.js +1 -1
- package/dist/commands/validate.js +2 -2
- package/dist/lib/measure.js +1 -3
- package/dist/lib/validate-checks.js +4 -4
- package/dist/workflows/archive.inline.md +4 -4
- package/dist/workflows/archive.template.md +14 -11
- package/dist/workflows/bet.cursor-command.md +1 -1
- package/dist/workflows/bet.inline.md +4 -4
- package/dist/workflows/bet.schema.yaml +1 -1
- package/dist/workflows/bet.template.md +5 -5
- package/dist/workflows/criteria.cursor-command.md +1 -1
- package/dist/workflows/criteria.inline.md +1 -1
- package/dist/workflows/criteria.template.md +2 -2
- package/dist/workflows/note.template.md +1 -1
- package/dist/workflows/promote.native.template.md +6 -6
- package/dist/workflows/promote.none.template.md +3 -3
- package/dist/workflows/promote.openspec.template.md +4 -4
- package/dist/workflows/review.cursor-command.md +1 -1
- package/dist/workflows/review.inline.md +1 -1
- package/dist/workflows/review.template.md +2 -2
- package/dist/workflows/spec-authoring.schema.yaml +1 -1
- package/dist/workflows/spec-authoring.template.md +20 -6
- package/dist/workflows/tutorial.schema.yaml +13 -0
- package/dist/workflows/tutorial.template.md +45 -0
- package/package.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -231,7 +231,7 @@ function doctorCommand() {
|
|
|
231
231
|
required: false,
|
|
232
232
|
});
|
|
233
233
|
// ── Discovery checks — warn if bet is missing discovery.md ───────────────
|
|
234
|
-
const betsDir = path.join(primerDir, 'bets');
|
|
234
|
+
const betsDir = path.join(primerDir, 'bets', 'pending');
|
|
235
235
|
if (fs.existsSync(betsDir)) {
|
|
236
236
|
const betEntries = fs.readdirSync(betsDir, { withFileTypes: true });
|
|
237
237
|
for (const entry of betEntries) {
|
package/dist/commands/init.js
CHANGED
|
@@ -64,7 +64,7 @@ function initCommand() {
|
|
|
64
64
|
const specFramework = await (0, install_agent_1.promptFrameworkSelection)(projectRoot);
|
|
65
65
|
const primerDir = path.join(projectRoot, 'oprim');
|
|
66
66
|
(0, scaffold_1.ensureDir)(path.join(primerDir, 'decisions'));
|
|
67
|
-
(0, scaffold_1.ensureDir)(path.join(primerDir, 'bets'));
|
|
67
|
+
(0, scaffold_1.ensureDir)(path.join(primerDir, 'bets', 'pending'));
|
|
68
68
|
(0, scaffold_1.ensureDir)(path.join(primerDir, 'reviews'));
|
|
69
69
|
(0, scaffold_1.ensureDir)(path.join(primerDir, 'notes'));
|
|
70
70
|
(0, scaffold_1.ensureDir)(path.join(primerDir, 'templates'));
|
|
@@ -94,7 +94,7 @@ function initCommand() {
|
|
|
94
94
|
(0, scaffold_1.writeFile)(path.join(primerDir, 'index.md'), (0, templates_1.indexTemplate)(projectName));
|
|
95
95
|
}
|
|
96
96
|
(0, scaffold_1.writeFileIfAbsent)(path.join(primerDir, 'decisions', '.gitkeep'), '');
|
|
97
|
-
(0, scaffold_1.writeFileIfAbsent)(path.join(primerDir, 'bets', '.gitkeep'), '');
|
|
97
|
+
(0, scaffold_1.writeFileIfAbsent)(path.join(primerDir, 'bets', 'pending', '.gitkeep'), '');
|
|
98
98
|
(0, scaffold_1.writeFileIfAbsent)(path.join(primerDir, 'reviews', '.gitkeep'), '');
|
|
99
99
|
(0, scaffold_1.writeFileIfAbsent)(path.join(primerDir, 'notes', '.gitkeep'), '');
|
|
100
100
|
console.log('\n' + chalk_1.default.green('✓') + ' oprim/ workspace created');
|
package/dist/commands/ovw.js
CHANGED
|
@@ -228,7 +228,7 @@ function ovwCommand() {
|
|
|
228
228
|
.action(() => {
|
|
229
229
|
const projectRoot = process.cwd();
|
|
230
230
|
const sequencePath = path.join(projectRoot, 'oprim', 'sequence.yaml');
|
|
231
|
-
const betsDir = path.join(projectRoot, 'oprim', 'bets');
|
|
231
|
+
const betsDir = path.join(projectRoot, 'oprim', 'bets', 'pending');
|
|
232
232
|
if (!fs.existsSync(sequencePath)) {
|
|
233
233
|
console.error("No oprim/sequence.yaml found — run 'oprim init' first");
|
|
234
234
|
process.exit(1);
|
|
@@ -65,10 +65,10 @@ function printReport(checks) {
|
|
|
65
65
|
console.log(`\n${passed}/${checks.length} checks passed.`);
|
|
66
66
|
}
|
|
67
67
|
function runDiff(projectRoot, betIdInput) {
|
|
68
|
-
const betsDir = path.join(projectRoot, 'oprim', 'bets');
|
|
68
|
+
const betsDir = path.join(projectRoot, 'oprim', 'bets', 'pending');
|
|
69
69
|
const resolvedDir = (0, spec_delta_1.resolveBetDirectory)(betsDir, betIdInput);
|
|
70
70
|
if (!resolvedDir) {
|
|
71
|
-
console.error(chalk_1.default.red(`Bet ${betIdInput} was not found in oprim/bets/.`));
|
|
71
|
+
console.error(chalk_1.default.red(`Bet ${betIdInput} was not found in oprim/bets/pending/.`));
|
|
72
72
|
process.exitCode = 1;
|
|
73
73
|
return;
|
|
74
74
|
}
|
package/dist/lib/measure.js
CHANGED
|
@@ -230,12 +230,10 @@ async function runBigQueryMetric(sqlPath) {
|
|
|
230
230
|
}
|
|
231
231
|
// ─── Criteria scanner (used by doctor) ───────────────────────────────────────
|
|
232
232
|
function scanCriteriaForSourceType(projectRoot, sourceType) {
|
|
233
|
-
const betsDir = path.join(projectRoot, 'oprim', 'bets');
|
|
233
|
+
const betsDir = path.join(projectRoot, 'oprim', 'bets', 'pending');
|
|
234
234
|
if (!fs.existsSync(betsDir))
|
|
235
235
|
return false;
|
|
236
236
|
for (const entry of fs.readdirSync(betsDir)) {
|
|
237
|
-
if (entry === 'archived')
|
|
238
|
-
continue;
|
|
239
237
|
const criteriaPath = path.join(betsDir, entry, 'criteria.yaml');
|
|
240
238
|
if (!fs.existsSync(criteriaPath))
|
|
241
239
|
continue;
|
|
@@ -73,7 +73,7 @@ function isPromoted(betDecisionContent) {
|
|
|
73
73
|
* change) that has no criteria.yaml alongside it. Un-promoted bets are not checked.
|
|
74
74
|
*/
|
|
75
75
|
function checkBetDefinitionOfDone(projectRoot, checks) {
|
|
76
|
-
const betsDir = path.join(projectRoot, 'oprim', 'bets');
|
|
76
|
+
const betsDir = path.join(projectRoot, 'oprim', 'bets', 'pending');
|
|
77
77
|
for (const entry of activeBetDirs(betsDir)) {
|
|
78
78
|
const betDir = path.join(betsDir, entry.name);
|
|
79
79
|
const decisionPath = path.join(betDir, 'bet-decision.md');
|
|
@@ -88,7 +88,7 @@ function checkBetDefinitionOfDone(projectRoot, checks) {
|
|
|
88
88
|
checks.push({
|
|
89
89
|
name: `bet: ${betId} promoted without criteria.yaml`,
|
|
90
90
|
pass: false,
|
|
91
|
-
note: `Add criteria.yaml to oprim/bets/${entry.name}/, or run the oprim-criteria skill`,
|
|
91
|
+
note: `Add criteria.yaml to oprim/bets/pending/${entry.name}/, or run the oprim-criteria skill`,
|
|
92
92
|
required: false,
|
|
93
93
|
});
|
|
94
94
|
}
|
|
@@ -99,7 +99,7 @@ function checkBetDefinitionOfDone(projectRoot, checks) {
|
|
|
99
99
|
* longer text-matches (whitespace-insensitive) current truth.
|
|
100
100
|
*/
|
|
101
101
|
function checkSpecDeltaDrift(projectRoot, checks) {
|
|
102
|
-
const betsDir = path.join(projectRoot, 'oprim', 'bets');
|
|
102
|
+
const betsDir = path.join(projectRoot, 'oprim', 'bets', 'pending');
|
|
103
103
|
for (const entry of activeBetDirs(betsDir)) {
|
|
104
104
|
const betId = extractBetId(entry.name);
|
|
105
105
|
const specsDir = path.join(betsDir, entry.name, 'specs');
|
|
@@ -133,7 +133,7 @@ function checkSpecDeltaDrift(projectRoot, checks) {
|
|
|
133
133
|
}
|
|
134
134
|
/** Surfaces overlapping requirement headers across active bets' spec deltas. */
|
|
135
135
|
function checkCrossBetConflicts(projectRoot, checks) {
|
|
136
|
-
const betsDir = path.join(projectRoot, 'oprim', 'bets');
|
|
136
|
+
const betsDir = path.join(projectRoot, 'oprim', 'bets', 'pending');
|
|
137
137
|
for (const conflict of (0, spec_delta_1.findCrossBetConflicts)(betsDir)) {
|
|
138
138
|
checks.push({
|
|
139
139
|
name: `spec-delta: ${extractBetId(conflict.betA)} and ${extractBetId(conflict.betB)} both touch "${conflict.header}" in ${conflict.capability}`,
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Archive a completed bet.
|
|
3
3
|
|
|
4
4
|
1. Ask for bet ID (accept bet-005, 005, 5, BET-005 — normalize to BET-NNN).
|
|
5
|
-
2. Verify `oprim/bets/BET-NNN/` exists.
|
|
6
|
-
3. Check `oprim/sequence.yaml` for entries where `blocked_by` or `unlocks` reference the target bet — warn if found. Also check other active bet dirs for delta specs against the same requirement (matching `### Requirement:` headers, whitespace-insensitive) — warn if an overlap is found. Ask "Archive anyway? (y/N)" if
|
|
7
|
-
4. If `oprim/bets/BET-NNN/specs/` exists, fold each capability's `## ADDED`/`## MODIFIED`/`## REMOVED Requirements` delta into `oprim/specs/<capability>/spec.md` (matching by `### Requirement:` header; create the current-truth file if the delta is entirely ADDED) — last-write-wins on overlaps, no 3-way merge. Skip this step entirely if no `specs/` dir is present.
|
|
8
|
-
5. Move directory: `oprim/bets/BET-NNN → oprim/bets/archived/BET-NNN`.
|
|
5
|
+
2. Verify `oprim/bets/pending/BET-NNN/` exists.
|
|
6
|
+
3. Check `oprim/sequence.yaml` for entries where `blocked_by` or `unlocks` reference the target bet — warn if found. Also check other active bet dirs for delta specs against the same requirement (matching `### Requirement:` headers, whitespace-insensitive) — warn if an overlap is found. Also check the bet's `tasks.md` (if present) for unchecked `- [ ]` items — warn with the count if any remain. Ask "Archive anyway? (y/N)" if any warning fires.
|
|
7
|
+
4. If `oprim/bets/pending/BET-NNN/specs/` exists, fold each capability's `## ADDED`/`## MODIFIED`/`## REMOVED Requirements` delta into `oprim/specs/<capability>/spec.md` (matching by `### Requirement:` header; create the current-truth file if the delta is entirely ADDED) — last-write-wins on overlaps, no 3-way merge. Skip this step entirely if no `specs/` dir is present.
|
|
8
|
+
5. Move directory: `oprim/bets/pending/BET-NNN → oprim/bets/archived/BET-NNN`.
|
|
9
9
|
6. Remove the bet entry from `oprim/sequence.yaml`.
|
|
10
10
|
7. Report what was done.
|
|
@@ -19,8 +19,8 @@ Normalize the input: accept `bet-005`, `005`, `5`, or `BET-005` — always treat
|
|
|
19
19
|
|
|
20
20
|
### 2. Resolve the bet directory
|
|
21
21
|
|
|
22
|
-
Look for the bet directory in `oprim/bets/` using two patterns:
|
|
23
|
-
1. Exact match: `oprim/bets/BET-NNN/` (legacy non-slug format)
|
|
22
|
+
Look for the bet directory in `oprim/bets/pending/` using two patterns:
|
|
23
|
+
1. Exact match: `oprim/bets/pending/BET-NNN/` (legacy non-slug format)
|
|
24
24
|
2. Slug variant: any directory starting with `BET-NNN-` (e.g., `BET-NNN-<slug>/`)
|
|
25
25
|
|
|
26
26
|
Use whichever pattern matches. Call this the **resolved directory name**.
|
|
@@ -30,17 +30,19 @@ If multiple directories match (e.g., both `BET-NNN/` and `BET-NNN-slug/` exist):
|
|
|
30
30
|
- Stop.
|
|
31
31
|
|
|
32
32
|
If neither pattern matches:
|
|
33
|
-
- Report: "Bet BET-NNN was not found in oprim/bets/. Nothing was changed."
|
|
33
|
+
- Report: "Bet BET-NNN was not found in oprim/bets/pending/. Nothing was changed."
|
|
34
34
|
- Stop.
|
|
35
35
|
|
|
36
|
-
### 3. Check for active dependencies
|
|
36
|
+
### 3. Check for active dependencies, concurrent spec-delta conflicts, and incomplete tasks.md
|
|
37
37
|
|
|
38
38
|
Read `oprim/sequence.yaml`. Scan every entry across all buckets (now, next, later, backlog) for any entry whose `blocked_by` or `unlocks` list contains the target bet ID.
|
|
39
39
|
|
|
40
|
-
Separately, if `oprim/bets/<resolved-dir>/specs/` exists: for each `<capability>/spec.md` delta file under it, extract every `### Requirement:` header from its `## ADDED`/`## MODIFIED`/`## REMOVED Requirements` sections. Then scan every other bet directory directly under `oprim/bets/` (excluding
|
|
40
|
+
Separately, if `oprim/bets/pending/<resolved-dir>/specs/` exists: for each `<capability>/spec.md` delta file under it, extract every `### Requirement:` header from its `## ADDED`/`## MODIFIED`/`## REMOVED Requirements` sections. Then scan every other bet directory directly under `oprim/bets/pending/` (excluding the bet being archived) for a `specs/<capability>/spec.md` file for the same capability; if one exists, extract its `### Requirement:` headers too. Flag any header that matches (whitespace-insensitive) between the archiving bet's delta and another still-active bet's delta as an **overlap**.
|
|
41
41
|
|
|
42
|
-
If
|
|
43
|
-
|
|
42
|
+
Separately, if `oprim/bets/pending/<resolved-dir>/tasks.md` exists: count the number of unchecked `- [ ]` items in it. If one or more remain, flag this as an **incomplete-tasks warning**. If `tasks.md` doesn't exist, or every item is checked (`- [x]`), this contributes nothing to the warning.
|
|
43
|
+
|
|
44
|
+
If any of sequence.yaml dependents, delta overlaps, or an incomplete tasks.md are found:
|
|
45
|
+
- Show a combined warning listing each dependent entry, each overlapping requirement, and (if applicable) the incomplete-tasks count.
|
|
44
46
|
|
|
45
47
|
Example:
|
|
46
48
|
```
|
|
@@ -48,18 +50,19 @@ If either sequence.yaml dependents or delta overlaps are found:
|
|
|
48
50
|
- BET-007 (blocked_by: [BET-005])
|
|
49
51
|
- BET-008 (unlocks: [BET-005])
|
|
50
52
|
⚠ Warning: BET-005's delta for requirement "The system SHALL ..." in capability foo overlaps with active bet BET-009's delta for the same requirement. Archiving BET-005 now applies its version to oprim/specs/foo/spec.md; if BET-009 archives later, its version will overwrite this requirement again (last-write-wins — no 3-way merge is attempted).
|
|
53
|
+
⚠ Warning: BET-005's tasks.md has 3 unchecked item(s) — implementation may be incomplete.
|
|
51
54
|
```
|
|
52
55
|
- Ask: "Archive BET-NNN anyway? (y/N)"
|
|
53
56
|
- If "n" or Enter: stop, no changes made.
|
|
54
57
|
- If "y": proceed.
|
|
55
58
|
|
|
56
|
-
If
|
|
59
|
+
If none are found: proceed without warning.
|
|
57
60
|
|
|
58
61
|
### 4. Fold spec deltas into current truth
|
|
59
62
|
|
|
60
|
-
If `oprim/bets/<resolved-dir>/specs/` does not exist: skip this step entirely and go to Step 5 — archive behavior is unchanged from before spec deltas existed.
|
|
63
|
+
If `oprim/bets/pending/<resolved-dir>/specs/` does not exist: skip this step entirely and go to Step 5 — archive behavior is unchanged from before spec deltas existed.
|
|
61
64
|
|
|
62
|
-
Otherwise, for each capability subdirectory under `oprim/bets/<resolved-dir>/specs/` containing a `spec.md`:
|
|
65
|
+
Otherwise, for each capability subdirectory under `oprim/bets/pending/<resolved-dir>/specs/` containing a `spec.md`:
|
|
63
66
|
|
|
64
67
|
1. Read the delta file's `## ADDED Requirements` / `## MODIFIED Requirements` / `## REMOVED Requirements` sections. Each `### Requirement:` block runs from its header through its body and any `#### Scenario:` sub-entries, up to the next `### Requirement:` or `## ` header.
|
|
65
68
|
2. Read `oprim/specs/<capability>/spec.md` if it exists (current truth uses a single flat `## Requirements` section).
|
|
@@ -85,7 +88,7 @@ mkdir -p oprim/bets/archived
|
|
|
85
88
|
|
|
86
89
|
Move the resolved directory:
|
|
87
90
|
```bash
|
|
88
|
-
mv oprim/bets/<resolved-dir> oprim/bets/archived/<resolved-dir>
|
|
91
|
+
mv oprim/bets/pending/<resolved-dir> oprim/bets/archived/<resolved-dir>
|
|
89
92
|
```
|
|
90
93
|
|
|
91
94
|
### 6. Remove the bet entry from sequence.yaml
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Create a new bet in `oprim/bets/`. First explain: "A bet is a product decision you're committing to explore — a problem worth solving, a hypothesis worth testing, or a direction worth taking. You'll name it, explain why now, and set a kill criterion." Then show: "Naming tip: verb + object [for context] — Good: 'Improve bet naming for scannability' / Bad: 'Naming'". Scan `BET-(\d+)` dirs for next ID (zero-padded, default 001). Check `oprim/sequence.yaml` exists (stop if not — advise oprim init). Read `oprim/config.yaml`'s `rules.bet` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged. After receiving the title, validate: if fewer than 4 words OR fewer than 25 characters, warn "this title may be too vague", suggest a reformulation, and ask "Proceed anyway? (y/N)" — if "n", prompt for a revised title. Gather: decision (default Build now), owner, review date, why-now, alternatives, expected outcomes, kill criteria, PDR links. Write `oprim/bets/BET-NNN/bet-decision.md` with an inline naming tip comment in the header. Append entry to sequence.yaml backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`. Then ask: "Do you want to scaffold a discovery.md now? (y/N)" — if "y", write `oprim/bets/BET-NNN/discovery.md` from the discovery template (sections: Problem Framing, User Research Signals, Competitive Context, Open Questions); if "n" or Enter, skip silently. Report what was created.
|
|
1
|
+
Create a new bet in `oprim/bets/pending/`. First explain: "A bet is a product decision you're committing to explore — a problem worth solving, a hypothesis worth testing, or a direction worth taking. You'll name it, explain why now, and set a kill criterion." Then show: "Naming tip: verb + object [for context] — Good: 'Improve bet naming for scannability' / Bad: 'Naming'". Scan `oprim/bets/pending/BET-(\d+)` and `oprim/bets/archived/BET-(\d+)` dirs for next ID (zero-padded, default 001). Check `oprim/sequence.yaml` exists (stop if not — advise oprim init). Read `oprim/config.yaml`'s `rules.bet` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged. After receiving the title, validate: if fewer than 4 words OR fewer than 25 characters, warn "this title may be too vague", suggest a reformulation, and ask "Proceed anyway? (y/N)" — if "n", prompt for a revised title. Gather: decision (default Build now), owner, review date, why-now, alternatives, expected outcomes, kill criteria, PDR links. Write `oprim/bets/pending/BET-NNN/bet-decision.md` with an inline naming tip comment in the header. Append entry to sequence.yaml backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`. Then ask: "Do you want to scaffold a discovery.md now? (y/N)" — if "y", write `oprim/bets/pending/BET-NNN/discovery.md` from the discovery template (sections: Problem Framing, User Research Signals, Competitive Context, Open Questions); if "n" or Enter, skip silently. Report what was created.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
### Bet authoring (oprim-bet)
|
|
2
|
-
Create a new bet in `oprim/bets/` and register it on the sequencing board.
|
|
2
|
+
Create a new bet in `oprim/bets/pending/` and register it on the sequencing board.
|
|
3
3
|
|
|
4
4
|
1. Show naming tip: "verb + object [for context] — e.g. 'Improve bet naming for scannability'"
|
|
5
5
|
2. Ask for the bet title. Validate: fewer than 4 words OR fewer than 25 chars → warn, suggest reformulation, ask "Proceed anyway? (y/N)".
|
|
6
|
-
3. Assign next BET ID: scan `oprim/bets/BET-(\d+)` dirs, max+1 zero-padded to 3 digits (default 001).
|
|
6
|
+
3. Assign next BET ID: scan `oprim/bets/pending/BET-(\d+)` and `oprim/bets/archived/BET-(\d+)` dirs, max+1 zero-padded to 3 digits (default 001).
|
|
7
7
|
4. Check `oprim/sequence.yaml` exists — stop if not, advise `oprim init`.
|
|
8
8
|
4b. Read `oprim/config.yaml`'s `rules.bet` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged.
|
|
9
9
|
5. Gather: decision (default Build now), owner, review date (YYYY-MM-DD), why now, alternatives, expected outcomes, kill criteria, PDR links.
|
|
10
|
-
6. Write `oprim/bets/BET-NNN/bet-decision.md` with all fields.
|
|
10
|
+
6. Write `oprim/bets/pending/BET-NNN/bet-decision.md` with all fields.
|
|
11
11
|
7. Append to `oprim/sequence.yaml` backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`.
|
|
12
|
-
8. Ask: "Scaffold a discovery.md now? (y/N)" — if "y", write `oprim/bets/BET-NNN/discovery.md`.
|
|
12
|
+
8. Ask: "Scaffold a discovery.md now? (y/N)" — if "y", write `oprim/bets/pending/BET-NNN/discovery.md`.
|
|
13
13
|
9. Report what was created.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
id: bet
|
|
2
2
|
skillName: oprim-bet
|
|
3
3
|
title: null
|
|
4
|
-
description: Create a new bet directory and bet-decision artifact in oprim/bets/, and add the bet to oprim/sequence.yaml backlog
|
|
4
|
+
description: Create a new bet directory and bet-decision artifact in oprim/bets/pending/, and add the bet to oprim/sequence.yaml backlog
|
|
5
5
|
cursorDescription: Create a new bet decision and register it on the sequencing board
|
|
6
6
|
claude:
|
|
7
7
|
skill: true
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oprim-bet
|
|
3
|
-
description: Create a new bet directory and bet-decision artifact in oprim/bets/, and add the bet to oprim/sequence.yaml backlog
|
|
3
|
+
description: Create a new bet directory and bet-decision artifact in oprim/bets/pending/, and add the bet to oprim/sequence.yaml backlog
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Create a new bet in `oprim/bets/` and register it on the sequencing board.
|
|
6
|
+
Create a new bet in `oprim/bets/pending/` and register it on the sequencing board.
|
|
7
7
|
|
|
8
8
|
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
9
9
|
|
|
@@ -30,7 +30,7 @@ After receiving the title, validate: if fewer than 4 words OR fewer than 25 char
|
|
|
30
30
|
- If "y": proceed with the original title
|
|
31
31
|
|
|
32
32
|
### 2. Assign the next BET ID
|
|
33
|
-
Scan both `oprim/bets/` and `oprim/bets/archived/` for directories whose names match `BET-(\d+)(-[^/]*)?` (handles both `BET-NNN/` and `BET-NNN-<slug>/`). Extract the numeric part from each match. Assign max+1, zero-padded to 3 digits. Default `001` if none found in either location.
|
|
33
|
+
Scan both `oprim/bets/pending/` and `oprim/bets/archived/` for directories whose names match `BET-(\d+)(-[^/]*)?` (handles both `BET-NNN/` and `BET-NNN-<slug>/`). Extract the numeric part from each match. Assign max+1, zero-padded to 3 digits. Default `001` if none found in either location.
|
|
34
34
|
|
|
35
35
|
### 2b. Derive the slug
|
|
36
36
|
From the bet title: lowercase all characters, replace any character that is not a letter or digit with a hyphen, collapse consecutive hyphens to one, strip leading/trailing hyphens, truncate to 40 characters at the last hyphen boundary. This becomes `<slug>`. Example: "Add title slugs to bet directories for scannability" → `add-title-slugs-to-bet-dirs-for`.
|
|
@@ -57,7 +57,7 @@ Then ask about each of the four risk dimensions (Low / Medium / High + short rat
|
|
|
57
57
|
Read `oprim/templates/bet-decision.md`. If it begins with a YAML frontmatter block (`---` ... `---`), this workspace has OKF frontmatter enabled. Ask for a one-line description and comma-separated tags (subject-area keywords). Prepare a frontmatter block with `type: bet-decision`, `title: <title>`, `description: <description>`, `tags: [<tags>]`, `timestamp: <today's date, ISO 8601>`, to prepend in step 5.
|
|
58
58
|
If no frontmatter block is found in the template, skip this step — write the file with no frontmatter, matching current behavior.
|
|
59
59
|
|
|
60
|
-
### 5. Write oprim/bets/BET-NNN-<slug>/bet-decision.md
|
|
60
|
+
### 5. Write oprim/bets/pending/BET-NNN-<slug>/bet-decision.md
|
|
61
61
|
Prepend the frontmatter block from step 4b, if one was prepared.
|
|
62
62
|
```
|
|
63
63
|
# Decision: BET-NNN <title>
|
|
@@ -108,7 +108,7 @@ Read → parse YAML → append → write back (2-space indentation):
|
|
|
108
108
|
|
|
109
109
|
### 7. Prompt for optional discovery scaffolding
|
|
110
110
|
Ask: "Do you want to scaffold a discovery.md now? (y/N)"
|
|
111
|
-
- If "y": write `oprim/bets/BET-NNN-<slug>/discovery.md` from the discovery template (same structure as `oprim/templates/discovery.md`).
|
|
111
|
+
- If "y": write `oprim/bets/pending/BET-NNN-<slug>/discovery.md` from the discovery template (same structure as `oprim/templates/discovery.md`).
|
|
112
112
|
- If "n" or Enter: skip silently.
|
|
113
113
|
|
|
114
114
|
### 8. Report what was created
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Add metrics to `oprim/bets/BET-NNN/criteria.yaml`. Verify bet dir exists. Gather: metric ID, name, baseline, target, timeframe, launch date, segment. Ask source type (amplitude or bigquery). Amplitude: event, aggregation, denominator_event. BigQuery: table, metric_column, filter, aggregation, denominator_query. If file exists: append to metrics list (never overwrite). If not: create. Ask if adding more metrics. Report what was created.
|
|
1
|
+
Add metrics to `oprim/bets/pending/BET-NNN/criteria.yaml`. Verify bet dir exists. Gather: metric ID, name, baseline, target, timeframe, launch date, segment. Ask source type (amplitude or bigquery). Amplitude: event, aggregation, denominator_event. BigQuery: table, metric_column, filter, aggregation, denominator_query. If file exists: append to metrics list (never overwrite). If not: create. Ask if adding more metrics. Report what was created.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
### Criteria authoring (oprim-criteria)
|
|
2
|
-
Create or append to `oprim/bets/BET-NNN/criteria.yaml`.
|
|
2
|
+
Create or append to `oprim/bets/pending/BET-NNN/criteria.yaml`.
|
|
3
3
|
|
|
4
4
|
1. Ask which bet (e.g. BET-042). Verify dir exists.
|
|
5
5
|
2. Gather: metric ID (snake_case), name, baseline, target, timeframe, launch date, segment.
|
|
@@ -3,7 +3,7 @@ name: oprim-criteria
|
|
|
3
3
|
description: Create or append to a criteria.yaml contract for a bet, with structured Amplitude and BigQuery source mapping
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Create or append to `oprim/bets/BET-NNN/criteria.yaml`.
|
|
6
|
+
Create or append to `oprim/bets/pending/BET-NNN/criteria.yaml`.
|
|
7
7
|
|
|
8
8
|
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ Create or append to `oprim/bets/BET-NNN/criteria.yaml`.
|
|
|
13
13
|
If not provided, ask: "Which bet are you adding criteria for? (e.g. BET-042)"
|
|
14
14
|
|
|
15
15
|
### 2. Verify bet exists
|
|
16
|
-
If `oprim/bets/BET-NNN/` not found: report and stop — advise using the `oprim-bet` skill first.
|
|
16
|
+
If `oprim/bets/pending/BET-NNN/` not found: report and stop — advise using the `oprim-bet` skill first.
|
|
17
17
|
|
|
18
18
|
### 3. Gather metric details
|
|
19
19
|
Ask: metric ID (snake_case), metric name, baseline (numeric), target (numeric), timeframe, launch date (YYYY-MM-DD or TBD), segment (optional).
|
|
@@ -61,6 +61,6 @@ timestamp: <today, ISO 8601>
|
|
|
61
61
|
OKF tier: same as above, with `description: "<description>"` inserted immediately after `title`.
|
|
62
62
|
|
|
63
63
|
### 8. Link back from referenced bets
|
|
64
|
-
For each BET-ID gathered in step 5: read `oprim/bets/BET-NNN/bet-decision.md`, and add `- Notes: NOTE-NNN` under its `## Links` section (append to an existing `Notes:` line, or add a new one).
|
|
64
|
+
For each BET-ID gathered in step 5: read `oprim/bets/pending/BET-NNN/bet-decision.md`, and add `- Notes: NOTE-NNN` under its `## Links` section (append to an existing `Notes:` line, or add a new one).
|
|
65
65
|
|
|
66
66
|
### 9. Report what was created
|
|
@@ -10,20 +10,20 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
10
10
|
|
|
11
11
|
## A. Bet → native oprim spec
|
|
12
12
|
|
|
13
|
-
1. **Locate the bet** — read `oprim/bets/BET-XXX/bet-decision.md`
|
|
13
|
+
1. **Locate the bet** — read `oprim/bets/pending/BET-XXX/bet-decision.md`
|
|
14
14
|
2. **Validate status** — decision must be "Build now"
|
|
15
15
|
3. **Check authority boundary** — confirm primer artifact owns why/order/outcome only
|
|
16
|
-
4. **Generate the native spec delta(s)** — for each capability listed under the bet's `## Capabilities` section (or a single capability derived from the bet title if none is listed), invoke the `oprim-spec` skill with this bet as context to write `oprim/bets/BET-XXX/specs/<capability>/spec.md` — a delta using `## ADDED`/`## MODIFIED`/`## REMOVED Requirements` headers reflecting the bet's why/outcome. No OpenSpec change directory is created and OpenSpec need not be installed. Nothing is written to `oprim/specs/<capability>/spec.md` (current truth) at promote time — that only happens when this bet is archived.
|
|
17
|
-
5. **Link artifacts** — add `- Spec (delta): oprim/bets/BET-XXX/specs/<capability>/spec.md` (one line per capability) to the bet-decision `## Links` section
|
|
18
|
-
6. **Copy criteria** — if `oprim/bets/BET-XXX/criteria.yaml` exists, note it alongside the spec link
|
|
16
|
+
4. **Generate the native spec delta(s)** — for each capability listed under the bet's `## Capabilities` section (or a single capability derived from the bet title if none is listed), invoke the `oprim-spec` skill with this bet as context to write `oprim/bets/pending/BET-XXX/specs/<capability>/spec.md` — a delta using `## ADDED`/`## MODIFIED`/`## REMOVED Requirements` headers reflecting the bet's why/outcome. No OpenSpec change directory is created and OpenSpec need not be installed. Nothing is written to `oprim/specs/<capability>/spec.md` (current truth) at promote time — that only happens when this bet is archived.
|
|
17
|
+
5. **Link artifacts** — add `- Spec (delta): oprim/bets/pending/BET-XXX/specs/<capability>/spec.md` (one line per capability) to the bet-decision `## Links` section
|
|
18
|
+
6. **Copy criteria** — if `oprim/bets/pending/BET-XXX/criteria.yaml` exists, note it alongside the spec link
|
|
19
19
|
7. **Report** — show what was created and linked, and note that merge-on-archive will fold the delta into `oprim/specs/` when the bet archives
|
|
20
20
|
|
|
21
21
|
## B. Note → Bet
|
|
22
22
|
|
|
23
23
|
1. **Locate the note** — read `oprim/notes/NOTE-XXX-<slug>.md`
|
|
24
|
-
2. **Assign the next BET ID** — scan both `oprim/bets/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
24
|
+
2. **Assign the next BET ID** — scan both `oprim/bets/pending/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
25
25
|
3. **Derive the slug** from the note's title (lowercase, non-alphanumeric → hyphen, collapse/trim hyphens, truncate to 40 chars at a hyphen boundary)
|
|
26
|
-
4. **Draft the bet** — write `oprim/bets/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
|
|
26
|
+
4. **Draft the bet** — write `oprim/bets/pending/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
|
|
27
27
|
5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`
|
|
28
28
|
6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
|
|
29
29
|
7. **Report** — show the new bet's path and flag that `Alternatives considered`, `Expected outcomes`, and `Kill criteria` still need authoring before this bet can itself be promoted
|
|
@@ -10,16 +10,16 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
10
10
|
|
|
11
11
|
## A. Bet → spec (no framework configured)
|
|
12
12
|
|
|
13
|
-
1. **Locate the bet** — read `oprim/bets/BET-XXX/bet-decision.md`
|
|
13
|
+
1. **Locate the bet** — read `oprim/bets/pending/BET-XXX/bet-decision.md`
|
|
14
14
|
2. **Validate status** — decision must be "Build now"
|
|
15
15
|
3. **Report and stop** — no speccing framework is configured (`integrations.spec_framework: none`). Add `- Spec: none (no speccing framework configured)` to the bet-decision `## Links` section. No spec artifact is created.
|
|
16
16
|
|
|
17
17
|
## B. Note → Bet
|
|
18
18
|
|
|
19
19
|
1. **Locate the note** — read `oprim/notes/NOTE-XXX-<slug>.md`
|
|
20
|
-
2. **Assign the next BET ID** — scan both `oprim/bets/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
20
|
+
2. **Assign the next BET ID** — scan both `oprim/bets/pending/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
21
21
|
3. **Derive the slug** from the note's title (lowercase, non-alphanumeric → hyphen, collapse/trim hyphens, truncate to 40 chars at a hyphen boundary)
|
|
22
|
-
4. **Draft the bet** — write `oprim/bets/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
|
|
22
|
+
4. **Draft the bet** — write `oprim/bets/pending/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
|
|
23
23
|
5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`
|
|
24
24
|
6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
|
|
25
25
|
7. **Report** — show the new bet's path and flag that `Alternatives considered`, `Expected outcomes`, and `Kill criteria` still need authoring before this bet can itself be promoted
|
|
@@ -10,7 +10,7 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
10
10
|
|
|
11
11
|
## A. Bet → OpenSpec change
|
|
12
12
|
|
|
13
|
-
1. **Locate the bet** — read `oprim/bets/BET-XXX/bet-decision.md`
|
|
13
|
+
1. **Locate the bet** — read `oprim/bets/pending/BET-XXX/bet-decision.md`
|
|
14
14
|
2. **Validate status** — decision must be "Build now"
|
|
15
15
|
3. **Check authority boundary** — confirm primer artifact owns why/order/outcome only
|
|
16
16
|
4. **Create OpenSpec change** — derive the change name as `bet-NNN-<slug>` where `NNN` is the zero-padded bet number (e.g. BET-004 → `bet-004`) and `<slug>` is a short kebab-case summary of the change. Then invoke the `/openspec-propose` skill (or `/opsx:propose`) with that name to create the change directory with **all required artifacts**: `proposal.md`, `design.md`, `tasks.md`, and `specs/<capability>/spec.md` for every capability listed under `## Capabilities`.
|
|
@@ -20,7 +20,7 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
20
20
|
5. **Link artifacts**:
|
|
21
21
|
- Add OpenSpec change path to bet-decision `## Links` section
|
|
22
22
|
- Add bet ID to OpenSpec proposal `## Context` section
|
|
23
|
-
6. **Copy criteria** — if `oprim/bets/BET-XXX/criteria.yaml` exists, link it from OpenSpec proposal
|
|
23
|
+
6. **Copy criteria** — if `oprim/bets/pending/BET-XXX/criteria.yaml` exists, link it from OpenSpec proposal
|
|
24
24
|
7. **Verify completeness** — confirm the change directory contains:
|
|
25
25
|
- `proposal.md`
|
|
26
26
|
- `design.md`
|
|
@@ -32,9 +32,9 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
32
32
|
## B. Note → Bet
|
|
33
33
|
|
|
34
34
|
1. **Locate the note** — read `oprim/notes/NOTE-XXX-<slug>.md`
|
|
35
|
-
2. **Assign the next BET ID** — scan both `oprim/bets/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
35
|
+
2. **Assign the next BET ID** — scan both `oprim/bets/pending/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
36
36
|
3. **Derive the slug** from the note's title (lowercase, non-alphanumeric → hyphen, collapse/trim hyphens, truncate to 40 chars at a hyphen boundary)
|
|
37
|
-
4. **Draft the bet** — write `oprim/bets/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
|
|
37
|
+
4. **Draft the bet** — write `oprim/bets/pending/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
|
|
38
38
|
5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`
|
|
39
39
|
6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
|
|
40
40
|
7. **Report** — show the new bet's path and flag that `Alternatives considered`, `Expected outcomes`, and `Kill criteria` still need authoring before this bet can itself be promoted
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Create KPI review in `oprim/reviews/YYYY-MM-DD-BET-NNN-kpi.md`. Read `oprim/config.yaml`'s `rules.review` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged. Read `criteria.yaml` for pre-fill (baseline/target). Check `oprim/bets/BET-NNN/measurements/` for `run-*.yaml` files — if found, use the most recent to pre-populate actuals and status (include "Actuals from run: YYYY-MM-DD" note). If no run result, ask for each metric's actual value. Status: actual >= target → hit, actual < target → missed, not provided → pending. Ask reviewer name and decision quality notes. Write review with metric table and Actions checklist. Report what was created.
|
|
1
|
+
Create KPI review in `oprim/reviews/YYYY-MM-DD-BET-NNN-kpi.md`. Read `oprim/config.yaml`'s `rules.review` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged. Read `criteria.yaml` for pre-fill (baseline/target). Check `oprim/bets/pending/BET-NNN/measurements/` for `run-*.yaml` files — if found, use the most recent to pre-populate actuals and status (include "Actuals from run: YYYY-MM-DD" note). If no run result, ask for each metric's actual value. Status: actual >= target → hit, actual < target → missed, not provided → pending. Ask reviewer name and decision quality notes. Write review with metric table and Actions checklist. Report what was created.
|
|
@@ -3,7 +3,7 @@ Create a KPI review artifact in `oprim/reviews/`.
|
|
|
3
3
|
|
|
4
4
|
1. Ask which bet (e.g. BET-042).
|
|
5
5
|
1b. Read `oprim/config.yaml`'s `rules.review` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged.
|
|
6
|
-
2. Read `oprim/bets/BET-NNN/criteria.yaml` for pre-fill. Check `oprim/bets/BET-NNN/measurements/` for `run-*.yaml` — use most recent if present.
|
|
6
|
+
2. Read `oprim/bets/pending/BET-NNN/criteria.yaml` for pre-fill. Check `oprim/bets/pending/BET-NNN/measurements/` for `run-*.yaml` — use most recent if present.
|
|
7
7
|
3. If no run result, ask for each metric's actual value.
|
|
8
8
|
4. Status: actual >= target → hit; actual < target → missed; not provided → pending.
|
|
9
9
|
5. Ask reviewer name and decision quality notes.
|
|
@@ -17,10 +17,10 @@ Read `oprim/config.yaml`. If it has a non-empty `rules.review` value, treat it a
|
|
|
17
17
|
|
|
18
18
|
### 2. Load criteria and check for a run result
|
|
19
19
|
|
|
20
|
-
Read `oprim/bets/BET-NNN/criteria.yaml` if it exists (pre-fills baseline and target).
|
|
20
|
+
Read `oprim/bets/pending/BET-NNN/criteria.yaml` if it exists (pre-fills baseline and target).
|
|
21
21
|
If not found: inform user and continue with empty metrics list.
|
|
22
22
|
|
|
23
|
-
**Check for measurement run result:** Scan `oprim/bets/BET-NNN/measurements/` for files matching `run-*.yaml`. If any exist, sort by filename (date-based) and read the most recent.
|
|
23
|
+
**Check for measurement run result:** Scan `oprim/bets/pending/BET-NNN/measurements/` for files matching `run-*.yaml`. If any exist, sort by filename (date-based) and read the most recent.
|
|
24
24
|
|
|
25
25
|
**If a run result exists:** use it to pre-populate actuals and status for every metric. Skip step 3 for those metrics. Note the run date — include "Actuals from run: YYYY-MM-DD" in the review artifact.
|
|
26
26
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
id: spec-authoring
|
|
2
2
|
skillName: oprim-spec
|
|
3
3
|
title: null
|
|
4
|
-
description: Generate a native oprim capability spec delta at oprim/bets/BET-NNN-<slug>/specs/<capability>/spec.md while a bet is active, in RFC 2119 (SHALL/SHOULD/MAY) requirements and Gherkin scenarios — folded into oprim/specs/<capability>/spec.md (current truth) when the bet is archived
|
|
4
|
+
description: Generate a native oprim capability spec delta at oprim/bets/pending/BET-NNN-<slug>/specs/<capability>/spec.md while a bet is active, in RFC 2119 (SHALL/SHOULD/MAY) requirements and Gherkin scenarios — folded into oprim/specs/<capability>/spec.md (current truth) when the bet is archived. On the first invocation for a bet, also generates design.md and tasks.md alongside the spec delta.
|
|
5
5
|
claude:
|
|
6
6
|
skill: true
|
|
7
7
|
command: null
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oprim-spec
|
|
3
|
-
description: Generate a native oprim capability spec delta at oprim/bets/BET-NNN-<slug>/specs/<capability>/spec.md while a bet is active, in RFC 2119 (SHALL/SHOULD/MAY) requirements and Gherkin scenarios — folded into oprim/specs/<capability>/spec.md (current truth) when the bet is archived
|
|
3
|
+
description: Generate a native oprim capability spec delta at oprim/bets/pending/BET-NNN-<slug>/specs/<capability>/spec.md while a bet is active, in RFC 2119 (SHALL/SHOULD/MAY) requirements and Gherkin scenarios — folded into oprim/specs/<capability>/spec.md (current truth) when the bet is archived. On the first invocation for a bet, also generates design.md and tasks.md alongside the spec delta.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Generate a capability spec delta for an active bet — RFC 2119 requirements plus Gherkin scenarios, no OpenSpec required. This skill never writes to `oprim/specs/` directly; `oprim-archive` folds the delta into current truth when the bet is archived.
|
|
@@ -12,14 +12,14 @@ Generate a capability spec delta for an active bet — RFC 2119 requirements plu
|
|
|
12
12
|
### 1. Get the active bet
|
|
13
13
|
If a bet ID was provided as context (e.g. invoked from `/oprim:promote`), use it directly. Otherwise ask: "Which bet is this spec change for? (e.g. BET-005)"
|
|
14
14
|
|
|
15
|
-
Resolve it to a directory in `oprim/bets/` using the same two patterns `oprim-archive` uses: exact `BET-NNN/` (legacy, no slug) or the slug variant `BET-NNN-<slug>/`. If neither matches, report "Bet BET-NNN was not found in oprim/bets/ — spec deltas can only be authored against an active bet" and stop.
|
|
15
|
+
Resolve it to a directory in `oprim/bets/pending/` using the same two patterns `oprim-archive` uses: exact `BET-NNN/` (legacy, no slug) or the slug variant `BET-NNN-<slug>/`. If neither matches, report "Bet BET-NNN was not found in oprim/bets/pending/ — spec deltas can only be authored against an active bet" and stop.
|
|
16
16
|
|
|
17
17
|
### 2. Get the capability name and description
|
|
18
18
|
If not provided, ask: "What capability are you specifying? (a short name, e.g. 'spec-authoring')" and "What does it do? (one or two sentences)"
|
|
19
19
|
|
|
20
20
|
### 2b. Derive the slug
|
|
21
21
|
From the capability name: lowercase all characters, replace any character that is not a letter or digit with a hyphen, collapse consecutive hyphens to one, strip leading/trailing hyphens. This becomes `<capability>`.
|
|
22
|
-
Output path: `oprim/bets/<resolved-bet-dir>/specs/<capability>/spec.md` (a delta, not `oprim/specs/<capability>/spec.md` — that file is current truth and is only ever written by `oprim-archive`'s merge step).
|
|
22
|
+
Output path: `oprim/bets/pending/<resolved-bet-dir>/specs/<capability>/spec.md` (a delta, not `oprim/specs/<capability>/spec.md` — that file is current truth and is only ever written by `oprim-archive`'s merge step).
|
|
23
23
|
|
|
24
24
|
### 2c. Check for custom rules
|
|
25
25
|
Read `oprim/config.yaml`. If it has a non-empty `rules.spec` value, treat it as additional guidance from the team — factor it into the requirements and scenarios you draft. If `rules.spec` is absent or empty, skip this step; behavior is unchanged.
|
|
@@ -33,8 +33,20 @@ For each requirement, ask whether it is new (**ADDED**), a change to an existing
|
|
|
33
33
|
### 4. Gather requirements and scenarios
|
|
34
34
|
For ADDED and MODIFIED requirements, phrase each as an RFC 2119 statement using SHALL (mandatory), SHOULD (recommended), or MAY (optional), then ask for at least one scenario: a WHEN (trigger) and a THEN (expected outcome), with an optional GIVEN (context) and additional AND steps. REMOVED requirements only need the matching header — no new scenarios.
|
|
35
35
|
|
|
36
|
-
### 5.
|
|
37
|
-
|
|
36
|
+
### 5. Check for existing design/tasks artifacts
|
|
37
|
+
Before writing the spec delta, check whether `design.md` and `tasks.md` already exist in `oprim/bets/pending/<resolved-bet-dir>/`.
|
|
38
|
+
|
|
39
|
+
- If neither exists, this is the first `oprim-spec` invocation for this bet — continue to Step 6 to draft both before writing the delta.
|
|
40
|
+
- If either already exists (from a prior `oprim-spec` invocation for this bet, e.g. for a different capability), skip Step 6 entirely and go straight to Step 7 — only the spec delta is written or appended.
|
|
41
|
+
|
|
42
|
+
### 6. Draft design.md and tasks.md (first invocation only)
|
|
43
|
+
Only performed when Step 5 found neither file yet exists. Write both to `oprim/bets/pending/<resolved-bet-dir>/`:
|
|
44
|
+
|
|
45
|
+
1. **`design.md`** — the technical approach and trade-offs for the capability being specced: key decisions, alternatives considered, risks. Scope it to this capability's implementation, not the whole bet.
|
|
46
|
+
2. **`tasks.md`** — a flat implementation checklist derived from the requirements and scenarios just captured in Step 4. Group tasks under `## N. <heading>` sections (one heading per logical unit of work, e.g. per requirement or component) with `- [ ] N.M <task description>` checkbox items beneath each — the same convention OpenSpec's own `tasks.md` uses, so completion is a simple parse (count of unchecked boxes), not a new format to learn.
|
|
47
|
+
|
|
48
|
+
### 7. Write the delta file
|
|
49
|
+
Append to (or create) `oprim/bets/pending/<resolved-bet-dir>/specs/<capability>/spec.md`, grouping requirements under the matching section header — only include a section if it has at least one requirement under it:
|
|
38
50
|
|
|
39
51
|
```markdown
|
|
40
52
|
## ADDED Requirements
|
|
@@ -64,5 +76,7 @@ Append to (or create) `oprim/bets/<resolved-bet-dir>/specs/<capability>/spec.md`
|
|
|
64
76
|
|
|
65
77
|
If the delta file already exists (a prior spec-authoring pass for this bet/capability), append new requirements to the matching section, creating that section if it's not yet present.
|
|
66
78
|
|
|
67
|
-
###
|
|
79
|
+
### 8. Report what was created
|
|
68
80
|
Show the delta file path, which bet it's scoped to, and a summary of the ADDED/MODIFIED/REMOVED requirements captured. Note that it merges into `oprim/specs/<capability>/spec.md` when `BET-NNN` is archived — nothing is current truth yet.
|
|
81
|
+
|
|
82
|
+
On a first invocation (Step 6 ran), also report the `design.md` and `tasks.md` paths that were created. On a later invocation (Step 6 skipped), note that those two files already existed and were left untouched.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
id: tutorial
|
|
2
|
+
skillName: oprim-tutorial
|
|
3
|
+
title: null
|
|
4
|
+
description: Guided walkthrough of the oprim bet -> spec -> archive cycle inside the todo-app example workspace (examples/) -- creates a real "add due dates to tasks" bet, specs it, and archives it, narrating each step against the example's existing PDRs, bets, and current-truth specs
|
|
5
|
+
claude:
|
|
6
|
+
skill: true
|
|
7
|
+
command: null
|
|
8
|
+
cursor:
|
|
9
|
+
skill: true
|
|
10
|
+
command: null
|
|
11
|
+
poolside:
|
|
12
|
+
skill: true
|
|
13
|
+
inline: false
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oprim-tutorial
|
|
3
|
+
description: Guided walkthrough of the oprim bet -> spec -> archive cycle inside the todo-app example workspace (examples/) -- creates a real "add due dates to tasks" bet, specs it, and archives it, narrating each step against the example's existing PDRs, bets, and current-truth specs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Walk the user through one full oprim workflow cycle — bet → spec → archive — inside this example workspace, using a real new feature for the mock todo app: **"Add due dates to tasks."** This is not a simulation: it invokes the same `oprim-bet`, `oprim-spec`, and `oprim-archive` skills a real project uses, against this workspace's real files.
|
|
7
|
+
|
|
8
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question you ask directly. When a step delegates to another skill via the Skill tool, that skill drives its own prompts.
|
|
9
|
+
|
|
10
|
+
## Before you start
|
|
11
|
+
|
|
12
|
+
Orient the user in 2-3 sentences before Step 1:
|
|
13
|
+
- This workspace (`oprim/`) already has a working example: `PDR-001`/`PDR-002` record two early product decisions, `BET-001` (archived) shipped the core add/complete/delete loop, and `oprim/specs/task-management/spec.md` is that feature's current-truth spec. `BET-002` (pending) is a backlog idea for later.
|
|
14
|
+
- This tutorial adds a new bet on top of that foundation — due dates — and carries it all the way through the cycle: bet decision → spec delta → archive.
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
### 1. Create the bet
|
|
19
|
+
Invoke the **`oprim-bet`** skill using the Skill tool. Suggest (but do not force) the title **"Add due dates to tasks"** if the user doesn't already have one in mind, and note that PDR-001 (flat lists) and PDR-002 (local-only storage) are the relevant prior decisions to link.
|
|
20
|
+
|
|
21
|
+
After the skill finishes, confirm the new bet's ID (it will be the next available `BET-NNN` — `BET-003` if this is the first tutorial run) and its directory under `oprim/bets/pending/`.
|
|
22
|
+
|
|
23
|
+
### 2. Author the spec delta
|
|
24
|
+
Invoke the **`oprim-spec`** skill using the Skill tool, passing the bet ID from Step 1. When it asks for a capability name, suggest **"task-management"** — the same capability `BET-001` shipped — since due dates extend that existing surface rather than introducing a new one. When it asks whether each requirement is ADDED/MODIFIED/REMOVED, this is an ADDED requirement (due dates are new, nothing existing changes).
|
|
25
|
+
|
|
26
|
+
This also scaffolds `design.md` and `tasks.md` for the bet (first `oprim-spec` invocation for a bet always does). Point the user at both files afterward.
|
|
27
|
+
|
|
28
|
+
### 3. Note the pre-archive state
|
|
29
|
+
Before archiving, briefly show the user what's about to change:
|
|
30
|
+
- `oprim/bets/pending/BET-NNN-.../` will move to `oprim/bets/archived/`
|
|
31
|
+
- The new ADDED requirement(s) will be appended to the existing `oprim/specs/task-management/spec.md` (current truth), alongside the three requirements `BET-001` already put there
|
|
32
|
+
- The bet's entry will be removed from `oprim/sequence.yaml`
|
|
33
|
+
|
|
34
|
+
This is the same fold `/oprim:archive` performs on any real project — nothing tutorial-specific happens here.
|
|
35
|
+
|
|
36
|
+
### 4. Archive the bet
|
|
37
|
+
Invoke the **`oprim-archive`** skill using the Skill tool, passing the bet ID from Step 1. Let it run its normal checks (dependents, delta overlaps, incomplete `tasks.md`) — if `tasks.md` still has unchecked items, that's expected for a fresh tutorial run; the user can confirm through the warning or check off items first.
|
|
38
|
+
|
|
39
|
+
### 5. Show the result
|
|
40
|
+
Report what changed, pointing at real file paths:
|
|
41
|
+
- `oprim/bets/archived/BET-NNN-.../` — the completed bet, in full
|
|
42
|
+
- `oprim/specs/task-management/spec.md` — now includes the due-date requirement(s) alongside `BET-001`'s original three
|
|
43
|
+
- `oprim/sequence.yaml` — no longer lists `BET-NNN`
|
|
44
|
+
|
|
45
|
+
Close by telling the user they just completed a full oprim cycle — the same one this tool uses on itself (see this repository's own `oprim/` workspace) — and that running it again works identically on their own project after `oprim init`.
|