beadboard/docs/plans/2026-02-12-project-context-model-design.md
2026-03-03 16:43:42 -08:00

52 lines
2.5 KiB
Markdown

# Project Context Model Design
## Summary
Aggregate views need stable project identity attached to each issue so cross-project Kanban, timeline, and session views can filter and display project metadata consistently. This design adds a normalized `ProjectContext` payload to every issue returned from read flows while keeping the raw JSONL format untouched.
## Requirements
- Use existing Windows-safe path normalization for stable identity.
- Attach project identity on every issue returned from read services.
- Include fields: `key`, `root`, `displayPath`, `name`, `source`, `addedAt`.
- Default single-project reads to `source="local"` and `addedAt=null`.
## Data Model
```ts
export type ProjectSource = 'local' | 'registry' | 'scanner';
export interface ProjectContext {
key: string; // windowsPathKey(root)
root: string; // canonicalizeWindowsPath(root)
displayPath: string; // toDisplayPath(root)
name: string; // path.basename(root)
source: ProjectSource;
addedAt: string | null;
}
export type BeadIssueWithProject = BeadIssue & { project: ProjectContext };
```
## Construction
- Add a helper (e.g., `buildProjectContext`) that accepts `projectRoot`, `source`, and `addedAt`.
- Normalize the root with `canonicalizeWindowsPath`, compute the key with `windowsPathKey`, display path with `toDisplayPath`, and name from `path.basename`.
- No filesystem checks are required; this is identity metadata only.
## Read Flow Integration
- Update `readIssuesFromDisk` to attach `project` to each parsed issue.
- Default `projectRoot` to `process.cwd()` when not provided.
- Keep the existing `BeadIssue` shape for raw parsing; project context is added in read services only.
## Error Handling
- If `projectRoot` is empty, throw a clear error early in `buildProjectContext`.
- Otherwise, treat normalization as pure string ops and do not swallow exceptions.
## Tests
- Add unit tests for `buildProjectContext` field derivation (key/root/displayPath/name/source/addedAt).
- Update `readIssuesFromDisk` tests to assert `project` is attached with expected fields.
## Alternatives Considered
1. **Wrapper object `{ project, issue }`**
Pro: explicit separation; Con: more refactors across UI filtering and query shapes.
2. **`metadata.project` on issues**
Pro: zero type changes; Con: weak typing and harder discoverability.
3. **Chosen: `issue.project` field**
Clear typing, predictable access, and minimal friction for UI + aggregate queries.