@imix-js/taproot 0.2.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/README.md +88 -0
- package/dist/adapters/index.d.ts +20 -0
- package/dist/adapters/index.js +452 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/acceptance-check.d.ts +26 -0
- package/dist/commands/acceptance-check.js +213 -0
- package/dist/commands/acceptance-check.js.map +1 -0
- package/dist/commands/check-orphans.d.ts +8 -0
- package/dist/commands/check-orphans.js +157 -0
- package/dist/commands/check-orphans.js.map +1 -0
- package/dist/commands/commithook.d.ts +15 -0
- package/dist/commands/commithook.js +389 -0
- package/dist/commands/commithook.js.map +1 -0
- package/dist/commands/coverage.d.ts +41 -0
- package/dist/commands/coverage.js +390 -0
- package/dist/commands/coverage.js.map +1 -0
- package/dist/commands/dod.d.ts +13 -0
- package/dist/commands/dod.js +141 -0
- package/dist/commands/dod.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.js +378 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/link-commits.d.ts +12 -0
- package/dist/commands/link-commits.js +126 -0
- package/dist/commands/link-commits.js.map +1 -0
- package/dist/commands/overview.d.ts +6 -0
- package/dist/commands/overview.js +192 -0
- package/dist/commands/overview.js.map +1 -0
- package/dist/commands/plan.d.ts +23 -0
- package/dist/commands/plan.js +167 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/sync-check.d.ts +8 -0
- package/dist/commands/sync-check.js +118 -0
- package/dist/commands/sync-check.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +309 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate-format.d.ts +8 -0
- package/dist/commands/validate-format.js +93 -0
- package/dist/commands/validate-format.js.map +1 -0
- package/dist/commands/validate-structure.d.ts +8 -0
- package/dist/commands/validate-structure.js +29 -0
- package/dist/commands/validate-structure.js.map +1 -0
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.js +86 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/configuration.d.ts +7 -0
- package/dist/core/configuration.js +112 -0
- package/dist/core/configuration.js.map +1 -0
- package/dist/core/dod-runner.d.ts +20 -0
- package/dist/core/dod-runner.js +233 -0
- package/dist/core/dod-runner.js.map +1 -0
- package/dist/core/dor-runner.d.ts +18 -0
- package/dist/core/dor-runner.js +156 -0
- package/dist/core/dor-runner.js.map +1 -0
- package/dist/core/fs-walker.d.ts +5 -0
- package/dist/core/fs-walker.js +74 -0
- package/dist/core/fs-walker.js.map +1 -0
- package/dist/core/git.d.ts +24 -0
- package/dist/core/git.js +76 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/impl-reader.d.ts +8 -0
- package/dist/core/impl-reader.js +39 -0
- package/dist/core/impl-reader.js.map +1 -0
- package/dist/core/language.d.ts +39 -0
- package/dist/core/language.js +159 -0
- package/dist/core/language.js.map +1 -0
- package/dist/core/markdown-parser.d.ts +3 -0
- package/dist/core/markdown-parser.js +37 -0
- package/dist/core/markdown-parser.js.map +1 -0
- package/dist/core/reporter.d.ts +3 -0
- package/dist/core/reporter.js +33 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.js +126 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/validators/format-rules.d.ts +10 -0
- package/dist/validators/format-rules.js +238 -0
- package/dist/validators/format-rules.js.map +1 -0
- package/dist/validators/structure-rules.d.ts +10 -0
- package/dist/validators/structure-rules.js +94 -0
- package/dist/validators/structure-rules.js.map +1 -0
- package/dist/validators/types.d.ts +68 -0
- package/dist/validators/types.js +2 -0
- package/dist/validators/types.js.map +1 -0
- package/docs/agents.md +88 -0
- package/docs/architecture.md +53 -0
- package/docs/cli.md +226 -0
- package/docs/concepts.md +268 -0
- package/docs/configuration.md +255 -0
- package/docs/demo.svg +111 -0
- package/docs/patterns.md +118 -0
- package/docs/security.md +95 -0
- package/docs/workflows.md +151 -0
- package/languages/de.json +20 -0
- package/languages/en.json +20 -0
- package/languages/es.json +20 -0
- package/languages/fr.json +20 -0
- package/languages/ja.json +20 -0
- package/languages/pt.json +20 -0
- package/package.json +54 -0
- package/skills/analyse-change.md +101 -0
- package/skills/behaviour.md +179 -0
- package/skills/bug.md +70 -0
- package/skills/commit.md +99 -0
- package/skills/decompose.md +101 -0
- package/skills/discover.md +392 -0
- package/skills/grill-me.md +65 -0
- package/skills/guide.md +118 -0
- package/skills/implement.md +149 -0
- package/skills/ineed.md +147 -0
- package/skills/intent.md +104 -0
- package/skills/plan.md +63 -0
- package/skills/promote.md +69 -0
- package/skills/refine.md +78 -0
- package/skills/research.md +122 -0
- package/skills/review-all.md +92 -0
- package/skills/review.md +80 -0
- package/skills/status.md +103 -0
- package/skills/sweep.md +89 -0
- package/skills/trace.md +151 -0
package/docs/cli.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# CLI Reference
|
|
2
|
+
|
|
3
|
+
The Taproot CLI handles setup, validation, and reporting. It does not generate content — that's what the agent skills do. The rule of thumb: use `/tr-*` commands when you want the AI to write or update documents; use `taproot` CLI commands to validate, report, and check structural integrity.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### `taproot init`
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
taproot init [--with-hooks] [--with-ci github|gitlab] [--with-skills] [--agent claude|cursor|copilot|windsurf|generic|all]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Initializes Taproot in the current directory. Creates `taproot/` and `.taproot/settings.yaml` if they don't exist, then installs whichever integrations you request.
|
|
16
|
+
|
|
17
|
+
| Option | Effect |
|
|
18
|
+
|--------|--------|
|
|
19
|
+
| `--with-hooks` | Installs `.git/hooks/pre-commit` running `taproot commithook` |
|
|
20
|
+
| `--with-ci github` | Generates `.github/workflows/taproot.yml` with validate-structure, validate-format, and check-orphans |
|
|
21
|
+
| `--with-ci gitlab` | Generates a `taproot-validate` job in `.gitlab-ci.yml` |
|
|
22
|
+
| `--with-skills` | Copies skill definitions to `.taproot/skills/`. Implied by `--agent claude` — only needed if you want skills without a Claude adapter. |
|
|
23
|
+
| `--agent <name>` | Generates agent adapter files (see [Agent Setup](agents.md)) |
|
|
24
|
+
|
|
25
|
+
Running `taproot init` again on an existing project is safe — it skips files that already exist and reports `exists` for each.
|
|
26
|
+
|
|
27
|
+
### `taproot update`
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
taproot update [--with-hooks]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Refreshes installed agent adapters and skills to the current version. Run this after upgrading the `taproot` package.
|
|
34
|
+
|
|
35
|
+
The update command also:
|
|
36
|
+
- Removes stale artefacts from older Taproot layouts (e.g., the pre-v0.1 `taproot/skills/` directory)
|
|
37
|
+
- Refreshes `.taproot/docs/` with the current taproot documentation (patterns, architecture, security, etc.) so agent skills have up-to-date reference material
|
|
38
|
+
- Runs a cross-link refresh: adds missing `## Behaviours <!-- taproot-managed -->` sections to `intent.md` files and `## Implementations <!-- taproot-managed -->` sections to `usecase.md` files, then appends any missing child links and prunes any links to non-existent files
|
|
39
|
+
- Migrates old `taproot validate-structure` / `taproot validate-format` pre-commit hooks to the newer `taproot commithook` format
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Validation
|
|
44
|
+
|
|
45
|
+
### `taproot validate-structure`
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
taproot validate-structure [--path taproot/] [--strict]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Verifies that the folder hierarchy follows Taproot's nesting rules:
|
|
52
|
+
- Intent folders contain `intent.md` and optionally behaviour subdirectories
|
|
53
|
+
- Behaviour folders contain `usecase.md` and optionally impl or sub-behaviour subdirectories
|
|
54
|
+
- Implementation folders contain `impl.md` and are always leaves (no children)
|
|
55
|
+
- Folder names match the allowed pattern (lowercase kebab-case)
|
|
56
|
+
|
|
57
|
+
Exit 0 if valid, exit 1 with violations. Use `--strict` to treat warnings as errors.
|
|
58
|
+
|
|
59
|
+
### `taproot validate-format`
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
taproot validate-format [--path taproot/] [--fix]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Validates that marker files (`intent.md`, `usecase.md`, `impl.md`) conform to their schemas — required sections present, required fields populated, state values from the allowed set. Also checks:
|
|
66
|
+
|
|
67
|
+
- Every `intent.md` with child behaviour folders has a `## Behaviours <!-- taproot-managed -->` section
|
|
68
|
+
- Every `usecase.md` with child impl folders has an `## Implementations <!-- taproot-managed -->` section
|
|
69
|
+
- Every link in those sections resolves to an existing file (detects `STALE_LINK`)
|
|
70
|
+
- `impl.md` `## Behaviour` references point to existing `usecase.md` files
|
|
71
|
+
- Every `usecase.md` with child impl folders has a `## Acceptance Criteria` section (warns `MISSING_ACCEPTANCE_CRITERIA` if absent)
|
|
72
|
+
- Acceptance criterion IDs (`AC-N` and `NFR-N`) are unique within their respective prefix namespace within a file (errors `DUPLICATE_CRITERION_ID` if duplicate)
|
|
73
|
+
|
|
74
|
+
Use `--fix` to scaffold missing section headers automatically. This is safe to run repeatedly — it only adds what's missing, never overwrites existing content.
|
|
75
|
+
|
|
76
|
+
### `taproot acceptance-check`
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
taproot acceptance-check [--path taproot/] [--tests <dir>] [--format json]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Verifies that every acceptance criterion ID (`AC-N` and `NFR-N`) defined in `## Acceptance Criteria` sections is referenced by at least one test or verification file, and that no test file references a criterion that doesn't exist in any spec.
|
|
83
|
+
|
|
84
|
+
Reports three categories:
|
|
85
|
+
- **Uncovered:** criterion IDs present in specs but not found in any test file (exit 1)
|
|
86
|
+
- **Orphaned:** criterion IDs found in test files but not defined in any `usecase.md` (exit 1)
|
|
87
|
+
- **Missing sections:** `usecase.md` files with child implementations but no `## Acceptance Criteria` section (warning only)
|
|
88
|
+
|
|
89
|
+
| Option | Effect |
|
|
90
|
+
|--------|--------|
|
|
91
|
+
| `--path <path>` | Limit spec collection to a subtree; test files are still scanned globally |
|
|
92
|
+
| `--tests <dir>` | Override test directory (repeatable; defaults to `test/`, `tests/`, `spec/`) |
|
|
93
|
+
| `--format json` | Output a JSON report instead of human-readable text |
|
|
94
|
+
|
|
95
|
+
Criterion ID matching is grep-based: the strings `AC-N` and `NFR-N` must appear verbatim somewhere in the test or verification file. Common patterns: `it('AC-1: ...')`, `describe('AC-3')`, `// covers AC-2`, `// NFR-1 verified by load-test/search.k6.js`. NFR-N references may point to load tests, security scan configs, or manual verification artefacts — any file containing the verbatim ID counts. Deprecated criteria (lines starting with `~~**AC-N` or `~~**NFR-N`) are excluded from the check.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Traceability
|
|
100
|
+
|
|
101
|
+
### `taproot link-commits`
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
taproot link-commits [--since <date|hash>] [--dry-run]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Scans the git log for commits matching the conventional format (`taproot(<path>): message` or `Taproot: <path>` trailer) and adds them to the `## Commits` section of the corresponding `impl.md`. Use `--dry-run` to preview changes without writing them. Use `--since` to limit the scan to recent commits.
|
|
108
|
+
|
|
109
|
+
### `taproot check-orphans`
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
taproot check-orphans [--path taproot/] [--include-unimplemented]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Finds broken references across the hierarchy:
|
|
116
|
+
- Source files listed in `impl.md` that no longer exist on disk
|
|
117
|
+
- `## Behaviour` references in `impl.md` that point to non-existent `usecase.md` files
|
|
118
|
+
- Commits in `## Commits` that are not in git history
|
|
119
|
+
|
|
120
|
+
Use `--include-unimplemented` to also report behaviours with no implementations (useful for coverage gaps, not just broken links).
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Reporting
|
|
125
|
+
|
|
126
|
+
### `taproot coverage`
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
taproot coverage [--path taproot/] [--format tree|json|markdown|context]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Summarizes implementation completeness across the hierarchy: how many behaviours have at least one implementation, how many are still planned. The default output is a tree view. Use `--format context` to write `taproot/CONTEXT.md` — a compact summary suitable for pasting into an agent context window.
|
|
133
|
+
|
|
134
|
+
### `taproot sync-check`
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
taproot sync-check [--path taproot/] [--since <date>]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Detects staleness in two directions:
|
|
141
|
+
- **Code newer than spec:** a source file in `## Source Files` has been modified more recently than the `impl.md` was last verified — the implementation may have drifted from the spec
|
|
142
|
+
- **Spec newer than implementation:** a `usecase.md` was reviewed after the corresponding `impl.md` was completed — the implementation may not reflect the latest spec
|
|
143
|
+
|
|
144
|
+
Run this before a release or when you suspect specs have drifted from the code.
|
|
145
|
+
|
|
146
|
+
### `taproot overview`
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
taproot overview
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Regenerates `taproot/OVERVIEW.md` — a compact hierarchy summary with clickable links to every intent, behaviour, and impl in the tree. The overview is the agent's entry point for orientation: agent skills read it to understand the shape of the project before drilling into individual files.
|
|
153
|
+
|
|
154
|
+
### `taproot plan`
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
taproot plan [--format tree|json]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Surfaces unimplemented behaviours as work items, ordered by priority (intents with more coverage are prioritized to reach completion). Useful for sprint planning or for orienting a new contributor. The `/tr-plan` skill provides an AI-driven version with more context.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Definition of Done
|
|
165
|
+
|
|
166
|
+
### `taproot dod`
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
taproot dod [impl-path] [--dry-run]
|
|
170
|
+
taproot dod <impl-path> --resolve <condition> --note "<text>" [--resolve <condition> --note "<text>" ...]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Runs all configured DoD conditions from `.taproot/settings.yaml` against the specified implementation (or all implementations if no path is given). If all conditions pass and an `impl-path` is provided, marks the impl `complete`, records the results in `## DoD Resolutions`, and automatically advances the parent `usecase.md` state from `specified` to `implemented` if it hasn't been already.
|
|
174
|
+
|
|
175
|
+
Use `--resolve`/`--note` to record agent resolutions for agent-driven conditions (e.g. `document-current`, `check-if-affected`, `check-if-affected-by`). Multiple pairs can be supplied in a single invocation — conditions are paired with notes by position.
|
|
176
|
+
|
|
177
|
+
See [Configuration](configuration.md) for how to define DoD conditions.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Pre-commit Hook
|
|
182
|
+
|
|
183
|
+
### `taproot commithook`
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
taproot commithook
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Classifies the staged files and runs the appropriate quality gate. Installed by `taproot init --with-hooks` as the content of `.git/hooks/pre-commit` — you do not call this directly.
|
|
190
|
+
|
|
191
|
+
The hook uses a three-tier classification, where the implementation tier is detected by **reverse-lookup**: the hook walks all `impl.md` files in `taproot/` and builds a map of every source file path listed in their `## Source Files` sections. Any staged file that appears in this map triggers the implementation tier. Files not tracked by any `impl.md` (e.g. `.gitignore`, CI configs) always pass as plain commits.
|
|
192
|
+
|
|
193
|
+
| Staged files | Gate applied |
|
|
194
|
+
|---|---|
|
|
195
|
+
| Only hierarchy files (`intent.md`, `usecase.md`) | `validate-structure` + `validate-format` — hierarchy must be valid before the commit lands |
|
|
196
|
+
| Only `impl.md` (no source files in map) | Definition of Ready — the parent `usecase.md` must be in `specified` state and have `## Flow` and `## Related` sections |
|
|
197
|
+
| Source files found in map + `impl.md` staged | Verify only `## Status` (and `## DoD Resolutions`) changed in `impl.md`; then run DoD |
|
|
198
|
+
| Source files found in map but `impl.md` NOT staged | **Blocked** — "Stage `impl.md` alongside your source files. No implementation commit should proceed without its traceability record." |
|
|
199
|
+
| No tracked source files, no hierarchy or impl files | No checks; commit proceeds |
|
|
200
|
+
|
|
201
|
+
The DoR gate prevents committing an implementation record before the behaviour is fully specified. The DoD gate prevents marking an implementation complete without passing the quality checks defined in `.taproot/settings.yaml`.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Commit Convention
|
|
206
|
+
|
|
207
|
+
Link commits to implementations using the conventional tag format:
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
taproot(<intent>/<behaviour>/<impl>): <message>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
For example:
|
|
214
|
+
```
|
|
215
|
+
taproot(password-reset/request-reset/email-trigger): add rate limiting
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Or use a commit trailer:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
fix: handle missing user gracefully
|
|
222
|
+
|
|
223
|
+
Taproot: password-reset/request-reset/email-trigger
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
After tagging commits, run `taproot link-commits` to update the `## Commits` section of the corresponding `impl.md` automatically. The CI integration (see [Configuration](configuration.md)) can run this on every merge.
|
package/docs/concepts.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Document Types
|
|
2
|
+
|
|
3
|
+
Taproot uses three layers, each represented by a markdown file. Every layer answers a different question:
|
|
4
|
+
|
|
5
|
+
| Layer | File | Question |
|
|
6
|
+
|-------|------|----------|
|
|
7
|
+
| Intent | `intent.md` | *Why* does this feature exist, and for whom? |
|
|
8
|
+
| Behaviour | `usecase.md` | *What* does the system do — observable, testable actions? |
|
|
9
|
+
| Implementation | `impl.md` | *How* is this built — code, tests, design decisions? |
|
|
10
|
+
|
|
11
|
+
This separation matters because the layers evolve at different rates. Business goals (intents) are stable. Specific use cases (behaviours) change when users give feedback. Implementations change every sprint. Keeping them separate means a code refactor doesn't invalidate a behaviour spec, and a scope change doesn't orphan an implementation.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Intent (`intent.md`)
|
|
16
|
+
|
|
17
|
+
An intent captures a business goal — the *why* behind a feature. It is written from a business perspective, not a technical one. The question it answers is: "Why are we building this at all, and how will we know it's done?"
|
|
18
|
+
|
|
19
|
+
An intent contains:
|
|
20
|
+
|
|
21
|
+
- **Goal** — one or two sentences on what outcome the feature delivers for the business or user
|
|
22
|
+
- **Stakeholders** — who cares about this and why; their priorities often conflict, and naming them makes trade-offs explicit
|
|
23
|
+
- **Success Criteria** — concrete, checkable statements; these become the acceptance criteria for the entire feature tree below this intent
|
|
24
|
+
- **Constraints** — scope boundaries: things this feature must not do, or must be compatible with. Constraints are not the same as NFR criteria — they define the intent's boundaries, not quality thresholds on specific behaviours (e.g. "must not reveal whether an email is registered" is a constraint; "all endpoints must respond within 500ms" is a cross-cutting NFR and belongs in `quality-gates/`)
|
|
25
|
+
- **Behaviours** — auto-maintained list of child behaviour specs (managed by `taproot update`)
|
|
26
|
+
- **Status** — lifecycle state: `draft` → `active` → `achieved` → `deprecated`
|
|
27
|
+
|
|
28
|
+
```markdown
|
|
29
|
+
# Intent: Password Reset Without Support Contact
|
|
30
|
+
|
|
31
|
+
## Goal
|
|
32
|
+
Enable users to reset their password without contacting support,
|
|
33
|
+
while preventing unauthorized account access.
|
|
34
|
+
|
|
35
|
+
## Stakeholders
|
|
36
|
+
- Product: Jane — reduce support ticket volume by 30%
|
|
37
|
+
- Security: team — prevent account takeover via reset flow
|
|
38
|
+
|
|
39
|
+
## Success Criteria
|
|
40
|
+
- [ ] Users can request a reset link via email
|
|
41
|
+
- [ ] Reset links expire after 15 minutes
|
|
42
|
+
- [ ] Failed attempts are rate-limited to prevent brute force
|
|
43
|
+
|
|
44
|
+
## Constraints
|
|
45
|
+
- Must not reveal whether an email address is registered (prevent enumeration)
|
|
46
|
+
|
|
47
|
+
## Behaviours <!-- taproot-managed -->
|
|
48
|
+
- [Request Password Reset](./request-reset/usecase.md)
|
|
49
|
+
- [Validate Reset Token](./validate-token/usecase.md)
|
|
50
|
+
|
|
51
|
+
## Status
|
|
52
|
+
- **State:** active
|
|
53
|
+
- **Created:** 2024-01-15
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Behaviour (`usecase.md`)
|
|
59
|
+
|
|
60
|
+
A behaviour is one observable thing the system does — from the perspective of an actor trying to accomplish something. It is written in UseCase format: preconditions, main flow, alternate flows, postconditions.
|
|
61
|
+
|
|
62
|
+
A behaviour spec is the contract between the intent (business goals) and the implementation (code). If you write the spec well, you can implement it without asking the product owner any more questions. If you write the code first and add a spec later, it reveals whether the code actually does what was intended.
|
|
63
|
+
|
|
64
|
+
A behaviour contains:
|
|
65
|
+
|
|
66
|
+
- **Actor** — who or what triggers this behaviour (a user, a scheduled job, another service)
|
|
67
|
+
- **Preconditions** — what must be true before this can happen
|
|
68
|
+
- **Main Flow** — the steps in the happy path, written as active-voice actions (subject + verb + object)
|
|
69
|
+
- **Alternate Flows** — named variations: what if the user cancels? what if a network call fails?
|
|
70
|
+
- **Postconditions** — what is true after successful completion (these should map to success criteria in the parent intent)
|
|
71
|
+
- **Error Conditions** — unrecoverable failures with specific triggers and specific system responses
|
|
72
|
+
- **Flow** — a Mermaid diagram of the main flow; the human-readable visual contract
|
|
73
|
+
- **Related** — other behaviours that share an actor, have precondition overlap, or must precede/follow this one
|
|
74
|
+
- **Implementations** — auto-maintained list of child impl records (managed by `taproot update`)
|
|
75
|
+
- **Status** — lifecycle state: `proposed` → `specified` → `implemented` → `tested` → `deprecated` (or `deferred` to park without deleting)
|
|
76
|
+
|
|
77
|
+
```markdown
|
|
78
|
+
# Behaviour: Request Password Reset
|
|
79
|
+
|
|
80
|
+
## Actor
|
|
81
|
+
Registered user who has forgotten their password
|
|
82
|
+
|
|
83
|
+
## Preconditions
|
|
84
|
+
- User account exists and is not locked
|
|
85
|
+
- User is not currently logged in
|
|
86
|
+
|
|
87
|
+
## Main Flow
|
|
88
|
+
1. User navigates to the login page and clicks "Forgot password"
|
|
89
|
+
2. User enters their email address and submits
|
|
90
|
+
3. System validates the email address format
|
|
91
|
+
4. System checks whether the email exists in the database
|
|
92
|
+
5. System generates a time-limited reset token and sends an email
|
|
93
|
+
6. System displays a "Check your email" confirmation page
|
|
94
|
+
|
|
95
|
+
## Alternate Flows
|
|
96
|
+
|
|
97
|
+
### Email not registered
|
|
98
|
+
- **Trigger:** Email address not found in the database at step 4
|
|
99
|
+
- **Steps:**
|
|
100
|
+
1. System displays the same "Check your email" confirmation (prevent enumeration)
|
|
101
|
+
2. No email is sent
|
|
102
|
+
|
|
103
|
+
### Rate limit exceeded
|
|
104
|
+
- **Trigger:** More than 3 reset requests from the same email within an hour
|
|
105
|
+
- **Steps:**
|
|
106
|
+
1. System returns 429 and displays a "Try again later" message
|
|
107
|
+
|
|
108
|
+
## Postconditions
|
|
109
|
+
- A reset token exists in the database, expiring in 15 minutes
|
|
110
|
+
- A reset email has been sent to the provided address (if the account existed)
|
|
111
|
+
|
|
112
|
+
## Error Conditions
|
|
113
|
+
- **Email service unavailable:** System shows an inline error, does not expose service details, preserves the form so the user can retry
|
|
114
|
+
|
|
115
|
+
## Flow
|
|
116
|
+
```mermaid
|
|
117
|
+
sequenceDiagram
|
|
118
|
+
User->>LoginPage: clicks "Forgot password"
|
|
119
|
+
User->>System: submits email address
|
|
120
|
+
System->>DB: check email exists
|
|
121
|
+
alt email found
|
|
122
|
+
System->>EmailService: send reset token
|
|
123
|
+
System-->>User: "Check your email"
|
|
124
|
+
else email not found
|
|
125
|
+
System-->>User: "Check your email" (same message)
|
|
126
|
+
end
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Related
|
|
130
|
+
- `./validate-token/usecase.md` — must follow this flow; consumes the token generated here
|
|
131
|
+
|
|
132
|
+
## Acceptance Criteria
|
|
133
|
+
|
|
134
|
+
**AC-1: Reset email sent**
|
|
135
|
+
- Given the user has a registered account and is not logged in
|
|
136
|
+
- When they submit their email address on the forgot-password form
|
|
137
|
+
- Then the system sends a reset email and displays "Check your email"
|
|
138
|
+
|
|
139
|
+
**AC-2: Unregistered email — same confirmation shown**
|
|
140
|
+
- Given the email address is not registered
|
|
141
|
+
- When the user submits it on the forgot-password form
|
|
142
|
+
- Then the system displays "Check your email" without sending an email
|
|
143
|
+
|
|
144
|
+
**AC-3: Rate limit exceeded**
|
|
145
|
+
- Given the user has already requested 3 resets in the last hour
|
|
146
|
+
- When they submit the form again
|
|
147
|
+
- Then the system returns a 429 response and displays "Try again later"
|
|
148
|
+
|
|
149
|
+
**NFR-1: Reset email delivery time**
|
|
150
|
+
- Given normal mail service conditions
|
|
151
|
+
- When the system sends a password reset email
|
|
152
|
+
- Then the email is delivered within 60 seconds (p95)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
NFR criteria (`**NFR-N:**`) express *how well* the system must perform a behaviour — quality constraints with measurable thresholds. They live in the same `## Acceptance Criteria` section, after the functional ACs. The `NFR-` prefix is distinct from `AC-` so IDs never collide.
|
|
156
|
+
|
|
157
|
+
A measurable threshold is one of:
|
|
158
|
+
- A number with a unit (`200ms`, `60%`, `500 concurrent users`)
|
|
159
|
+
- A named standard (`WCAG 2.1 AA`, `PCI DSS 4.0`)
|
|
160
|
+
- A testable boolean condition (`account is locked`, `notification email is sent`)
|
|
161
|
+
|
|
162
|
+
ISO 25010 provides a useful taxonomy for NFR categories: **performance efficiency**, **reliability**, **security**, **maintainability**, **portability**, **compatibility**, **interaction capability**. Teams are not required to use these names — any consistent label works.
|
|
163
|
+
|
|
164
|
+
```markdown
|
|
165
|
+
## Implementations <!-- taproot-managed -->
|
|
166
|
+
- [Email Trigger](./email-trigger/impl.md)
|
|
167
|
+
|
|
168
|
+
## Status
|
|
169
|
+
- **State:** implemented
|
|
170
|
+
- **Created:** 2024-01-20
|
|
171
|
+
- **Last reviewed:** 2024-03-01
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Implementation (`impl.md`)
|
|
177
|
+
|
|
178
|
+
An implementation record is the traceability bridge between a behaviour spec and actual code. It is not a technical spec — that belongs in code comments or ADRs. Instead it answers: "Where in the codebase does this behaviour live, what decisions were made, and is it still current?"
|
|
179
|
+
|
|
180
|
+
An implementation contains:
|
|
181
|
+
|
|
182
|
+
- **Behaviour** — relative path to the parent `usecase.md`
|
|
183
|
+
- **Design Decisions** — choices made during implementation that aren't obvious from the code; the *why* behind architectural choices
|
|
184
|
+
- **Source Files** — the files that constitute this implementation; used by `sync-check` to detect staleness
|
|
185
|
+
- **Commits** — linked by `taproot link-commits` automatically from conventional commit messages
|
|
186
|
+
- **Tests** — test files that verify this behaviour; used by DoD checks
|
|
187
|
+
- **DoD Resolutions** — recorded outcomes of Definition of Done checks
|
|
188
|
+
- **Status** — lifecycle state: `planned` → `in-progress` → `complete` → `needs-rework`
|
|
189
|
+
|
|
190
|
+
```markdown
|
|
191
|
+
# Implementation: Email Trigger
|
|
192
|
+
|
|
193
|
+
## Behaviour
|
|
194
|
+
../usecase.md
|
|
195
|
+
|
|
196
|
+
## Design Decisions
|
|
197
|
+
- Generic error message regardless of email existence (prevent enumeration attacks)
|
|
198
|
+
- Rate limit: 3 requests per email per hour, enforced via Redis with TTL
|
|
199
|
+
- Token stored as a bcrypt hash — raw token only in the email, never logged
|
|
200
|
+
|
|
201
|
+
## Source Files
|
|
202
|
+
- `src/auth/password-reset.ts`
|
|
203
|
+
- `src/auth/password-reset-email.ts`
|
|
204
|
+
- `src/auth/reset-token.ts`
|
|
205
|
+
|
|
206
|
+
## Commits
|
|
207
|
+
- `a1b2c3d` — Add password reset request endpoint
|
|
208
|
+
- `f4e5d6c` — Add rate limiting to reset requests
|
|
209
|
+
|
|
210
|
+
## Tests
|
|
211
|
+
- `test/auth/password-reset.test.ts`
|
|
212
|
+
- `test/auth/reset-token.test.ts`
|
|
213
|
+
|
|
214
|
+
## Status
|
|
215
|
+
- **State:** complete
|
|
216
|
+
- **Created:** 2024-02-01
|
|
217
|
+
- **Last verified:** 2024-03-01
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Nesting
|
|
223
|
+
|
|
224
|
+
Behaviours can contain sub-behaviours. Use sub-behaviours when a single UseCase has distinct phases with different actors, different preconditions, or enough complexity to warrant separate testing. Sub-behaviours are still leaves relative to their parent behaviour but can have their own implementations.
|
|
225
|
+
|
|
226
|
+
Implementations are always leaves — they never contain child folders.
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
taproot/
|
|
230
|
+
└── password-reset/
|
|
231
|
+
├── intent.md
|
|
232
|
+
├── request-reset/
|
|
233
|
+
│ ├── usecase.md
|
|
234
|
+
│ └── email-trigger/
|
|
235
|
+
│ └── impl.md
|
|
236
|
+
└── validate-token/
|
|
237
|
+
├── usecase.md
|
|
238
|
+
├── token-validation/
|
|
239
|
+
│ └── impl.md
|
|
240
|
+
└── rate-limit-check/ ← sub-behaviour
|
|
241
|
+
├── usecase.md
|
|
242
|
+
└── redis-check/
|
|
243
|
+
└── impl.md
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Document States
|
|
247
|
+
|
|
248
|
+
Each document has a `State` field that tracks its lifecycle. States are validated by `taproot validate-format` and enforced by the pre-commit hook.
|
|
249
|
+
|
|
250
|
+
| State | Intent | Behaviour | Implementation |
|
|
251
|
+
|-------|--------|-----------|----------------|
|
|
252
|
+
| `draft` | Being written, not ready for dev | — | — |
|
|
253
|
+
| `active` | Feature in progress | — | — |
|
|
254
|
+
| `proposed` | — | Idea, not yet specified | — |
|
|
255
|
+
| `specified` | — | Fully specced, ready to implement | — |
|
|
256
|
+
| `implemented` | — | Code exists | — |
|
|
257
|
+
| `tested` | — | Tests passing and reviewed | — |
|
|
258
|
+
| `planned` | — | — | Scoped, not started |
|
|
259
|
+
| `in-progress` | — | — | Being implemented |
|
|
260
|
+
| `complete` | — | — | Done; DoD passed |
|
|
261
|
+
| `needs-rework` | — | — | Implementation failed DoD |
|
|
262
|
+
| `achieved` | All behaviours complete | — | — |
|
|
263
|
+
| `deprecated` | No longer relevant | No longer relevant | — |
|
|
264
|
+
| `deferred` | — | Not pursuing for now | Not pursuing for now |
|
|
265
|
+
|
|
266
|
+
The pre-commit hook uses state transitions to enforce workflow gates: you cannot declare an implementation without the parent behaviour being in `specified` state, and you cannot mark an implementation complete without passing Definition of Done checks.
|
|
267
|
+
|
|
268
|
+
**Deferred items** are consciously parked — `deferred` is not a synonym for `proposed` (not started) or `needs-rework` (broken). It means "we explored or attempted this and decided to stop for now." Deferred behaviours are excluded from `taproot plan` candidates, and deferred implementations are excluded from `check-orphans` errors (missing source files, missing test files). Both are reported in a separate Parked section by `tr-status`. Use `deprecated` (not `deferred`) on intents.
|