@reddb-io/red-skills-dev 2.75.2
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/LICENSE +202 -0
- package/README.md +30 -0
- package/package.json +32 -0
- package/skills/engineering/README.md +36 -0
- package/skills/engineering/_report-runtime/WRAPPER.md +47 -0
- package/skills/engineering/afk/AGENT-PROMPT.md +295 -0
- package/skills/engineering/afk/SAFETY.md +103 -0
- package/skills/engineering/afk/SKILL.md +160 -0
- package/skills/engineering/afk/TROUBLESHOOTING.md +260 -0
- package/skills/engineering/afk/actions-lane.md +213 -0
- package/skills/engineering/afk/bin/README.md +29 -0
- package/skills/engineering/afk/bin/afk.mjs +51 -0
- package/skills/engineering/afk/defaults/cargo-pre-worktree.sh +37 -0
- package/skills/engineering/afk/defaults/envelope-post-attempt.sh +47 -0
- package/skills/engineering/afk/defaults/gradle-pre-worktree.sh +42 -0
- package/skills/engineering/afk/defaults/heartbeat-post-attempt.sh +43 -0
- package/skills/engineering/afk/defaults/validation-post-merge.sh +77 -0
- package/skills/engineering/afk/detectors/README.md +72 -0
- package/skills/engineering/afk/detectors/cargo.sh +27 -0
- package/skills/engineering/afk/detectors/gradle.sh +35 -0
- package/skills/engineering/afk/docs/BOOT-SWEEPS.md +81 -0
- package/skills/engineering/afk/docs/CONFIG.md +238 -0
- package/skills/engineering/afk/docs/ENVELOPE.md +177 -0
- package/skills/engineering/afk/docs/HANDOFF.md +70 -0
- package/skills/engineering/afk/docs/LIVENESS.md +56 -0
- package/skills/engineering/afk/docs/OPERATIONS.md +409 -0
- package/skills/engineering/afk/examples/only-mine.sh +32 -0
- package/skills/engineering/afk/examples/red-afk-attempt-action.yml +53 -0
- package/skills/engineering/afk/examples/rs-afk-attempt.yml +76 -0
- package/skills/engineering/afk/fleet.md +127 -0
- package/skills/engineering/afk/hooks/red-cargo +44 -0
- package/skills/engineering/afk/hooks/red-envelope +50 -0
- package/skills/engineering/afk/hooks/red-gradle +50 -0
- package/skills/engineering/afk/hooks/red-heartbeat +46 -0
- package/skills/engineering/afk/hooks/red-validation +79 -0
- package/skills/engineering/afk/monitor.md +170 -0
- package/skills/engineering/afk/runner-claude-minimax.md +71 -0
- package/skills/engineering/afk/runner-claude.md +67 -0
- package/skills/engineering/afk/runner-codex.md +138 -0
- package/skills/engineering/afk/runner-hermes.md +40 -0
- package/skills/engineering/afk/runner-opencode.md +142 -0
- package/skills/engineering/ask-red/SKILL.md +137 -0
- package/skills/engineering/audit-skills/SKILL.md +46 -0
- package/skills/engineering/code-review/SKILL.md +106 -0
- package/skills/engineering/context/SKILL.md +142 -0
- package/skills/engineering/curate/SKILL.md +133 -0
- package/skills/engineering/daily-review/SKILL.md +68 -0
- package/skills/engineering/dashboard/SKILL.md +44 -0
- package/skills/engineering/diagnose/SKILL.md +155 -0
- package/skills/engineering/diagnose/scripts/hitl-loop.template.sh +41 -0
- package/skills/engineering/go/SKILL.md +144 -0
- package/skills/engineering/go/TROUBLESHOOTING.md +81 -0
- package/skills/engineering/ground-truth/SKILL.md +84 -0
- package/skills/engineering/hitl/SKILL.md +171 -0
- package/skills/engineering/hitl/TROUBLESHOOTING.md +76 -0
- package/skills/engineering/implement/SKILL.md +69 -0
- package/skills/engineering/improve-codebase-architecture/DEEPENING.md +37 -0
- package/skills/engineering/improve-codebase-architecture/INTERFACE-DESIGN.md +44 -0
- package/skills/engineering/improve-codebase-architecture/LANGUAGE.md +53 -0
- package/skills/engineering/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/engineering/model-tier-policy/SKILL.md +83 -0
- package/skills/engineering/prototype/LOGIC.md +79 -0
- package/skills/engineering/prototype/SKILL.md +30 -0
- package/skills/engineering/prototype/UI.md +112 -0
- package/skills/engineering/red-doctor/APPLY.md +35 -0
- package/skills/engineering/red-doctor/SKILL.md +172 -0
- package/skills/engineering/red-gains/SKILL.md +49 -0
- package/skills/engineering/red-setup/INTERVIEW.md +266 -0
- package/skills/engineering/red-setup/ISSUE-SWEEP.md +18 -0
- package/skills/engineering/red-setup/REFERENCE.md +35 -0
- package/skills/engineering/red-setup/SKILL.md +58 -0
- package/skills/engineering/red-setup/WORKFLOWS.md +74 -0
- package/skills/engineering/red-setup/WRITE-CONTRACT.md +151 -0
- package/skills/engineering/red-setup/config-template.yaml +174 -0
- package/skills/engineering/red-setup/domain.md +52 -0
- package/skills/engineering/red-setup/issue-tracker-github.md +88 -0
- package/skills/engineering/red-setup/rsp-retirement-runbook.md +35 -0
- package/skills/engineering/red-setup/scripts/install-runtime-shim.sh +189 -0
- package/skills/engineering/red-setup/triage-labels.md +176 -0
- package/skills/engineering/red-setup/workflows/red-issues-needs-triage.yml +36 -0
- package/skills/engineering/red-statusline/HOST-NOTES.md +180 -0
- package/skills/engineering/red-statusline/SKILL.md +58 -0
- package/skills/engineering/report-bug/SKILL.md +155 -0
- package/skills/engineering/resolving-merge-conflicts/SKILL.md +143 -0
- package/skills/engineering/retake/SKILL.md +184 -0
- package/skills/engineering/review-adrs/SKILL.md +125 -0
- package/skills/engineering/start/ADR-FORMAT.md +47 -0
- package/skills/engineering/start/CONTEXT-FORMAT.md +77 -0
- package/skills/engineering/start/DOC-LANDING-FINALIZER.md +9 -0
- package/skills/engineering/start/SKILL.md +179 -0
- package/skills/engineering/tdd/SKILL.md +96 -0
- package/skills/engineering/tdd/deep-modules.md +33 -0
- package/skills/engineering/tdd/interface-design.md +31 -0
- package/skills/engineering/tdd/mocking.md +59 -0
- package/skills/engineering/tdd/tests.md +85 -0
- package/skills/engineering/to-spec/SKILL.md +104 -0
- package/skills/engineering/to-tickets/SKILL.md +158 -0
- package/skills/engineering/triage/AGENT-BRIEF.md +207 -0
- package/skills/engineering/triage/OUT-OF-SCOPE.md +101 -0
- package/skills/engineering/triage/SKILL.md +176 -0
- package/skills/engineering/verify/SKILL.md +136 -0
- package/skills/engineering/wayfinder/SKILL.md +128 -0
- package/skills/engineering/zoom-out/SKILL.md +51 -0
- package/skills/engineering/zoom-out/scripts/graph-mode-plumbing.sh +36 -0
- package/skills/engineering/zoom-out/scripts/tests/contract.test.sh +168 -0
- package/skills/knowledge/README.md +5 -0
- package/skills/knowledge/research/SKILL.md +76 -0
- package/skills/misc/README.md +8 -0
- package/skills/misc/branch-lock/SKILL.md +187 -0
- package/skills/misc/branch-lock/scripts/branch-lock-hook.sh +113 -0
- package/skills/misc/branch-lock/scripts/branch-lock-session-start.sh +81 -0
- package/skills/misc/branch-lock/scripts/branch-lock.sh +68 -0
- package/skills/misc/branch-lock/scripts/lib/dev-config.sh +126 -0
- package/skills/misc/branch-lock/scripts/lib/git-command-classifier.sh +198 -0
- package/skills/misc/branch-lock/scripts/lib/lock-store.sh +67 -0
- package/skills/misc/branch-lock/scripts/lib/scope-resolver.sh +43 -0
- package/skills/misc/branch-lock/scripts/tests/branch-lock-cli.test.sh +114 -0
- package/skills/misc/branch-lock/scripts/tests/claude-plugin-hook.test.sh +117 -0
- package/skills/misc/branch-lock/scripts/tests/codex-hook.test.sh +229 -0
- package/skills/misc/branch-lock/scripts/tests/dev-config.test.sh +99 -0
- package/skills/misc/branch-lock/scripts/tests/git-command-classifier.test.sh +187 -0
- package/skills/misc/branch-lock/scripts/tests/lock-store.test.sh +90 -0
- package/skills/misc/branch-lock/scripts/tests/scope-resolver.test.sh +63 -0
- package/skills/misc/branch-lock/scripts/tests/session-start.test.sh +123 -0
- package/skills/misc/git-guardrails-claude-code/SKILL.md +105 -0
- package/skills/misc/git-guardrails-claude-code/scripts/block-dangerous-git.sh +231 -0
- package/skills/misc/git-guardrails-claude-code/scripts/tests/block-dangerous-git.test.sh +143 -0
- package/skills/misc/migrate-to-shoehorn/SKILL.md +96 -0
- package/skills/misc/setup-pre-commit/SKILL.md +94 -0
- package/skills/productivity/README.md +8 -0
- package/skills/productivity/ff/SKILL.md +116 -0
- package/skills/productivity/handoff/SKILL.md +37 -0
- package/skills/productivity/reflect/SKILL.md +18 -0
- package/skills/productivity/write-a-skill/SKILL.md +190 -0
- package/skills/productivity/write-a-skill/WRITING-STYLE.md +62 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd
|
|
3
|
+
description: Test-driven development with a red → green loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green", wants integration tests, or asks for test-first development.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test-Driven Development
|
|
7
|
+
|
|
8
|
+
<what-to-do>
|
|
9
|
+
|
|
10
|
+
**Loop rule: red before green, one slice at a time, tests only at pre-agreed seams.** One failing test, the minimum code to pass it, nothing more; repeat until every listed behaviour is covered.
|
|
11
|
+
|
|
12
|
+
The loop has no prescribed steps — pick the next uncovered behaviour, write one failing test, write the minimum code to pass it, check the cycle checklist, move on.
|
|
13
|
+
|
|
14
|
+
### Seams
|
|
15
|
+
|
|
16
|
+
A **seam** is the public boundary you test at. Agree on seams before writing the first test — no test at an unconfirmed seam. Each seam carries one behaviour per cycle.
|
|
17
|
+
|
|
18
|
+
### Hard rules — do not break these
|
|
19
|
+
|
|
20
|
+
- ❌ Do **not** write all tests first then all implementation ("horizontal slicing"). This produces tests of *imagined* behaviour, not *actual* behaviour. See `<supporting-info>` for why.
|
|
21
|
+
- ❌ Do **not** write more code than the current test requires. No speculative features, no anticipating the next test.
|
|
22
|
+
- ❌ Do **not** test private methods or mock internal collaborators. Tests must go through the public interface.
|
|
23
|
+
- ❌ Do **not** verify behaviour through external means (querying the database directly when an API exists, etc.).
|
|
24
|
+
- ❌ Do **not** write tautological assertions — expected values must come from an independent source of truth (a literal, a worked example, the spec), not by recomputing the same way the code does. `expect(add(a, b)).toBe(a + b)` always passes and can never catch a bug.
|
|
25
|
+
- ✅ **Do** confirm seams before writing the first test — no test at an unconfirmed seam.
|
|
26
|
+
- ✅ **Do** check each cycle against the per-cycle checklist before moving to the next test.
|
|
27
|
+
- ✅ **Do** keep tests focused on observable behaviour through public interfaces.
|
|
28
|
+
- ✅ **Do** use the project's domain vocabulary in test names and interface design.
|
|
29
|
+
|
|
30
|
+
### Per-cycle checklist
|
|
31
|
+
|
|
32
|
+
Before declaring a cycle done, confirm every box:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
[ ] A reader can tell what the system does from this test alone — not how it does it
|
|
36
|
+
[ ] The test reaches the system only through its public interface
|
|
37
|
+
[ ] This test would still pass if the internal implementation were replaced entirely
|
|
38
|
+
[ ] No code was added beyond what this test required
|
|
39
|
+
[ ] No features were anticipated beyond the current test
|
|
40
|
+
[ ] Expected values come from a literal, worked example, or spec — not recomputed the way the code computes them
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If any box is unchecked, the cycle isn't done — fix it before moving on.
|
|
44
|
+
|
|
45
|
+
Once all tests are GREEN, refactoring is a separate concern — run `/code-review` on the branch to clean up.
|
|
46
|
+
|
|
47
|
+
</what-to-do>
|
|
48
|
+
|
|
49
|
+
<supporting-info>
|
|
50
|
+
|
|
51
|
+
## Philosophy
|
|
52
|
+
|
|
53
|
+
**Core principle**: Tests verify behaviour through public interfaces, not implementation details. Code can change entirely; tests shouldn't.
|
|
54
|
+
|
|
55
|
+
**Good tests** are integration-style: they exercise real code paths through public APIs. They describe *what* the system does, not *how*. A good test reads like a specification — *"user can checkout with valid cart"* tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
|
|
56
|
+
|
|
57
|
+
**Implementation-coupled tests** are the first bad-test pattern: they mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behaviour hasn't changed.
|
|
58
|
+
|
|
59
|
+
**Tautological tests** are the second bad-test pattern: a test whose assertion is recomputed the way the code computes it passes by construction and proves nothing. `expect(add(a, b)).toBe(a + b)` will always pass regardless of what `add` does — the assertion restates the implementation instead of verifying it against an independent expected value. Expected values must come from a literal, a worked example, or the spec.
|
|
60
|
+
|
|
61
|
+
See [tests.md](tests.md) for examples of both anti-patterns, [mocking.md](mocking.md) for mocking guidelines, [interface-design.md](interface-design.md) for designing for testability.
|
|
62
|
+
|
|
63
|
+
## Seams
|
|
64
|
+
|
|
65
|
+
A **seam** is the public boundary you test at: the interface where you observe behaviour without reaching inside the implementation. Tests live at seams, never against internals.
|
|
66
|
+
|
|
67
|
+
Confirming seams before the first test is how testing effort lands on critical paths and complex logic — not on every edge case. One seam, one test, one implementation per cycle.
|
|
68
|
+
|
|
69
|
+
## Why horizontal slicing is forbidden
|
|
70
|
+
|
|
71
|
+
Treating RED as "write all tests" and GREEN as "write all code" produces **crap tests**:
|
|
72
|
+
|
|
73
|
+
- Tests written in bulk test *imagined* behaviour, not *actual* behaviour
|
|
74
|
+
- You end up testing the *shape* of things (data structures, signatures) rather than user-facing behaviour
|
|
75
|
+
- Tests become insensitive to real changes — they pass when behaviour breaks, fail when behaviour is fine
|
|
76
|
+
- You outrun your headlights, committing to test structure before understanding the implementation
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
WRONG (horizontal):
|
|
80
|
+
RED: test1, test2, test3, test4, test5
|
|
81
|
+
GREEN: impl1, impl2, impl3, impl4, impl5
|
|
82
|
+
|
|
83
|
+
RIGHT (vertical):
|
|
84
|
+
RED→GREEN: test1→impl1
|
|
85
|
+
RED→GREEN: test2→impl2
|
|
86
|
+
RED→GREEN: test3→impl3
|
|
87
|
+
…
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behaviour matters and how to verify it.
|
|
91
|
+
|
|
92
|
+
## Planning aids
|
|
93
|
+
|
|
94
|
+
When listing behaviours, also identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation) and design interfaces for [testability](interface-design.md). These choices are easier to make before any code exists.
|
|
95
|
+
|
|
96
|
+
</supporting-info>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Deep Modules
|
|
2
|
+
|
|
3
|
+
From "A Philosophy of Software Design":
|
|
4
|
+
|
|
5
|
+
**Deep module** = small interface + lots of implementation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────┐
|
|
9
|
+
│ Small Interface │ ← Few methods, simple params
|
|
10
|
+
├─────────────────────┤
|
|
11
|
+
│ │
|
|
12
|
+
│ │
|
|
13
|
+
│ Deep Implementation│ ← Complex logic hidden
|
|
14
|
+
│ │
|
|
15
|
+
│ │
|
|
16
|
+
└─────────────────────┘
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Shallow module** = large interface + little implementation (avoid)
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────┐
|
|
23
|
+
│ Large Interface │ ← Many methods, complex params
|
|
24
|
+
├─────────────────────────────────┤
|
|
25
|
+
│ Thin Implementation │ ← Just passes through
|
|
26
|
+
└─────────────────────────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
When designing interfaces, ask:
|
|
30
|
+
|
|
31
|
+
- Can I reduce the number of methods?
|
|
32
|
+
- Can I simplify the parameters?
|
|
33
|
+
- Can I hide more complexity inside?
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Interface Design for Testability
|
|
2
|
+
|
|
3
|
+
Good interfaces make testing natural:
|
|
4
|
+
|
|
5
|
+
1. **Accept dependencies, don't create them**
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// Testable
|
|
9
|
+
function processOrder(order, paymentGateway) {}
|
|
10
|
+
|
|
11
|
+
// Hard to test
|
|
12
|
+
function processOrder(order) {
|
|
13
|
+
const gateway = new StripeGateway();
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. **Return results, don't produce side effects**
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// Testable
|
|
21
|
+
function calculateDiscount(cart): Discount {}
|
|
22
|
+
|
|
23
|
+
// Hard to test
|
|
24
|
+
function applyDiscount(cart): void {
|
|
25
|
+
cart.total -= discount;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
3. **Small surface area**
|
|
30
|
+
- Fewer methods = fewer tests needed
|
|
31
|
+
- Fewer params = simpler test setup
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# When to Mock
|
|
2
|
+
|
|
3
|
+
Mock at **system boundaries** only:
|
|
4
|
+
|
|
5
|
+
- External APIs (payment, email, etc.)
|
|
6
|
+
- Databases (sometimes - prefer test DB)
|
|
7
|
+
- Time/randomness
|
|
8
|
+
- File system (sometimes)
|
|
9
|
+
|
|
10
|
+
Don't mock:
|
|
11
|
+
|
|
12
|
+
- Your own classes/modules
|
|
13
|
+
- Internal collaborators
|
|
14
|
+
- Anything you control
|
|
15
|
+
|
|
16
|
+
## Designing for Mockability
|
|
17
|
+
|
|
18
|
+
At system boundaries, design interfaces that are easy to mock:
|
|
19
|
+
|
|
20
|
+
**1. Use dependency injection**
|
|
21
|
+
|
|
22
|
+
Pass external dependencies in rather than creating them internally:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// Easy to mock
|
|
26
|
+
function processPayment(order, paymentClient) {
|
|
27
|
+
return paymentClient.charge(order.total);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Hard to mock
|
|
31
|
+
function processPayment(order) {
|
|
32
|
+
const client = new StripeClient(process.env.STRIPE_KEY);
|
|
33
|
+
return client.charge(order.total);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**2. Prefer SDK-style interfaces over generic fetchers**
|
|
38
|
+
|
|
39
|
+
Create specific functions for each external operation instead of one generic function with conditional logic:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// GOOD: Each function is independently mockable
|
|
43
|
+
const api = {
|
|
44
|
+
getUser: (id) => fetch(`/users/${id}`),
|
|
45
|
+
getOrders: (userId) => fetch(`/users/${userId}/orders`),
|
|
46
|
+
createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// BAD: Mocking requires conditional logic inside the mock
|
|
50
|
+
const api = {
|
|
51
|
+
fetch: (endpoint, options) => fetch(endpoint, options),
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The SDK approach means:
|
|
56
|
+
- Each mock returns one specific shape
|
|
57
|
+
- No conditional logic in test setup
|
|
58
|
+
- Easier to see which endpoints a test exercises
|
|
59
|
+
- Type safety per endpoint
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Good and Bad Tests
|
|
2
|
+
|
|
3
|
+
## Good Tests
|
|
4
|
+
|
|
5
|
+
**Integration-style**: Test through real interfaces, not mocks of internal parts.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// GOOD: Tests observable behavior
|
|
9
|
+
test("user can checkout with valid cart", async () => {
|
|
10
|
+
const cart = createCart();
|
|
11
|
+
cart.add(product);
|
|
12
|
+
const result = await checkout(cart, paymentMethod);
|
|
13
|
+
expect(result.status).toBe("confirmed");
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Characteristics:
|
|
18
|
+
|
|
19
|
+
- Tests behavior users/callers care about
|
|
20
|
+
- Uses public API only
|
|
21
|
+
- Survives internal refactors
|
|
22
|
+
- Describes WHAT, not HOW
|
|
23
|
+
- One logical assertion per test
|
|
24
|
+
|
|
25
|
+
## Bad Tests
|
|
26
|
+
|
|
27
|
+
**Implementation-detail tests**: Coupled to internal structure.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// BAD: Tests implementation details
|
|
31
|
+
test("checkout calls paymentService.process", async () => {
|
|
32
|
+
const mockPayment = jest.mock(paymentService);
|
|
33
|
+
await checkout(cart, payment);
|
|
34
|
+
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Red flags:
|
|
39
|
+
|
|
40
|
+
- Mocking internal collaborators
|
|
41
|
+
- Testing private methods
|
|
42
|
+
- Asserting on call counts/order
|
|
43
|
+
- Test breaks when refactoring without behavior change
|
|
44
|
+
- Test name describes HOW not WHAT
|
|
45
|
+
- Verifying through external means instead of interface
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// BAD: Bypasses interface to verify
|
|
49
|
+
test("createUser saves to database", async () => {
|
|
50
|
+
await createUser({ name: "Alice" });
|
|
51
|
+
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
|
|
52
|
+
expect(row).toBeDefined();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// GOOD: Verifies through interface
|
|
56
|
+
test("createUser makes user retrievable", async () => {
|
|
57
|
+
const user = await createUser({ name: "Alice" });
|
|
58
|
+
const retrieved = await getUser(user.id);
|
|
59
|
+
expect(retrieved.name).toBe("Alice");
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Tautological Tests
|
|
64
|
+
|
|
65
|
+
**Tautological assertion**: expected value recomputed the way the code computes it — passes by construction and proves nothing.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// BAD: tautological — assertion mirrors the implementation
|
|
69
|
+
test("add returns the sum", () => {
|
|
70
|
+
const a = 3;
|
|
71
|
+
const b = 4;
|
|
72
|
+
expect(add(a, b)).toBe(a + b); // always true regardless of what add does
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// GOOD: expected value from an independent source of truth
|
|
76
|
+
test("add returns the sum", () => {
|
|
77
|
+
expect(add(3, 4)).toBe(7); // 7 is a literal from a worked example
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Red flags:
|
|
82
|
+
|
|
83
|
+
- Expected value is computed using the same operator/function as the implementation
|
|
84
|
+
- Swapping the implementation for any expression that satisfies the same type would still pass
|
|
85
|
+
- The test can never catch an off-by-one or wrong-operator bug
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: to-spec
|
|
3
|
+
description: Turn the current conversation context into a Spec and publish it to the project issue tracker. Use when user wants to create a Spec from the current context.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**Synthesize the current conversation into a Spec and publish it — no interview, no implementation.** Just synthesize what you already know from the conversation.
|
|
7
|
+
|
|
8
|
+
The issue tracker and triage label vocabulary should have been provided to you — run `/red-setup` if not.
|
|
9
|
+
|
|
10
|
+
<what-to-do>
|
|
11
|
+
|
|
12
|
+
## Process
|
|
13
|
+
|
|
14
|
+
1. Explore the repo to understand the current state of the codebase, if you haven't already. Use the project's domain glossary vocabulary throughout the Spec, and respect any ADRs in the area you're touching.
|
|
15
|
+
|
|
16
|
+
2. Sketch out the testing seams for the feature. Prefer existing, high-level seams over new low-level ones. If new seams are needed, propose them at the highest point that can exercise the behavior.
|
|
17
|
+
|
|
18
|
+
Check with the user that these seams match their expectations.
|
|
19
|
+
|
|
20
|
+
**Capture every HITL call** made during the conversation that led to this Spec — testing seam choices, module shape choices, trade-offs the user took a side on, alternatives they rejected, constraints they imposed. These go into the `Human Decisions` section of the template. Do not silently fold them into `Implementation Decisions` — once `/to-tickets` slices this Spec and `/afk` picks up the children, the human's calls become indistinguishable from agent inference unless they are flagged here.
|
|
21
|
+
|
|
22
|
+
3. **Cascade gate — run before publishing.** AFK workers branch from `origin/{base}` and cannot see the primary checkout's working-tree edits, so never publish while docs are unlanded.
|
|
23
|
+
|
|
24
|
+
a. `git fetch origin`, then compare the `.red/` docs (`.red/CONTEXT.md`, `.red/CONTEXT-MAP.md`, `.red/contexts/**`, `.red/adr/**`) between the primary working tree and `origin/{base}` (base resolved lock > pin > main). "Landed" means reachable from `origin/{base}`, not merely present on disk — origin-first comparison, mirroring the `/review-adrs` convention.
|
|
25
|
+
b. **On mismatch:** run the doc-landing procedure from the `/start` end-of-session finalizer (canonized by ADR 0092) first, then continue to step 4.
|
|
26
|
+
c. **If landing is impossible** (no network, no push access): abort — never publish while docs are unlanded. State clearly which files must be landed and stop.
|
|
27
|
+
|
|
28
|
+
4. Write the Spec using the template below, then publish it to the project issue tracker.
|
|
29
|
+
|
|
30
|
+
**Labels on publish:** apply `type:spec` and `needs-slicing`. **Do not apply `ready-for-agent` to a Spec — a Spec is not an implementable unit; `/to-tickets` must slice it first.** `/afk` hard-filters anything tagged `type:spec` so an accidental `ready-for-agent` will be ignored, but the right pre-condition is to not set it in the first place.
|
|
31
|
+
|
|
32
|
+
The next step after publish is `/to-tickets` (manual or scheduled) which consumes `needs-slicing` Specs, generates child issues with `spec:{N}` + `ready-for-agent`, and removes `needs-slicing` from the parent.
|
|
33
|
+
|
|
34
|
+
</what-to-do>
|
|
35
|
+
|
|
36
|
+
<supporting-info>
|
|
37
|
+
|
|
38
|
+
<spec-template>
|
|
39
|
+
|
|
40
|
+
## Problem Statement
|
|
41
|
+
|
|
42
|
+
The problem that the user is facing, from the user's perspective.
|
|
43
|
+
|
|
44
|
+
## Solution
|
|
45
|
+
|
|
46
|
+
The solution to the problem, from the user's perspective.
|
|
47
|
+
|
|
48
|
+
## User Stories
|
|
49
|
+
|
|
50
|
+
A LONG, numbered list of user stories. Each user story should be in the format of:
|
|
51
|
+
|
|
52
|
+
1. As an <actor>, I want a <feature>, so that <benefit>
|
|
53
|
+
|
|
54
|
+
<user-story-example>
|
|
55
|
+
1. As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending
|
|
56
|
+
</user-story-example>
|
|
57
|
+
|
|
58
|
+
This list of user stories should be extremely extensive and cover all aspects of the feature.
|
|
59
|
+
|
|
60
|
+
## Human Decisions
|
|
61
|
+
|
|
62
|
+
Calls the human made during the conversation that produced this Spec. These are load-bearing — they reflect judgement that the agent could not have inferred on its own and must survive into the slicing + implementation phases. One bullet per decision, in this shape:
|
|
63
|
+
|
|
64
|
+
- **Decision:** what was decided
|
|
65
|
+
- **Why:** the reason the human gave
|
|
66
|
+
- **Alternatives considered:** options that were rejected and why (omit if not applicable)
|
|
67
|
+
|
|
68
|
+
If a decision is genuinely just agent inference, it does not belong here — put it in `Implementation Decisions` instead.
|
|
69
|
+
|
|
70
|
+
## Implementation Decisions
|
|
71
|
+
|
|
72
|
+
A list of implementation decisions that were made. This can include:
|
|
73
|
+
|
|
74
|
+
- The modules that will be built/modified
|
|
75
|
+
- The interfaces of those modules that will be modified
|
|
76
|
+
- Technical clarifications from the developer
|
|
77
|
+
- Architectural decisions
|
|
78
|
+
- Schema changes
|
|
79
|
+
- API contracts
|
|
80
|
+
- Specific interactions
|
|
81
|
+
|
|
82
|
+
Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.
|
|
83
|
+
|
|
84
|
+
Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
|
85
|
+
|
|
86
|
+
## Testing Decisions
|
|
87
|
+
|
|
88
|
+
A list of testing decisions that were made. Include:
|
|
89
|
+
|
|
90
|
+
- A description of what makes a good test (only test external behavior, not implementation details)
|
|
91
|
+
- Which seams or modules will be tested
|
|
92
|
+
- Prior art for the tests (i.e. similar types of tests in the codebase)
|
|
93
|
+
|
|
94
|
+
## Out of Scope
|
|
95
|
+
|
|
96
|
+
A description of the things that are out of scope for this Spec.
|
|
97
|
+
|
|
98
|
+
## Further Notes
|
|
99
|
+
|
|
100
|
+
Any further notes about the feature.
|
|
101
|
+
|
|
102
|
+
</spec-template>
|
|
103
|
+
|
|
104
|
+
</supporting-info>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: to-tickets
|
|
3
|
+
description: Break a plan or Spec into independently-grabbable Tickets on the project issue tracker using tracer-bullet vertical slices. Use when user wants to convert a plan into Tickets, create implementation Tickets, or break down work into Tickets.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# To Tickets
|
|
7
|
+
|
|
8
|
+
**Break a plan into independently-grabbable vertical slices and publish them to the issue tracker.**
|
|
9
|
+
|
|
10
|
+
The issue tracker and triage label vocabulary should have been provided to you — if not, ask the user to run `/red-setup` and stop.
|
|
11
|
+
|
|
12
|
+
<what-to-do>
|
|
13
|
+
|
|
14
|
+
### Step 1 — Gather context
|
|
15
|
+
|
|
16
|
+
Work from whatever is already in the conversation context. If the user passes an issue reference (issue number, URL, or path) as an argument, fetch it from the issue tracker and read its **full body and all comments** before continuing.
|
|
17
|
+
|
|
18
|
+
### Step 2 — Explore the codebase (only if you haven't already)
|
|
19
|
+
|
|
20
|
+
Skip if the current conversation already explored. Otherwise: read enough to understand the current state. Issue titles and descriptions **must** use the project's domain glossary vocabulary. Respect ADRs in the area you're touching.
|
|
21
|
+
|
|
22
|
+
### Step 3 — Draft vertical slices
|
|
23
|
+
|
|
24
|
+
Break the plan into **tracer-bullet** issues. Each slice cuts end-to-end through every layer.
|
|
25
|
+
|
|
26
|
+
Apply the rules in `<supporting-info>` literally. The cardinal one: **vertical, not horizontal.**
|
|
27
|
+
|
|
28
|
+
Mark each slice with its routing class:
|
|
29
|
+
|
|
30
|
+
(i) **AFK** — mergeable without human decision
|
|
31
|
+
(ii) **HITL** — requires an architecture call or human judgment (design review, external access)
|
|
32
|
+
|
|
33
|
+
Prefer AFK over HITL wherever possible.
|
|
34
|
+
|
|
35
|
+
**File disjunction check — serialize entangled slices.** After assigning routing classes, inspect every pair of parallel slices (those with no `req:N` edge between them) for file-set overlap. Two slices are *entangled* when they both write to the same file(s). Entangled concurrent slices produce a merge conflict that no runtime resolves — the conflict is inherent, not recoverable. Serialize them: add a `req:N` dependency edge from the later slice to the earlier one so only one is `ready-for-agent` at a time. Parallel slots belong exclusively to *file-disjoint* slices that touch non-overlapping file sets. This is the canonical serialization mechanism; the AFK fleet width is calibrated from the resulting disjunction structure (see `/afk fleet` docs).
|
|
36
|
+
|
|
37
|
+
### Step 4 — Quiz the user (mandatory — do not skip)
|
|
38
|
+
|
|
39
|
+
Present the proposed breakdown as a numbered list. For each slice show:
|
|
40
|
+
|
|
41
|
+
- **Title** — short descriptive name
|
|
42
|
+
- **Type** — HITL / AFK
|
|
43
|
+
- **Blocked by** — which other slices (if any) must complete first
|
|
44
|
+
- **User stories covered** — which user stories this addresses (if the source has them)
|
|
45
|
+
|
|
46
|
+
Then ask, **explicitly**:
|
|
47
|
+
|
|
48
|
+
- Does the granularity feel right? (too coarse / too fine)
|
|
49
|
+
- Are the dependency relationships correct?
|
|
50
|
+
- Should any slices be merged or split further?
|
|
51
|
+
- Are the correct slices marked HITL vs AFK?
|
|
52
|
+
|
|
53
|
+
**Iterate until the user approves the breakdown.** Do not advance to Step 5 on silence or implicit approval.
|
|
54
|
+
|
|
55
|
+
### Step 5 — Cascade gate
|
|
56
|
+
|
|
57
|
+
**Verify referenced `.red/` docs are landed on `origin/{base}` before publishing** — AFK workers branch from `origin/{base}` and cannot see the primary checkout's working-tree edits, so never publish while docs are unlanded.
|
|
58
|
+
|
|
59
|
+
1. `git fetch origin`, then compare the `.red/` docs (`.red/CONTEXT.md`, `.red/CONTEXT-MAP.md`, `.red/contexts/**`, `.red/adr/**`) between the primary working tree and `origin/{base}` (base resolved lock > pin > main). "Landed" means reachable from `origin/{base}`, not merely present on disk — origin-first comparison, mirroring the `/review-adrs` convention.
|
|
60
|
+
2. **On mismatch:** run the doc-landing procedure from the `/start` end-of-session finalizer (canonized by ADR 0092) first, then continue to Step 6.
|
|
61
|
+
3. **If landing is impossible** (no network, no push access): abort — never publish while docs are unlanded. State clearly which files must be landed and stop.
|
|
62
|
+
|
|
63
|
+
### Step 6 — Publish in dependency order
|
|
64
|
+
|
|
65
|
+
For each approved slice, publish a new issue. Use the issue template in `<supporting-info>`.
|
|
66
|
+
|
|
67
|
+
- Publish in **dependency order** (blockers first) so you can reference real issue identifiers in each "Blocked by" field
|
|
68
|
+
- Tag only currently-unblocked AFK slices with the canonical `ready-for-agent` triage label (mapped string from `/red-setup`).
|
|
69
|
+
- If an AFK slice has open blockers, publish it as `blocked:dependency` + one `req:N` label **per blocker** (NOT `ready-for-human` — a dependency-blocked slice is healthy and must never page a human). Also create the tracker-native blocked-by relationship for each blocker using `/red-setup` issue-tracker-github *Dependency & hierarchy operations*. The native blocked-by relationship is the human surface; req:N labels remain the machine truth for `/afk` close cascade, boot sweep, and gate census. Keep the strict `## Blocked by` task list in the body as the ADR 0094 AFK boot-sweep fallback and human-facing mirror. `req:N` labels are created on demand (`gh label create req:<n>` if missing). `/afk` auto-promotes the issue to `ready-for-agent` the moment its last dependency closes (event-driven close cascade, with the boot sweep as a safety net). See `/red-setup` triage-labels *Dependency Edges* and ADR 0094.
|
|
70
|
+
- **`req:N` targets must be executable slices, never a Spec** (authoritative statement + rationale in Hard rules below). Before creating each `req:N` label, check the target with `gh issue view N --json labels`: if #N carries `type:spec`, **refuse the edge** and re-point it at the Spec's concrete executable slice(s) instead (the child issues carrying `spec:N`); when the Spec has no slices yet, first create the concrete slice the dependent actually waits on, then point `req:N` at that slice.
|
|
71
|
+
- If a slice is HITL, publish it as `ready-for-human`. Do **not** include a literal `## Blocked by` section unless it should be auto-promoted to AFK after blockers close; use `## Current blocker` / `## Human decision needed` for gates, measurements, and decisions where closing a referenced issue is not enough to make the slice delegable.
|
|
72
|
+
- If the parent is a Spec (carries `type:spec` + `needs-slicing`), tag every child with `spec:{N}` referencing the parent and create the tracker-native sub-issue relationship from the parent Spec to the child Ticket using `/red-setup` issue-tracker-github *Dependency & hierarchy operations*. The native sub-issue relationship is the human surface; `spec:{N}` remains the label contract. After all slices are published, remove `needs-slicing` from the parent Spec. Never remove `type:spec` — it is a permanent type marker. Never apply `ready-for-agent` to the parent Spec itself.
|
|
73
|
+
|
|
74
|
+
### Hard rules — do not break these
|
|
75
|
+
|
|
76
|
+
- ❌ Do **not** mark two slices as parallelizable when they write to the same file(s) — that is a file-entanglement merge conflict waiting to happen at landing. Add `req:N` edges to serialize them instead; file-disjoint slices run in parallel, entangled ones run serial.
|
|
77
|
+
- ❌ Do **not** publish until the user explicitly approved the breakdown in Step 4
|
|
78
|
+
- ❌ Do **not** modify or close any parent issue
|
|
79
|
+
- ❌ Do **not** create horizontal-slice issues ("the schema layer", "the API layer", "the UI layer")
|
|
80
|
+
- ❌ Do **not** invent label strings — use the mapping from `/red-setup`
|
|
81
|
+
- ❌ Do **not** create a `req:N` edge whose target #N is a `type:spec` — dependency edges must point at executable slices. A Spec closes only after a manual bookkeeping step long after its substance ships (#907/#928: 46/46 children closed, Specs still open), so a `req:<Spec>` edge strands the dependent in `blocked:dependency` forever. Re-point at the Spec's `spec:N` children, or, when the Spec has no slices yet, first create the concrete slice the dependent waits on and point `req:N` at that.
|
|
82
|
+
- ❌ Do **not** "clean up" controlled redundancy between native tracker edges and labels/body text. Do not clean up either side: native sub-issue relationship and native blocked-by relationship edges are for humans; req:N labels remain the machine truth; the `## Blocked by` body fallback stays for compatibility.
|
|
83
|
+
- ❌ Do **not** inline file paths or code snippets in issue bodies — they go stale. The one exception is in `<supporting-info>` (decision-rich prototype output)
|
|
84
|
+
- ✅ **Do** publish in dependency order so "Blocked by" fields point at real issue IDs
|
|
85
|
+
- ✅ **Do** prefer many thin slices over few thick ones
|
|
86
|
+
|
|
87
|
+
</what-to-do>
|
|
88
|
+
|
|
89
|
+
<supporting-info>
|
|
90
|
+
|
|
91
|
+
## Vertical-slice rules
|
|
92
|
+
|
|
93
|
+
<vertical-slice-rules>
|
|
94
|
+
- Each slice delivers a narrow but COMPLETE path through every layer (schema, API, UI, tests)
|
|
95
|
+
- A completed slice is demoable or verifiable on its own
|
|
96
|
+
- Prefer many thin slices over few thick ones
|
|
97
|
+
- Parallel slices must be file-disjoint; file-overlapping slices get `req:N` serialization edges so they run serial, not concurrent
|
|
98
|
+
</vertical-slice-rules>
|
|
99
|
+
|
|
100
|
+
A horizontal slice ("build the schema for all tables") cannot be demoed and cannot be merged independently — it produces issues that block each other unnecessarily and tests imagined behaviour.
|
|
101
|
+
|
|
102
|
+
## Issue body template
|
|
103
|
+
|
|
104
|
+
<issue-template>
|
|
105
|
+
## Parent
|
|
106
|
+
|
|
107
|
+
A reference to the parent Spec on the issue tracker, written as a literal `Spec #N` line (if the source was an existing Spec, otherwise omit this section). The `/afk` pin-reader parses this exact `Spec #N` form to inherit the parent's `branch:` pin — keep the word `Spec` and the `#N` reference literal.
|
|
108
|
+
|
|
109
|
+
## What to build
|
|
110
|
+
|
|
111
|
+
A concise description of this vertical slice. Describe the end-to-end behaviour, not layer-by-layer implementation.
|
|
112
|
+
|
|
113
|
+
Avoid specific file paths or code snippets — they go stale fast. **Exception**: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it here and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
|
114
|
+
|
|
115
|
+
## Acceptance criteria
|
|
116
|
+
|
|
117
|
+
- [ ] Criterion 1
|
|
118
|
+
- [ ] Criterion 2
|
|
119
|
+
- [ ] Criterion 3
|
|
120
|
+
|
|
121
|
+
## Blocked by
|
|
122
|
+
|
|
123
|
+
- [ ] #123
|
|
124
|
+
- [ ] #456
|
|
125
|
+
|
|
126
|
+
Format: one GitHub task-list tracking entry per blocker, `- [ ] #N`. This is not GitHub's issue-dependencies widget. Its job is the ADR 0094 body fallback: the `/afk` boot sweep parses this exact section to auto-promote issues whose blockers have all closed, and humans still get a readable mirror in the issue body. Keep the heading literal (`## Blocked by`, capitalised, no extra punctuation) and the format strict.
|
|
127
|
+
|
|
128
|
+
This section is the body fallback required by ADR 0094. When the tracker supports native edges, publish the native blocked-by relationship too; keep this section anyway so older paths and audits retain the same source shape.
|
|
129
|
+
|
|
130
|
+
Omit the section entirely (do not write "None") if the slice has no blockers.
|
|
131
|
+
|
|
132
|
+
For a human gate, use this shape instead:
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
## Current blocker
|
|
136
|
+
|
|
137
|
+
<!-- red:blocker-state v1 -->
|
|
138
|
+
status: blocked
|
|
139
|
+
kind: decision
|
|
140
|
+
ref: #123
|
|
141
|
+
summary: The dependency closed, but the measurement did not prove the required win.
|
|
142
|
+
next: Human must decide whether to stop, redesign, or continue anyway.
|
|
143
|
+
<!-- /red:blocker-state -->
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
</issue-template>
|
|
147
|
+
|
|
148
|
+
## Wide refactors — expand → migrate → contract
|
|
149
|
+
|
|
150
|
+
**When a change is blast-radius-wide** (a rename, signature change, or API move touching dozens of call sites), a single vertical slice cannot both stay small and stay mergeable. Slice it as an **expand–contract** chain instead — three (or more) Tickets that each merge green on their own:
|
|
151
|
+
|
|
152
|
+
1. **Expand.** Introduce the new form *alongside* the old one — new function/flag/label/type added, old one untouched and still authoritative. Nothing calls the new form yet. This Ticket merges without changing any behaviour.
|
|
153
|
+
2. **Migrate.** Move call sites onto the new form, in one Ticket per independently-verifiable batch (by package, by layer, or by directory). Each batch merges green because the old form still exists as a fallback. Many thin migration Tickets beat one giant one — a failed batch never blocks the others.
|
|
154
|
+
3. **Contract.** Once every call site is migrated, remove the old form in a final Ticket. This is the only Ticket that deletes the old vocabulary, and it is `blocked:dependency` on all the migration Tickets (one `req:N` per batch).
|
|
155
|
+
|
|
156
|
+
**Big-bang exception.** Some flips must NOT expand–contract — when the two forms cannot coexist (a single atomic label/flag rename with no transition window, e.g. ADR 0093's vocabulary flip), slice it as one Ticket that changes definition + call sites + tests together, and say so explicitly in the Ticket body so a reviewer does not expect a fallback. Prefer expand–contract; reach for big-bang only when coexistence is genuinely impossible or the maintainer decided against a transition window.
|
|
157
|
+
|
|
158
|
+
</supporting-info>
|