@rmrdeveloper/sideroom-pi 9.0.0 → 9.1.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.
Files changed (62) hide show
  1. package/README.md +23 -28
  2. package/assets/visual-context/browser/background.js +2 -2
  3. package/assets/visual-context/browser/popup.js +1 -2
  4. package/extensions/ask/index.ts +0 -1
  5. package/extensions/ask/model.ts +1 -1
  6. package/extensions/ask/selection.ts +0 -4
  7. package/extensions/done/guard.ts +20 -0
  8. package/extensions/done/index.ts +0 -1
  9. package/extensions/done/model.ts +1 -3
  10. package/extensions/explain/guard.ts +11 -21
  11. package/extensions/explain/index.ts +0 -1
  12. package/extensions/explain/model.ts +0 -5
  13. package/extensions/guidelines/execute.ts +16 -2
  14. package/extensions/guidelines/guard.ts +1 -2
  15. package/extensions/guidelines/index.ts +7 -7
  16. package/extensions/guidelines/prompt.ts +0 -2
  17. package/extensions/guidelines/review.ts +9 -10
  18. package/extensions/jev/client.ts +0 -7
  19. package/extensions/jev/context.ts +0 -2
  20. package/extensions/jev/guard.ts +78 -44
  21. package/extensions/jev/index.ts +0 -3
  22. package/extensions/jev/key.ts +0 -3
  23. package/extensions/jev/model.ts +0 -8
  24. package/extensions/jev/queue.ts +125 -0
  25. package/extensions/jev/ui.ts +0 -14
  26. package/extensions/modified-files/index.ts +0 -1
  27. package/extensions/monorepo-skills/index.ts +0 -1
  28. package/extensions/persona/checks.ts +0 -4
  29. package/extensions/persona/guard.ts +45 -6
  30. package/extensions/persona/index.ts +4 -2
  31. package/extensions/persona/model.ts +0 -2
  32. package/extensions/persona/prompt.ts +0 -2
  33. package/extensions/rules/checks.ts +0 -5
  34. package/extensions/rules/index.ts +0 -1
  35. package/extensions/rules/lexer.ts +1 -1
  36. package/extensions/shared/check-status.ts +35 -0
  37. package/extensions/shared/file-path.ts +0 -3
  38. package/extensions/shared/read-file.ts +0 -2
  39. package/extensions/shared/settle.ts +78 -13
  40. package/extensions/todo/guards.ts +40 -84
  41. package/extensions/todo/index.ts +1 -2
  42. package/extensions/todo/model.ts +6 -48
  43. package/extensions/todo/session.ts +1 -4
  44. package/extensions/visual-context/broker.ts +10 -10
  45. package/extensions/visual-context/configure.ts +4 -4
  46. package/extensions/visual-context/files.ts +0 -3
  47. package/extensions/visual-context/index.ts +8 -9
  48. package/extensions/visual-context/literal.ts +0 -1
  49. package/extensions/visual-context/markup.ts +3 -5
  50. package/extensions/visual-context/model.ts +2 -2
  51. package/extensions/visual-context/pairing.ts +1 -2
  52. package/extensions/visual-context/parse-astro.ts +9 -12
  53. package/extensions/visual-context/parse-jsx.ts +2 -2
  54. package/extensions/visual-context/parse-vue.ts +1 -1
  55. package/extensions/visual-context/setup.ts +8 -8
  56. package/extensions/visual-context/source.ts +0 -3
  57. package/package.json +15 -9
  58. package/skills/sideroom-guidelines/SKILL.md +1 -1
  59. package/skills/sideroom-persona/SKILL.md +8 -6
  60. package/skills/sideroom-user-stories/SKILL.md +78 -0
  61. package/skills/sideroom-user-stories/references/review.md +84 -0
  62. package/skills/sideroom-user-stories/references/templates.md +131 -0
@@ -84,8 +84,7 @@ export async function ensurePairing(
84
84
  flag: 'wx',
85
85
  });
86
86
  try {
87
- // Linking a complete file prevents concurrent Pi processes from reading
88
- // a half-written credential or replacing another process's identity.
87
+
89
88
  linkSync(staging, join(directory, FILE));
90
89
  } catch (cause) {
91
90
  if (!hasCode(cause, 'EEXIST')) {
@@ -40,8 +40,6 @@ function byteOffsets(source: string): Int32Array {
40
40
  return offsets;
41
41
  }
42
42
 
43
- // Astro's self-closing node end may precede its attributes. Find only the end
44
- // of its original opening tag, respecting quoted and braced attribute values.
45
43
  function openingTagEnd(source: string, start: number): number {
46
44
  let quote = '';
47
45
  let braces = 0;
@@ -87,16 +85,16 @@ export function parseAstro(source: string): ParsedMarkup {
87
85
  : undefined;
88
86
  const value =
89
87
  expression === undefined ? undefined : literalText(expression);
90
- // Expression text is JS source, not rendered text. Visit only real tags.
88
+
91
89
  return value === undefined
92
90
  ? [
93
- {
94
- kind: 'dynamic',
95
- children: node.children
96
- .filter((child) => child.type !== 'text')
97
- .flatMap(convert),
98
- },
99
- ]
91
+ {
92
+ kind: 'dynamic',
93
+ children: node.children
94
+ .filter((child) => child.type !== 'text')
95
+ .flatMap(convert),
96
+ },
97
+ ]
100
98
  : [{ kind: 'text', value }];
101
99
  }
102
100
  if (node.type === 'root' || node.type === 'fragment') {
@@ -117,8 +115,7 @@ export function parseAstro(source: string): ParsedMarkup {
117
115
  const closingTag = reportedRange
118
116
  .slice(reportedRange.lastIndexOf('</'))
119
117
  .replace(/\s+>$/u, '>');
120
- // When the compiler's closing position is wrong, the verified opening tag
121
- // is still a valid source range; never extend it to a guessed closing tag.
118
+
122
119
  const end =
123
120
  isClosed || closingTag !== `</${node.name}>` ? openingEnd : reportedEnd;
124
121
  if (!node.position?.end && !isClosed) {
@@ -50,7 +50,7 @@ export function parseJsx(source: string, typescript: boolean): ParsedMarkup {
50
50
  let attributes: Record<string, string | undefined> = {};
51
51
  for (const attribute of node.openingElement.attributes) {
52
52
  if (attribute.type === 'JSXSpreadAttribute') {
53
- // A spread may overwrite descriptors or make captured text private.
53
+
54
54
  attributes = { hidden: undefined, contenteditable: undefined };
55
55
  continue;
56
56
  }
@@ -92,7 +92,7 @@ export function parseJsx(source: string, typescript: boolean): ParsedMarkup {
92
92
  }
93
93
  const children = node.children.flatMap((child): readonly SourceNode[] => {
94
94
  if (child.type === 'JSXText') {
95
- // JSX trims indentation at line boundaries, unlike HTML textContent.
95
+
96
96
  const lines = child.value.replace(/\t/gu, ' ').split(/\r\n|\n|\r/u);
97
97
  const text = lines
98
98
  .map((line, index) => {
@@ -96,7 +96,7 @@ export function parseVue(source: string): ParsedMarkup {
96
96
  prop.arg.type !== NodeTypes.SIMPLE_EXPRESSION ||
97
97
  !prop.arg.isStatic
98
98
  ) {
99
- // An unknown key or object binding may replace a static id.
99
+
100
100
  attributes.id = undefined;
101
101
  attributes.hidden = undefined;
102
102
  attributes.contenteditable = undefined;
@@ -63,15 +63,15 @@ export function prepareSetup(cwd: string, project: string) {
63
63
  args:
64
64
  inspected.manager === 'npm'
65
65
  ? [
66
- 'install',
67
- '--save-dev',
68
- `${compilerPackage(inspected.kind)}@${INSPECTOR_VERSION}`,
69
- ]
66
+ 'install',
67
+ '--save-dev',
68
+ `${compilerPackage(inspected.kind)}@${INSPECTOR_VERSION}`,
69
+ ]
70
70
  : [
71
- 'add',
72
- '--dev',
73
- `${compilerPackage(inspected.kind)}@${INSPECTOR_VERSION}`,
74
- ],
71
+ 'add',
72
+ '--dev',
73
+ `${compilerPackage(inspected.kind)}@${INSPECTOR_VERSION}`,
74
+ ],
75
75
  },
76
76
  checkScripts: Object.keys(manifest.scripts ?? {}).filter((script) =>
77
77
  ['check', 'typecheck', 'lint', 'test', 'build'].includes(script),
@@ -240,7 +240,6 @@ export function candidateIsCurrent(
240
240
  return currentCandidates(cwd, [candidate]).length === 1;
241
241
  }
242
242
 
243
- // Each call owns its file versions; no validation survives into another prompt.
244
243
  export function currentCandidates(
245
244
  cwd: string,
246
245
  candidates: readonly SourceCandidate[],
@@ -290,8 +289,6 @@ export function currentCandidates(
290
289
  });
291
290
  }
292
291
 
293
- // A path that turned into a file on the way reports ENOTDIR, which means the
294
- // remembered source is gone rather than unreadable.
295
292
  function isGone(cause: unknown): boolean {
296
293
  return (
297
294
  isMissingFile(cause) ||
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rmrdeveloper/sideroom-pi",
3
- "version": "9.0.0",
4
- "description": "Pi coding-agent package that asks clarifying questions, shows a live todo board, enforces coding guidelines, and will not call it done while your checks are red.",
3
+ "version": "9.1.0",
4
+ "description": "Make \"done\" mean done for Pi. Asks before it guesses, enforces your coding rules, and refuses to finish while your project's checks are red.",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "extensions",
@@ -17,15 +17,15 @@
17
17
  "commitlint": "commitlint",
18
18
  "types": "tsc --noEmit",
19
19
  "test": "node --test 'extensions/**/*.test.ts' 'scripts/**/*.test.mjs'",
20
- "lint": "biome lint extensions assets/visual-context/browser scripts commitlint.config.mjs",
21
- "lint:fix": "biome lint --write extensions assets/visual-context/browser scripts commitlint.config.mjs",
22
- "format": "biome format --write extensions assets/visual-context/browser scripts package.json biome.json commitlint.config.mjs .changeset/config.json",
23
- "format:check": "biome format extensions assets/visual-context/browser scripts package.json biome.json commitlint.config.mjs .changeset/config.json",
20
+ "lint": "eslint extensions assets/visual-context/browser scripts commitlint.config.mjs eslint.config.mjs package.json tsconfig.json .changeset/config.json",
21
+ "lint:fix": "eslint extensions assets/visual-context/browser scripts commitlint.config.mjs eslint.config.mjs package.json tsconfig.json .changeset/config.json --fix",
22
+ "format": "eslint extensions assets/visual-context/browser scripts commitlint.config.mjs eslint.config.mjs package.json tsconfig.json .changeset/config.json --fix",
23
+ "format:check": "eslint extensions assets/visual-context/browser scripts commitlint.config.mjs eslint.config.mjs package.json tsconfig.json .changeset/config.json",
24
24
  "preview": "node scripts/render-preview.mjs",
25
25
  "cache:report": "node scripts/cache-report.mjs",
26
26
  "package:browser": "node scripts/package-browser.mjs",
27
27
  "check:if-blocks": "node scripts/check-if-blocks.mjs",
28
- "check": "npm run types && npm run check:if-blocks && biome check extensions assets/visual-context/browser scripts package.json biome.json commitlint.config.mjs .changeset/config.json && npm test"
28
+ "check": "npm run types && npm run check:if-blocks && npm run lint && npm test"
29
29
  },
30
30
  "engines": {
31
31
  "node": ">=22.19.0"
@@ -86,17 +86,23 @@
86
86
  "typebox": "*"
87
87
  },
88
88
  "devDependencies": {
89
- "@biomejs/biome": "2.5.11",
90
89
  "@changesets/cli": "^3.0.2",
91
90
  "@commitlint/cli": "^21.2.2",
92
91
  "@commitlint/config-conventional": "^21.2.2",
93
92
  "@earendil-works/pi-coding-agent": "1.2.0",
94
93
  "@earendil-works/pi-tui": "1.2.0",
94
+ "@eslint/js": "^10.0.1",
95
+ "@stylistic/eslint-plugin": "^5.10.0",
95
96
  "@types/node": "^22.20.1",
96
97
  "@types/ws": "^8.18.2",
98
+ "eslint": "^10.13.0",
99
+ "eslint-plugin-jsonc": "^3.4.2",
100
+ "eslint-plugin-no-comments": "^1.2.1",
97
101
  "fflate": "0.8.3",
102
+ "globals": "^17.13.0",
98
103
  "typebox": "1.3.7",
99
- "typescript": "^5.6.0"
104
+ "typescript": "^5.6.0",
105
+ "typescript-eslint": "^8.72.0"
100
106
  },
101
107
  "dependencies": {
102
108
  "@astrojs/compiler": "^4.0.0",
@@ -11,7 +11,7 @@ metadata:
11
11
 
12
12
  ## Activation Contract
13
13
 
14
- Before `write` or `edit`, call `sideroom_guidelines` with the target file's `path`. It returns this whole skill and the matching complete language guide, or only this skill for unsupported languages. Reuse loaded guides during the same agent run; load any additional target language before editing it. Reload after compaction or a new run.
14
+ Before `write` or `edit`, call `sideroom_guidelines` with the target file's `path`. On first load it returns this whole skill and the matching complete language guide, or only this skill for unsupported languages. Later calls return only missing complete documents; an already loaded target receives a short acknowledgement. Reuse loaded guides during the same agent run; load any additional target language before editing it. Reload after compaction or a new run.
15
15
 
16
16
  Alternatively, load this whole skill with `read`, without `offset` or `limit`, using the absolute packaged path shown in Available Skills. For each supported target language, also fully load its exact guide from `references/languages/`. Both routes satisfy the same gate only on successful, complete delivery.
17
17
 
@@ -89,12 +89,14 @@ dead-locked.
89
89
 
90
90
  ### What steering means
91
91
 
92
- The extension sends a corrective steer after a degraded artifact block or a
93
- prohibited assistant message. Both moments happen while the agent is working, so
94
- the steer travels through `pi.sendMessage` with `{ triggerTurn: true, deliverAs:
95
- 'steer' }` and joins the current run. A steer sent after the run ended belongs
96
- to the guidelines review and the walkthrough offer, which append a
97
- `custom_message` entry on `agent_before_settle` instead.
92
+ The extension retains a correction for the latest successful final response.
93
+ A later clean response, tool-use response, error, or cancellation clears it.
94
+ Corrections append a `custom_message` entry on a completed, quiet
95
+ `agent_before_settle`: they join an existing review/check continuation when
96
+ possible, and request a continuation only if the final response still needs
97
+ correction. Aborts and queued work postpone corrections without consuming them.
98
+ Degraded artifact warnings use `{ triggerTurn: false, deliverAs: 'steer' }`
99
+ and wait for the next natural response.
98
100
  At most three steers are sent per agent run.
99
101
 
100
102
  ## Boundaries
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: sideroom-user-stories
3
+ description: "Write, revise, review, or split user stories and their acceptance criteria from a brief or existing ticket. Use for functional stories and technical enablement tickets, independent of the issue tracker. Use sideroom-grill for an open feature plan; this skill owns the resulting story, not implementation or ticket publication."
4
+ license: MIT
5
+ metadata:
6
+ author: RMRdeveloper
7
+ version: "1.0"
8
+ ---
9
+
10
+ # Sideroom user stories
11
+
12
+ ## Scope and authority
13
+
14
+ Produce stories that product, development, and QA can interpret alike without
15
+ reconstructing the conversation. Write in the user's language, preserving the
16
+ project's domain terms.
17
+
18
+ Writing or reviewing a story authorizes text in the conversation only. Save a
19
+ file, publish or edit a remote ticket, set metadata, or change workflow state
20
+ only when the user requests that action. Story work does not authorize
21
+ implementation, glossary edits, or architectural decisions.
22
+
23
+ ## Workflow
24
+
25
+ 1. **Read the available facts.** Start with the brief or existing ticket and
26
+ the relevant project conventions, glossary, approved specs, and linked
27
+ dependencies. Inspect code only for facts needed to distinguish current
28
+ behavior from the requested change; code does not establish product intent.
29
+ Reuse local ticket conventions, including required fields, without
30
+ importing another project's roles, boards, labels, estimates, or priorities.
31
+ If the tracker is available, search the relevant project for duplicates
32
+ before proposing a new ticket. Otherwise state that duplicates were not
33
+ checked; do not claim uniqueness or invent issue keys.
34
+ 2. **Identify the need.** State the actor receiving value, the capability,
35
+ the useful result, and the boundaries. Prefer a complete user outcome over
36
+ separate UI, API, and persistence stories. Split independently valuable
37
+ outcomes; use technical tasks for coordination or honor an explicitly
38
+ approved project split. Each proposed story must retain a clear result and
39
+ explain any real dependency.
40
+ 3. **Resolve material gaps.** Ask through `sideroom_ask` only about missing
41
+ facts or decisions that change scope or acceptance. Load
42
+ `../sideroom-grill/SKILL.md` when the broader feature plan is open, or
43
+ `../sideroom-architecture/SKILL.md` for an unresolved architectural choice.
44
+ Reuse settled answers rather than repeating an interview. If clarification
45
+ is unavailable or the user wants a draft, mark it as a draft and list open
46
+ questions separately. Never turn a proposed permission, rule, metric,
47
+ dependency, or error response into an agreed requirement.
48
+ 4. **Draft the story.** Read [templates](references/templates.md) before
49
+ drafting or rewriting. The normal output is a result-oriented title, a
50
+ story sentence, scope, and acceptance criteria. Add other sections only
51
+ when they clarify a real condition. A technical enablement story names
52
+ its consumer and the capability it enables. For a bug, use the bug format
53
+ instead of forcing a story sentence.
54
+ 5. **Review the result.** Read [review guidance](references/review.md) before
55
+ returning a draft or review. Trace each criterion to scope or a business
56
+ rule; remove accidental new work and repeated content. Check the successful
57
+ path and only the failure or boundary cases that can change the result.
58
+ A story is ready for review when each criterion has an objective pass/fail
59
+ result and no unresolved question changes the promised behavior. If that
60
+ gate fails, return the draft with its blockers, not a readiness claim.
61
+
62
+ ## Output boundaries
63
+
64
+ For a review, report concrete ambiguities, missing outcomes, or unsupported
65
+ requirements; propose corrected wording without silently expanding scope.
66
+ For a split, show each story and explain the actual dependency between them.
67
+
68
+ Keep the ticket focused on need, behavior, rules, and observable results.
69
+ Implementation steps, source paths, logs, and routine lint/build commands
70
+ belong in implementation records. Link approved design decisions when needed;
71
+ include their relevant behavioral consequences in the ticket itself. Technical
72
+ contracts belong in acceptance only when they define the agreed deliverable.
73
+
74
+ When publication is requested, confirm the destination and required metadata
75
+ from project facts or the user, check duplicates, and explain blocking links
76
+ in the correct direction: the prerequisite blocks the dependent. Report what
77
+ was actually saved; if access or required facts are missing, return the draft
78
+ and the missing information instead of claiming publication.
@@ -0,0 +1,84 @@
1
+ # Story review
2
+
3
+ Apply this review to each story, including stories proposed in a split.
4
+ Report findings against specific wording, with a correction or a question
5
+ that would resolve the issue.
6
+
7
+ ## Title, actor, and value
8
+
9
+ - The title names a recognizable result and business object.
10
+ - The actor receives the value; an implementation team is not a substitute.
11
+ - The capability uses a precise action such as consult, assign, cancel, or
12
+ configure. Replace “improve” or “manage” with what actually changes.
13
+ - The value explains why the actor needs the capability without promising
14
+ metrics or benefits unsupported by the agreed scope.
15
+ - One main need has clear boundaries. Split independent outcomes rather
16
+ than mechanically separating implementation layers.
17
+
18
+ ## Scope and rules
19
+
20
+ Scope states what can be done or seen and which state changes. Business
21
+ rules state the conditions governing that behavior: permissions, restrictions,
22
+ precedence, ownership, or a source of truth when relevant.
23
+
24
+ A rule should survive a different implementation. “A reservation has at most
25
+ one active holder” is a rule; a proposed database index is a design choice
26
+ unless that index is itself the technical deliverable.
27
+
28
+ Include agreed behavior, not guesses. An unresolved permission or conflict
29
+ policy is a question, not a criterion. Link decisions with enough behavioral
30
+ detail that QA can understand the expected outcome without reading the code.
31
+
32
+ ## Acceptance criteria
33
+
34
+ Each criterion needs a relevant precondition, an action or event, and an
35
+ observable result. Use Given/When/Then for conditional behavior; a direct
36
+ bullet is sufficient when it still carries all three parts.
37
+
38
+ Each criterion must be:
39
+
40
+ - Observable through a public interface or a business result.
41
+ - Specific enough that two reviewers agree whether it passes.
42
+ - Focused on one main rule rather than a chain of unrelated behaviors.
43
+ - Traceable to scope or an agreed business rule, without introducing new work.
44
+ - Neutral to implementation, except for an agreed technical contract.
45
+
46
+ Cover the successful path. Add only cases that can change the outcome:
47
+ authorization and visibility, invalid input and recovery, empty data,
48
+ persistence on a later consultation, retries and duplicate requests,
49
+ concurrent changes, dates and limits, dependency failure, or agreed legacy
50
+ compatibility. Ask about unknown behavior instead of completing this list
51
+ by assumption. Measurable performance requirements need an agreed threshold
52
+ and conditions; never invent them.
53
+
54
+ | Weak wording | Correction |
55
+ | --- | --- |
56
+ | “Works correctly” | Name the initial state, event, and final result. |
57
+ | “The interface is intuitive” | State the action the actor can complete and the feedback received. |
58
+ | “Handle errors” | Name the relevant failure and the observable state afterward. |
59
+ | “According to the design” | State the rule and link the specific approved design if needed. |
60
+ | “Create an endpoint, table, and tests” | State the observable contract or move implementation steps out. |
61
+ | “Pass lint and update documentation” | Keep routine delivery checks in the project's Definition of Done, not functional acceptance. |
62
+
63
+ Acceptance describes behavior. Tests demonstrate it; PR checks record the
64
+ tools and commands used. Do not turn those records into user requirements.
65
+
66
+ ## Readiness check
67
+
68
+ Before calling a story ready for review, confirm:
69
+
70
+ - The actor, capability, and useful result are explicit.
71
+ - Scope and acceptance describe the same single need.
72
+ - Relevant business rules and outcome-changing edge cases are settled.
73
+ - Every criterion can objectively pass or fail.
74
+ - Optional sections clarify something real and do not repeat other sections.
75
+ - Dependencies have real references and an explanation when known.
76
+ - Open questions are outside the promised requirements; unresolved behavior
77
+ that changes acceptance keeps the story marked as a draft.
78
+ - Technical detail defines an agreed contract or remains outside the story.
79
+ - Tracker conventions and metadata come from the project or user.
80
+ - Duplicate search and publication status are reported honestly.
81
+
82
+ This is a semantic review, not proof of product approval, implementation,
83
+ or QA completion. Do not mark a remote workflow state merely because this
84
+ checklist passes.
@@ -0,0 +1,131 @@
1
+ # Story templates
2
+
3
+ Use the user's language for headings and content; translate these English
4
+ examples when needed. Replace placeholders with agreed facts; omit optional
5
+ sections that add no information. Keep unresolved questions outside the
6
+ promised scope.
7
+
8
+ ## Functional story
9
+
10
+ Title: `<Action verb + result and business object>`
11
+
12
+ ```md
13
+ As a <person>, I want <capability>, so that <useful, verifiable result>.
14
+
15
+ ## Context
16
+
17
+ <Why this is needed, or the contrast between current and expected behavior.>
18
+
19
+ ## Scope
20
+
21
+ - <Included capability and its boundaries.>
22
+
23
+ ## Business rules
24
+
25
+ - <Permission, restriction, or precedence that changes the outcome.>
26
+
27
+ ## Acceptance criteria
28
+
29
+ - Given <initial state>, when <action or event>, then <observable result>.
30
+
31
+ ## Dependencies
32
+
33
+ - <Actual reference> — <required capability and reason it blocks this work>.
34
+
35
+ ## Out of scope
36
+
37
+ - <Nearby work that could be mistaken for part of this delivery>.
38
+ ```
39
+
40
+ The story sentence, scope, and acceptance criteria are required. Context,
41
+ business rules, dependencies, and exclusions are conditional. A project may
42
+ require additional fields; use its conventions rather than inventing values.
43
+
44
+ Choose the most specific role whose workflow improves. Put secondary
45
+ beneficiaries in scope only when relevant. The capability describes what the
46
+ actor achieves, not a screen, table, or endpoint. The value explains a useful
47
+ consequence, not “so that I can use the feature” or an unsubstantiated benefit.
48
+
49
+ A title must distinguish the result in a list without opening the ticket.
50
+ Keep product vocabulary; include technology only when it defines the
51
+ deliverable. Prefer “View orders by delivery status” over
52
+ “Changes to Orders.vue”.
53
+
54
+ ## Technical enablement
55
+
56
+ Use a developer or another technical consumer only when they receive the
57
+ value. Keep the functional template, but make the enabled capability explicit:
58
+
59
+ > As an integration developer, I want to query an order's status through a
60
+ > stable contract, so that I can report its progress without duplicating
61
+ > tracking rules.
62
+
63
+ Include agreed schemas, routes, constraints, or compatibility rules when those
64
+ are the deliverable. “Create a table, endpoint, and tests” is implementation
65
+ work, not an observable acceptance criterion. If the contract is undecided,
66
+ resolve it or return a draft; do not invent its fields.
67
+
68
+ ## Bug
69
+
70
+ ```md
71
+ ## Observed behavior
72
+
73
+ <What happens and who is affected.>
74
+
75
+ ## Expected behavior
76
+
77
+ <Correct result supported by a rule or decision.>
78
+
79
+ ## Steps to reproduce
80
+
81
+ 1. <Known initial state.>
82
+ 2. <Action.>
83
+ 3. <Observed result.>
84
+
85
+ ## Impact
86
+
87
+ <Known affected users, frequency, and blockers; state what needs confirmation.>
88
+
89
+ ## Acceptance criteria
90
+
91
+ - Given <condition that reproduces the defect>, when <action>,
92
+ then <correct observable result>.
93
+ ```
94
+
95
+ Use minimal anonymized reproduction data. Keep secrets and unnecessary
96
+ personal information out. If reproduction is unknown, state that explicitly
97
+ rather than supplying fictional steps.
98
+
99
+ ## Complete functional example
100
+
101
+ The permissions and behavior here are illustrative, not defaults for a project.
102
+
103
+ **Title:** Allow cancellation of a pending reservation
104
+
105
+ As a reservation holder, I want to cancel my reservation before confirmation,
106
+ so that I can release the time slot I no longer need.
107
+
108
+ ### Scope
109
+
110
+ - The holder can cancel their own reservation while it is pending.
111
+ - The holder can view the resulting status when reopening the reservation.
112
+
113
+ ### Business rules
114
+
115
+ - Only the reservation holder can cancel the reservation.
116
+ - This flow does not allow cancellation of a confirmed reservation.
117
+
118
+ ### Acceptance criteria
119
+
120
+ - Given a pending reservation, when its holder cancels it, then the reservation
121
+ becomes cancelled.
122
+ - Given a cancelled reservation, when its holder views it again, then they see
123
+ the cancelled status.
124
+ - Given another person's reservation, when someone attempts to cancel it,
125
+ then the action is denied and the reservation retains its status.
126
+ - Given a confirmed reservation, when its holder attempts to cancel it through
127
+ this flow, then cancellation is rejected and the reservation stays confirmed.
128
+
129
+ ### Out of scope
130
+
131
+ - Cancelling confirmed reservations or processing refunds.