5.9 KiB
Action Point Template
Each action point lives in its own file at blueprint/action-points/AP-NN-<slug>.md. The numbering is global and stable - once an AP gets a number, that number doesn't change even if the AP is later edited or reordered (use the dependency graph to express order, not the filename).
The default target is ~38 APs for a substantial system. Fewer is fine for smaller systems; more is fine for large ones. The right granularity is: one AP is a single coherent unit of implementation work that an engineer (or Claude Code) can execute in one focused session without needing to re-plan. If an AP would take a week or touches 20 files, it's too big - split it. If an AP is "rename this variable," it's too small - fold it into a neighbor.
Template
# AP-NN: <Title>
**Summary:** <one sentence - what this AP accomplishes>
**Status:** draft | reviewed | approved
**Complexity:** S | M | L | XL
**Depends on:** AP-XX, AP-YY (or "none")
**Blocks:** AP-ZZ (optional, useful for the dependency graph)
## Goal
<2–4 sentences. What does the system look like after this AP is done that it didn't before? Why does this AP exist?>
## Files involved
| Path | Action | Notes |
|---|---|---|
| `src/auth/token.ts` | create | New module for token issuance |
| `src/auth/index.ts` | modify | Export the new token API |
| `src/db/schema.sql` | modify | Add `tokens` table |
| `tests/auth/token.test.ts` | create | Unit tests for token issuance |
Every path here must be a real path that fits the architecture defined in `plan/01-architecture.md` and the data model in `plan/04-data-model.md`. If you find yourself inventing a path, stop and check those files first.
## Code flow
<Prose, function by function. Not pseudocode - describe what each function does, what it calls, what it returns, and what state it touches. The reader should be able to picture the implementation without seeing it.>
Example:
> `issueToken(userId, scope)` generates a random 256-bit token, stores a hashed copy in the `tokens` table with `userId`, `scope`, `createdAt`, and `expiresAt` (24h default), and returns the raw token to the caller. It calls `hashToken()` from `crypto-utils.ts` and `db.tokens.insert()` from the data layer. On a DB error it throws `TokenIssuanceError`, which the caller (the auth controller) maps to a 500.
## Interfaces touched
<Function signatures, API contracts, schema changes. Be exact.>
```ts
// New
export function issueToken(userId: string, scope: TokenScope): Promise<string>;
export function validateToken(raw: string): Promise<TokenClaims | null>;
// Modified
// (none)
Schema delta:
CREATE TABLE tokens (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id),
hash TEXT NOT NULL UNIQUE,
scope TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX tokens_user_id_idx ON tokens(user_id);
How it interacts with other components
<Explicit named references to components defined in plan/03-components/. No vague "the auth layer" - say which file.>
- Consumed by:
plan/03-components/auth-controller.mdcallsissueTokenon successful login. - Calls into:
plan/03-components/db-layer.mdfor inserts;plan/03-components/crypto-utils.mdfor hashing. - Observed by:
plan/03-components/audit-logger.mdsubscribes to thetoken.issuedevent (added in AP-NN-1).
Verification
<How does the user / Claude Code know this AP is done correctly? Concrete, testable.>
tests/auth/token.test.tscovers: happy path, expired token, unknown token, malformed token, scope mismatch.- Manual check:
curl -X POST /loginreturns a token;curl -H "Authorization: Bearer <token>" /mereturns the user. - DB check:
SELECT * FROM tokens WHERE user_id = '<id>'shows the new row with a hashed value (never the raw). - Lint and typecheck pass.
Edge cases considered
- Concurrent login from the same user → multiple tokens allowed; both valid until expiry.
- Token issued just before clock skew → use server clock only; document this.
- DB unreachable during issuance → fail closed, return 500, do not return a token the DB doesn't know about.
Open questions / risks
- Should tokens be revocable individually, or only by user? (Punted to AP-NN+5.)
- Rate-limit on issuance? (See
plan/06-non-functional.md- TBD.)
Research notes
<Any web research that informed this AP, with citations. Link to the file in research/ where the full notes live.>
- See
research/auth-libraries-2026.mdfor why we're not usingjsonwebtokendirectly.
## Rules for writing APs
1. **No invented names.** Every file path, function name, type, and table name must be consistent with the architecture and component files. If something isn't defined yet, define it in the right `plan/` file *first*, then reference it from the AP.
2. **Verification is not optional.** An AP without a verification section is a wish, not a plan.
3. **Edge cases are not optional.** Force yourself to write at least three. If you can't think of three, you haven't thought hard enough - try scale=0, scale=max, and concurrent access as starting prompts.
4. **Dependencies are explicit.** "Depends on" should list AP IDs, not vague phrases like "after the auth work."
5. **Complexity is honest.** S = under an hour. M = a half day. L = a full day or two. XL = split it.
6. **Cite research.** If the AP makes a choice informed by research, link the research file.
## The dependency graph
After all APs are drafted, write `blueprint/action-points/README.md` containing:
- A one-paragraph overview of the AP set.
- A table of all APs with ID, title, complexity, and dependencies.
- A mermaid graph showing the dependency DAG. Group nodes that can run in parallel.
- A recommended execution order - a topological sort of the DAG, with parallel groups marked.
The execution order is what the user will hand to Claude Code (or any other implementer) when the blueprint is approved.