Decompose architecture into 28 atomic implementation tasks
Break the @alkdev/taskgraph architecture specs into dependency-ordered implementation tasks across 8 component directories: setup, schema, error, graph, analysis, cost-benefit, frontmatter, api, and review. Each task has clear acceptance criteria referencing specific architecture docs. Three review tasks serve as quality gates at critical junction points (schemas-and-errors, graph-complete, complete-library). The dependency graph is validated acyclic with 9 topological levels enabling significant parallelism across independent work streams.
This commit is contained in:
38
tasks/implementation/analysis/bottlenecks.md
Normal file
38
tasks/implementation/analysis/bottlenecks.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: analysis/bottlenecks
|
||||
name: Implement bottlenecks analysis function
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/construction
|
||||
- graph/queries
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement `bottlenecks(graph: TaskGraph): Array<{ taskId: string; score: number }>` using `graphology-metrics` betweenness centrality. Bottleneck tasks are those on the most shortest paths between other nodes.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `bottlenecks` returns array of `{ taskId, score }` objects sorted by score descending
|
||||
- [ ] Uses `graphology-metrics` betweenness centrality computation
|
||||
- [ ] Normalized scores (0.0–1.0 range)
|
||||
- [ ] Tasks with score 0 are still included (they're not bottlenecks)
|
||||
- [ ] Works on disconnected graphs (betweenness is 0 for disconnected components)
|
||||
- [ ] Unit tests: linear chain (middle node has highest betweenness), star graph (center has highest), independent nodes (all zero)
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — bottlenecks signature
|
||||
- docs/architecture/build-distribution.md — graphology-metrics dependency
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
40
tasks/implementation/analysis/critical-path.md
Normal file
40
tasks/implementation/analysis/critical-path.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
id: analysis/critical-path
|
||||
name: Implement criticalPath and weightedCriticalPath functions
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/construction
|
||||
- graph/queries
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement `criticalPath` and `weightedCriticalPath` as standalone functions. `criticalPath` finds the longest path from sources to sinks using default edge weighting. `weightedCriticalPath` accepts a custom weight function for per-node weighting.
|
||||
|
||||
`criticalPath` can be implemented via topological order + dynamic programming (longest path in DAG). For unweighted, each edge has weight 1; for weighted, each node contributes a weight.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `criticalPath(graph: TaskGraph): string[]` — returns the longest path as an ordered array of task IDs
|
||||
- [ ] `weightedCriticalPath(graph: TaskGraph, weightFn: (taskId: string, attrs: TaskGraphNodeAttributes) => number): string[]` — returns the path with the highest cumulative weight
|
||||
- [ ] Both functions throw `CircularDependencyError` if graph is cyclic
|
||||
- [ ] When multiple paths tie, returns any one of them (deterministic order preferred)
|
||||
- [ ] Empty graph returns `[]`; single-node graph returns `[nodeId]`
|
||||
- [ ] Unit tests: linear chain (the chain itself is critical path), diamond graph (tests path selection), weighted variant with diverse scope values
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — criticalPath, weightedCriticalPath signatures
|
||||
- docs/architecture/graph-model.md — edge direction (prerequisite→dependent determines source→sink)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
38
tasks/implementation/analysis/parallel-groups.md
Normal file
38
tasks/implementation/analysis/parallel-groups.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: analysis/parallel-groups
|
||||
name: Implement parallelGroups analysis function
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/construction
|
||||
- graph/queries
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement `parallelGroups(graph: TaskGraph): string[][]` in `src/analysis/index.ts` or a dedicated module. This returns groups of tasks that can be executed concurrently — tasks at the same topological depth. Uses `graphology-dag.topologicalGenerations`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `parallelGroups` returns `string[][]` where each inner array is a generation of tasks at the same depth from sources
|
||||
- [ ] Uses `graphology-dag.topologicalGenerations()` for the generation computation
|
||||
- [ ] Tasks with zero prerequisites are in the first group
|
||||
- [ ] Throws `CircularDependencyError` if the graph is cyclic (delegated to `topologicalGenerations` behavior)
|
||||
- [ ] Works on disconnected graphs (each connected component sorted independently, then merged by depth)
|
||||
- [ ] Unit tests: linear chain (each group size 1), diamond graph, disconnected components
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — parallelGroups signature
|
||||
- docs/architecture/graph-model.md — parallel groups definition
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
57
tasks/implementation/api/public-exports.md
Normal file
57
tasks/implementation/api/public-exports.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: api/public-exports
|
||||
name: Wire up public API surface in src/index.ts
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/taskgraph-class
|
||||
- graph/construction
|
||||
- graph/mutation
|
||||
- graph/queries
|
||||
- graph/subgraph-and-validation
|
||||
- graph/export
|
||||
- analysis/parallel-groups
|
||||
- analysis/critical-path
|
||||
- analysis/bottlenecks
|
||||
- cost-benefit/ev-calculation
|
||||
- cost-benefit/dag-propagation
|
||||
- cost-benefit/workflow-cost
|
||||
- cost-benefit/risk-analysis
|
||||
- frontmatter/parsing
|
||||
- frontmatter/file-io-and-serialize
|
||||
- schema/numeric-methods-and-defaults
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: project
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Wire up `src/index.ts` to re-export the full public API surface. This is the main entry point for consumers: everything they need should be importable from `@alkdev/taskgraph`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/index.ts` re-exports all public API items:
|
||||
- `TaskGraph` class (from `src/graph/index.ts`)
|
||||
- All analysis functions: `parallelGroups`, `criticalPath`, `weightedCriticalPath`, `bottlenecks`, `riskPath`, `riskDistribution`, `shouldDecomposeTask`, `workflowCost`, `calculateTaskEv`
|
||||
- All categorical numeric functions: `scopeCostEstimate`, `scopeTokenEstimate`, `riskSuccessProbability`, `riskWeight`, `impactWeight`, `resolveDefaults`
|
||||
- All frontmatter functions: `parseFrontmatter`, `parseTaskFile`, `parseTaskDirectory`, `serializeFrontmatter`
|
||||
- All schemas and types: all enum schemas, `TaskInput`, `DependencyEdge`, `TaskGraphNodeAttributes`, `TaskGraphEdgeAttributes`, `TaskGraphSerialized`, all result types
|
||||
- All error classes: `TaskgraphError`, `TaskNotFoundError`, `CircularDependencyError`, `InvalidInputError`, `DuplicateNodeError`, `DuplicateEdgeError`
|
||||
- [ ] No internal implementation details leak through the public API
|
||||
- [ ] `package.json` `"exports"` field configured for ESM primary + CJS compat
|
||||
- [ ] TypeScript declarations (`tsc --emitDeclarationOnly`) verify the public surface compiles correctly
|
||||
- [ ] Consumer import `import { TaskGraph, workflowCost } from "@alkdev/taskgraph"` works
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — full public API
|
||||
- docs/architecture/build-distribution.md — package name, ESM primary
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
51
tasks/implementation/cost-benefit/dag-propagation.md
Normal file
51
tasks/implementation/cost-benefit/dag-propagation.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: cost-benefit/dag-propagation
|
||||
name: Implement DAG-propagation effective probability computation
|
||||
status: pending
|
||||
depends_on:
|
||||
- cost-benefit/ev-calculation
|
||||
- graph/queries
|
||||
- schema/numeric-methods-and-defaults
|
||||
scope: moderate
|
||||
risk: high
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the DAG-propagation cost model in `src/analysis/cost-benefit.ts`. This is the core algorithmic contribution beyond the Rust CLI — it captures the structural reality that upstream failures multiply downstream damage.
|
||||
|
||||
Per [cost-benefit.md](../../../docs/architecture/cost-benefit.md), the algorithm:
|
||||
1. Processes tasks in topological order
|
||||
2. For each task with prerequisites, computes `pEffective` from intrinsic probability + upstream propagation
|
||||
3. Upstream propagation: `parentP + (1 - parentP) × qualityRetention` for each parent
|
||||
4. `pEffective` = intrinsic × product of all inherited quality factors
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `computeEffectiveP(taskId, graph, upstreamSuccessProbs, defaultQualityRetention, propagationMode)` — internal helper
|
||||
- [ ] In `dag-propagate` mode: for each task in topological order:
|
||||
- Get intrinsic probability from `resolveDefaults(risk).successProbability`
|
||||
- For each prerequisite, compute inherited quality: `parentP + (1 - parentP) × qualityRetention`
|
||||
- `pEffective` = intrinsic × product of all inherited quality factors
|
||||
- Store task's **actual** success probability for downstream propagation (use `pEffective` if this is the task's real probability)
|
||||
- [ ] In `independent` mode: `pEffective = pIntrinsic` (no propagation)
|
||||
- [ ] Completed tasks (`status: "completed"`): propagate with `p = 1.0` when `includeCompleted: false`
|
||||
- [ ] `qualityRetention` per edge defaults to 0.9, can be overridden per-edge via `defaultQualityRetention` option or edge attributes
|
||||
- [ ] Throws `CircularDependencyError` if graph is cyclic (needs topo sort)
|
||||
- [ ] Unit tests: simple chain (verify compounding effect), diamond graph, independent vs dag-propagate comparison matches Python research model results, completed task exclusion/propagation semantics
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/cost-benefit.md — DAG-propagation algorithm, qualityRetention semantics
|
||||
- docs/architecture/decisions/004-workflow-cost-dag-propagation.md — ADR-004
|
||||
- docs/architecture/decisions/005-no-depth-escalation-v1.md — no depth escalation in v1
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
48
tasks/implementation/cost-benefit/ev-calculation.md
Normal file
48
tasks/implementation/cost-benefit/ev-calculation.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: cost-benefit/ev-calculation
|
||||
name: Implement calculateTaskEv pure function
|
||||
status: pending
|
||||
depends_on:
|
||||
- schema/numeric-methods-and-defaults
|
||||
- schema/result-types
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement `calculateTaskEv(p, scopeCost, impactWeight, config?): EvResult` in `src/analysis/cost-benefit.ts`. This is the pure math function — takes numeric inputs, returns EV result. No graph dependency.
|
||||
|
||||
Per [cost-benefit.md](../../../docs/architecture/cost-benefit.md):
|
||||
```
|
||||
EV = P_success × C_success + (1 - P_success) × C_fail
|
||||
```
|
||||
Where `C_fail = scopeCost + fallbackCost + timeLost × expectedRetries`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `calculateTaskEv(p: number, scopeCost: number, impactWeight: number, config?: EvConfig): EvResult`
|
||||
- [ ] Returns `EvResult`: `{ ev, pSuccess, expectedRetries }`
|
||||
- [ ] `expectedRetries` = `(1 - p) / p` when `p > 0`, else 0 (geometric series)
|
||||
- [ ] `C_fail = scopeCost * impactWeight + fallbackCost + timeLost * expectedRetries` (impactWeight scales the cost)
|
||||
- [ ] `EV = p * scopeCost * impactWeight + (1-p) * C_fail`
|
||||
- [ ] When `config.retries` is provided and > 0, caps `expectedRetries` at `retries`
|
||||
- [ ] When `config.valueRate` is non-zero, multiplies the final EV
|
||||
- [ ] Edge cases: `p = 0` (guaranteed failure), `p = 1` (guaranteed success), default config (all zeros)
|
||||
- [ ] Unit tests: known calculations from the Python research model, boundary values, config variations
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/cost-benefit.md — EV formula, EvConfig parameters, EvResult
|
||||
- docs/architecture/api-surface.md — calculateTaskEv signature
|
||||
- docs/architecture/schemas.md — EvConfig, EvResult schemas
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
50
tasks/implementation/cost-benefit/risk-analysis.md
Normal file
50
tasks/implementation/cost-benefit/risk-analysis.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
id: cost-benefit/risk-analysis
|
||||
name: Implement riskPath, riskDistribution, and shouldDecomposeTask functions
|
||||
status: pending
|
||||
depends_on:
|
||||
- cost-benefit/ev-calculation
|
||||
- analysis/critical-path
|
||||
- schema/numeric-methods-and-defaults
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the three risk analysis functions: `riskPath`, `riskDistribution`, and `shouldDecomposeTask`. These are standalone composable functions that serve different analysis use cases.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `riskPath(graph: TaskGraph): RiskPathResult`:
|
||||
- Calls `weightedCriticalPath` with weight function `riskWeight * impactWeight`
|
||||
- Returns `{ path: string[], totalRisk: number }`
|
||||
- `totalRisk` is the sum of weight values along the path
|
||||
- [ ] `riskDistribution(graph: TaskGraph): RiskDistributionResult`:
|
||||
- Groups all tasks by their `risk` attribute
|
||||
- Returns `{ trivial: string[], low: string[], medium: string[], high: string[], critical: string[], unspecified: string[] }`
|
||||
- Tasks with `risk: undefined` (not assessed) go in `unspecified`
|
||||
- No duplicate task IDs in any bucket
|
||||
- [ ] `shouldDecomposeTask(attrs: TaskGraphNodeAttributes): DecomposeResult`:
|
||||
- Pure function — takes node attributes (not a graph)
|
||||
- Internally calls `resolveDefaults` for `risk` and `scope` (nullable fields)
|
||||
- Flags decomposition when: risk >= "high" OR scope >= "broad"
|
||||
- Returns `{ shouldDecompose: boolean, reasons: string[] }`
|
||||
- Unassessed tasks (null/undefined risk or scope) are never flagged — default values are below threshold
|
||||
- Provides specific reasons: e.g., `"risk: high — failure probability 0.35"`, `"scope: broad — cost estimate 4.0"`
|
||||
- [ ] Unit tests for all three functions with known inputs/outputs
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — risk analysis functions
|
||||
- docs/architecture/cost-benefit.md — riskPath, riskDistribution, shouldDecomposeTask, decomposition threshold
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
45
tasks/implementation/cost-benefit/workflow-cost.md
Normal file
45
tasks/implementation/cost-benefit/workflow-cost.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: cost-benefit/workflow-cost
|
||||
name: Implement workflowCost orchestration function
|
||||
status: pending
|
||||
depends_on:
|
||||
- cost-benefit/dag-propagation
|
||||
- graph/queries
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement `workflowCost(graph: TaskGraph, options?: WorkflowCostOptions): WorkflowCostResult` in `src/analysis/cost-benefit.ts`. This orchestrates the per-task EV calculations, handles DAG propagation, and enriches results with `taskId` and `name` from the graph.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `workflowCost` accepts `WorkflowCostOptions` with optional: `includeCompleted`, `limit`, `propagationMode`, `defaultQualityRetention`
|
||||
- [ ] Default propagation mode: `"dag-propagate"` per ADR-004
|
||||
- [ ] Default `defaultQualityRetention`: 0.9
|
||||
- [ ] Each task in result includes: `taskId`, `name`, `ev`, `pIntrinsic`, `pEffective`, `probability` (= `pEffective`), `scopeCost`, `impactWeight`
|
||||
- [ ] `totalEv`: sum of all task EVs (excluding completed tasks from output when `includeCompleted: false`)
|
||||
- [ ] `averageEv`: `totalEv / tasks.length`
|
||||
- [ ] `propagationMode`: reflected in result
|
||||
- [ ] When `includeCompleted: false`: completed tasks excluded from `tasks` array but remain in propagation chain with p=1.0
|
||||
- [ ] When `includeCompleted: false`: only `"completed"` status triggers exclusion; `"failed"` and `"blocked"` are always included
|
||||
- [ ] When `limit` is set: returns at most `limit` tasks (sorted by EV descending? or topological order? spec says "limits the number of tasks in the result" — use topological order with limit)
|
||||
- [ ] Throws `CircularDependencyError` if graph is cyclic
|
||||
- [ ] Unit tests: full workflow cost calculation, independent vs dag-propagate comparison, excludeCompleted scenarios, limit behavior
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/cost-benefit.md — workflow cost, skip-completed semantics
|
||||
- docs/architecture/api-surface.md — workflowCost signature, WorkflowCostOptions
|
||||
- docs/architecture/decisions/004-workflow-cost-dag-propagation.md — ADR-004
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
43
tasks/implementation/error/error-hierarchy.md
Normal file
43
tasks/implementation/error/error-hierarchy.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: error/error-hierarchy
|
||||
name: Implement typed error class hierarchy
|
||||
status: pending
|
||||
depends_on:
|
||||
- setup/project-init
|
||||
scope: narrow
|
||||
risk: trivial
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the custom error classes in `src/error/index.ts` per [errors-validation.md](../../../docs/architecture/errors-validation.md). All errors extend `TaskgraphError` which extends `Error`. Each subclass adds typed fields for programmatic error recovery.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/error/index.ts` exports:
|
||||
- `TaskgraphError extends Error` — base class
|
||||
- `TaskNotFoundError extends TaskgraphError` with `taskId: string` field
|
||||
- `CircularDependencyError extends TaskgraphError` with `cycles: string[][]` field
|
||||
- `InvalidInputError extends TaskgraphError` with `field: string` and `message: string` fields
|
||||
- `DuplicateNodeError extends TaskgraphError` with `taskId: string` field
|
||||
- `DuplicateEdgeError extends TaskgraphError` with `prerequisite: string` and `dependent: string` fields
|
||||
- [ ] Each error class sets `this.name` to the class name
|
||||
- [ ] Each error class properly extends the prototype chain (`Object.setPrototypeOf(this, new.target.prototype)`)
|
||||
- [ ] `InvalidInputError` supports construction from TypeBox `Value.Errors()` output (receives structured field/path/value info)
|
||||
- [ ] `CircularDependencyError` receives `string[][]` where each inner array is an ordered cycle path
|
||||
- [ ] Unit tests verifying: correct `instanceof` chain, field access, `.name` property, error messages
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/errors-validation.md — error types, when each is thrown
|
||||
- docs/architecture/api-surface.md — error documentation on specific methods
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
54
tasks/implementation/frontmatter/file-io-and-serialize.md
Normal file
54
tasks/implementation/frontmatter/file-io-and-serialize.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: frontmatter/file-io-and-serialize
|
||||
name: Implement parseTaskFile, parseTaskDirectory, and serializeFrontmatter
|
||||
status: pending
|
||||
depends_on:
|
||||
- frontmatter/parsing
|
||||
- schema/input-schemas
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the file I/O frontmatter functions and the serializer. These are convenience wrappers for common use cases and the reverse operation (TaskInput → markdown).
|
||||
|
||||
Per [frontmatter.md](../../../docs/architecture/frontmatter.md):
|
||||
- `parseTaskFile`/`parseTaskDirectory` depend on `node:fs/promises` (Node.js only)
|
||||
- `parseFrontmatter` is runtime-agnostic
|
||||
- `serializeFrontmatter` uses `yaml.stringify()` for the data portion
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `parseTaskFile(filePath: string): Promise<TaskInput>`:
|
||||
- Reads file using `node:fs/promises.readFile`
|
||||
- Delegates to `parseFrontmatter` for parsing and validation
|
||||
- Throws underlying Node.js error for I/O failures (ENOENT, EACCES, etc.)
|
||||
- [ ] `parseTaskDirectory(dirPath: string): Promise<TaskInput[]>`:
|
||||
- Recursive directory scanning via `node:fs/promises.readdir` with `{ recursive: true }` or manual recursion
|
||||
- Filters for `.md` files only
|
||||
- Silently skips files without valid `---`-delimited frontmatter (no error thrown, just omitted from results)
|
||||
- Throws underlying Node.js error for I/O failures
|
||||
- Uses `parseTaskFile` per file
|
||||
- [ ] `serializeFrontmatter(task: TaskInput, body?: string): string`:
|
||||
- Constructs `---`-delimited markdown output
|
||||
- Uses `yaml.stringify()` for the `TaskInput` data (excludes `id` from frontmatter? No — per Rust CLI convention, `id` comes from the filename, but in the schema it's part of `TaskInput`. Follow schema: include all `TaskInput` fields in the YAML.)
|
||||
- Appends body content (default: empty string) after closing `---`
|
||||
- Handles nullable fields correctly: `risk: null` → `risk: null` in YAML (explicit null), absent fields → omitted from YAML
|
||||
- [ ] File I/O functions documented as Node.js-only in JSDoc comments
|
||||
- [ ] Unit tests: parseTaskFile with temp file, parseTaskDirectory with temp dir (including non-.md files, missing frontmatter files), serializeFrontmatter round-trip parseFrontmatter(serializeFrontmatter(task)) ≈ task
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/frontmatter.md — file I/O functions, splitter, serializer
|
||||
- docs/architecture/schemas.md — TaskInput definition for serialization
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
51
tasks/implementation/frontmatter/parsing.md
Normal file
51
tasks/implementation/frontmatter/parsing.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: frontmatter/parsing
|
||||
name: Implement parseFrontmatter with YAML parsing and TypeBox validation
|
||||
status: pending
|
||||
depends_on:
|
||||
- frontmatter/splitter
|
||||
- schema/input-schemas
|
||||
- error/error-hierarchy
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement `parseFrontmatter(markdown: string): TaskInput` in `src/frontmatter/parse.ts`. This combines the splitter with `yaml.parse()` and TypeBox validation. Invalid frontmatter throws `InvalidInputError` with field-level details.
|
||||
|
||||
Per [frontmatter.md](../../../docs/architecture/frontmatter.md), the function uses:
|
||||
- The custom splitter for `---` extraction
|
||||
- `yaml.parse()` (from `yaml` package, zero dependencies) for YAML↔JS conversion
|
||||
- TypeBox `Value.Check()` + `Value.Errors()` for structured field-level validation
|
||||
- `Value.Clean()` to strip unknown properties from untrusted input
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `parseFrontmatter(markdown: string): TaskInput`:
|
||||
- Calls splitter to extract YAML string
|
||||
- Throws `InvalidInputError` if no valid frontmatter found (not `null` return — the caller expects TaskInput)
|
||||
- Calls `yaml.parse(yamlString)` for YAML 1.2 parsing
|
||||
- Runs `Value.Clean(TaskInput, parsed)` to strip unknown properties
|
||||
- Runs `Value.Check(TaskInput, cleaned)` — if fails, runs `Value.Errors()` and throws `InvalidInputError` with structured field/path/message/value details
|
||||
- Returns validated `TaskInput`
|
||||
- [ ] `InvalidInputError` is populated with field-level details from `Value.Errors()` output
|
||||
- [ ] YAML 1.2 used exclusively (the `yaml` package default) — no YAML 1.1 type coercion
|
||||
- [ ] Handles YAML `null` values (e.g., `risk:` with no value) correctly — becomes `null` in the TaskInput (distinction from absent field)
|
||||
- [ ] Unit tests: valid frontmatter, missing required fields, invalid enum values, unknown fields stripped, null categorical values preserved
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/frontmatter.md — parseFrontmatter, yaml package, no gray-matter
|
||||
- docs/architecture/schemas.md — TaskInput schema, Nullable helper
|
||||
- docs/architecture/errors-validation.md — InvalidInputError
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
45
tasks/implementation/frontmatter/splitter.md
Normal file
45
tasks/implementation/frontmatter/splitter.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: frontmatter/splitter
|
||||
name: Implement frontmatter delimiter splitter (~40 lines)
|
||||
status: pending
|
||||
depends_on:
|
||||
- setup/project-init
|
||||
scope: single
|
||||
risk: trivial
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the self-contained `---` delimited frontmatter splitter in `src/frontmatter/parse.ts`. This is a ~40 line function that extracts the YAML data string and markdown content body from a markdown string. No gray-matter dependency.
|
||||
|
||||
Per [frontmatter.md](../../../docs/architecture/frontmatter.md), the splitter:
|
||||
1. Checks for opening `---` delimiter (not `----`)
|
||||
2. Finds closing `\n---` delimiter
|
||||
3. Extracts YAML data string and markdown content body
|
||||
4. Returns `{ data: string, content: string }` or `null` if no valid frontmatter
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `splitFrontmatter(markdown: string): { data: string; content: string } | null`
|
||||
- [ ] Opening `---` must be at the start of the file (or after optional BOM/whitespace on first line)
|
||||
- [ ] `----` (4+ dashes) is NOT a valid delimiter — only exact `---`
|
||||
- [ ] Closing delimiter requires `\n---` (newline before dashes)
|
||||
- [ ] Returns `null` if no valid frontmatter found
|
||||
- [ ] Returns `{ data: "", content: "" }` if frontmatter is present but empty (e.g., `---\n---`)
|
||||
- [ ] Content body starts after the closing `---` + newline
|
||||
- [ ] Handles edge cases: no closing delimiter (returns null), file with only `---\n---`, file with no `---` at all
|
||||
- [ ] Unit tests: standard frontmatter, no frontmatter, empty frontmatter, multi-line content, dashes in content body (shouldn't be treated as delimiters), 4+ dashes ignored
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/frontmatter.md — splitter design, supply chain decision
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
62
tasks/implementation/graph/construction.md
Normal file
62
tasks/implementation/graph/construction.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
id: graph/construction
|
||||
name: Implement TaskGraph construction methods (fromTasks, fromRecords, fromJSON, addTask, addDependency)
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/taskgraph-class
|
||||
scope: broad
|
||||
risk: high
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the four construction methods in `src/graph/construction.ts` and integrate them as static methods on `TaskGraph`. These are the primary ways to create a graph instance from structured data. Each method has distinct semantics for edge handling, error behavior, and validation.
|
||||
|
||||
Per [graph-model.md](../../../docs/architecture/graph-model.md), the preferred internal approach is to build a serialized graph JSON blob and call `graph.import()` for paths 1 and 2 (better performance than N individual addNode/addEdge calls).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `TaskGraph.fromTasks(tasks: TaskInput[]): TaskGraph`:
|
||||
- Transforms `TaskInput[]` into node data + edge data, builds serialized blob, calls `graph.import()`
|
||||
- Each `dependsOn` entry creates an edge with default `qualityRetention: 0.9`
|
||||
- `dependsOn` targets not matching any task ID become orphan nodes with default attributes
|
||||
- Duplicate task IDs throw `DuplicateNodeError`
|
||||
- Uses `mergeNode` for idempotent node merging (same ID gets merged attributes)
|
||||
- Duplicate `dependsOn` entries for the same pair create only one edge (idempotent via `addEdgeWithKey`)
|
||||
- [ ] `TaskGraph.fromRecords(tasks: TaskInput[], edges: DependencyEdge[]): TaskGraph`:
|
||||
- Edges must reference tasks that exist in the `tasks` array — throws `TaskNotFoundError` for dangling references
|
||||
- Per-edge `qualityRetention` from the `DependencyEdge` objects
|
||||
- Duplicate task IDs throw `DuplicateNodeError`
|
||||
- Duplicate edges (same prerequisite→dependent pair) throw `DuplicateEdgeError`
|
||||
- [ ] `TaskGraph.fromJSON(data: TaskGraphSerialized): TaskGraph`:
|
||||
- Validates input against `TaskGraphSerialized` schema (using TypeBox `Value.Check`)
|
||||
- Uses `graph.import()` on the validated data
|
||||
- Orphan nodes in JSON are preserved
|
||||
- [ ] `addTask(id: string, attributes: TaskGraphNodeAttributes): void`:
|
||||
- Throws `DuplicateNodeError` if ID already exists
|
||||
- Adds node to internal graphology instance
|
||||
- [ ] `addDependency(prerequisite: string, dependent: string, qualityRetention?: number): void`:
|
||||
- Throws `TaskNotFoundError` if either endpoint doesn't exist
|
||||
- Throws `DuplicateEdgeError` if edge already exists
|
||||
- Uses `addEdgeWithKey` with deterministic key `${prerequisite}->${dependent}`
|
||||
- Default `qualityRetention: 0.9` if not provided
|
||||
- [ ] `fromTasks`/`fromRecords` strip `null` → `undefined` for categorical fields during `TaskInput` → `TaskGraphNodeAttributes` transformation
|
||||
- [ ] `TaskInput` fields `tags`, `assignee`, `due`, `created`, `modified` are not stored on graph nodes (belong to caller)
|
||||
- [ ] Unit tests for each construction method: happy path, error cases, edge cases (empty arrays, cycles not rejected at construction time)
|
||||
- [ ] All construction methods use deterministic edge keys per ADR-006
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/graph-model.md — construction paths, TaskInput→attributes transformation, error handling table
|
||||
- docs/architecture/api-surface.md — TaskGraph class, fromTasks/fromRecords/fromJSON/addTask/addDependency
|
||||
- docs/architecture/decisions/006-deterministic-edge-keys.md — deterministic edge keys
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
36
tasks/implementation/graph/export.md
Normal file
36
tasks/implementation/graph/export.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: graph/export
|
||||
name: Implement TaskGraph export methods (export, toJSON)
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/taskgraph-class
|
||||
scope: single
|
||||
risk: trivial
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the `export()` and `toJSON()` methods on `TaskGraph`. These wrap graphology's `export()` to produce `TaskGraphSerialized` output.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `export(): TaskGraphSerialized` — wraps `graph.export()` and validates the output conforms to the `TaskGraphSerialized` schema
|
||||
- [ ] `toJSON(): TaskGraphSerialized` — alias for `export()` (enables `JSON.stringify(graph)` to work)
|
||||
- [ ] Exported data includes all node attributes and edge attributes (including `qualityRetention`)
|
||||
- [ ] Round-trip: `TaskGraph.fromJSON(graph.export())` produces an equivalent graph
|
||||
- [ ] Unit test: create graph, add tasks/edges, export, round-trip through fromJSON, verify equivalence
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — export/toJSON methods
|
||||
- docs/architecture/schemas.md — TaskGraphSerialized schema
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
38
tasks/implementation/graph/mutation.md
Normal file
38
tasks/implementation/graph/mutation.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: graph/mutation
|
||||
name: Implement TaskGraph mutation methods (remove, update, updateEdgeAttributes)
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/taskgraph-class
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement mutation methods in `src/graph/mutation.ts` and integrate on `TaskGraph`. These methods modify the graph in-place.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `removeTask(id: string): void` — No-op if node doesn't exist. Removes node and cascades edge removal (graphology handles this automatically).
|
||||
- [ ] `removeDependency(prerequisite: string, dependent: string): void` — No-op if edge doesn't exist. Uses deterministic edge key `${prerequisite}->${dependent}` to identify the edge.
|
||||
- [ ] `updateTask(id: string, attributes: Partial<TaskGraphNodeAttributes>): void` — Throws `TaskNotFoundError` if ID doesn't exist. Uses `mergeNodeAttributes` for shallow merge of provided attributes.
|
||||
- [ ] `updateEdgeAttributes(prerequisite: string, dependent: string, attrs: Partial<TaskGraphEdgeAttributes>): void` — Throws `TaskNotFoundError` (actually `TaskNotFoundError` for the edge itself, but per the spec, edge attributes need both endpoints to exist) if the edge doesn't exist. Uses `mergeEdgeAttributes` for shallow merge.
|
||||
- [ ] All mutations maintain the deterministic edge key format
|
||||
- [ ] Unit tests: remove nonexistent node/edge is no-op, update nonexistent throws, partial updates merge correctly
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — mutation methods
|
||||
- docs/architecture/errors-validation.md — mutation operation behavior table
|
||||
- docs/architecture/graph-model.md — edge attributes, mutation semantics
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
53
tasks/implementation/graph/queries.md
Normal file
53
tasks/implementation/graph/queries.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: graph/queries
|
||||
name: Implement TaskGraph query methods (hasCycles, findCycles, topologicalOrder, dependencies, dependents, taskCount, getTask)
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/taskgraph-class
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement query methods in `src/graph/queries.ts` and integrate on `TaskGraph`. The `findCycles` implementation requires a custom 3-color DFS since `graphology-components` only gives SCCs, not cycle paths.
|
||||
|
||||
Per [errors-validation.md](../../../docs/architecture/errors-validation.md):
|
||||
- `hasCycles()` returns boolean (uses `graphology-dag` or `graphology-components` for fast check)
|
||||
- `findCycles()` returns `string[][]` — each inner array is an ordered cycle path
|
||||
- `topologicalOrder()` throws `CircularDependencyError` with `cycles` populated when graph is cyclic (per ADR-003)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `hasCycles(): boolean` — uses `graphology-dag.hasCycle()` or `graphology-components` SCC check as fast pre-check
|
||||
- [ ] `findCycles(): string[][]`:
|
||||
- Uses `stronglyConnectedComponents()` as pre-check: if zero multi-node SCCs and no self-loops, skip DFS
|
||||
- Custom 3-color DFS (WHITE/GREY/BLACK) to extract cycle paths
|
||||
- Returns one representative cycle per back edge, not exhaustive enumeration
|
||||
- Each inner array is an ordered node sequence where last node has edge back to first
|
||||
- [ ] `topologicalOrder(): string[]`:
|
||||
- Uses `graphology-dag.topologicalSort()` for the actual sort
|
||||
- **Throws `CircularDependencyError`** (with `cycles` from `findCycles()`) when graph is cyclic
|
||||
- Returns `string[]` of task IDs in prerequisite→dependent order
|
||||
- [ ] `dependencies(taskId: string): string[]` — returns prerequisite task IDs (inNeighbors). Throws `TaskNotFoundError` if ID doesn't exist.
|
||||
- [ ] `dependents(taskId: string): string[]` — returns dependent task IDs (outNeighbors). Throws `TaskNotFoundError` if ID doesn't exist.
|
||||
- [ ] `taskCount(): number` — returns number of nodes
|
||||
- [ ] `getTask(taskId: string): TaskGraphNodeAttributes | undefined` — returns node attributes or undefined
|
||||
- [ ] Unit tests: cycle detection on known cyclic/acyclic graphs, topologicalOrder on DAG, topologicalOrder throws on cyclic graph, dependency/dependent queries
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — query methods
|
||||
- docs/architecture/errors-validation.md — cycle handling, CircularDependencyError
|
||||
- docs/architecture/cost-benefit.md — findCycles algorithm description
|
||||
- docs/architecture/decisions/003-topo-order-throws-on-cycle.md — ADR-003
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
54
tasks/implementation/graph/subgraph-and-validation.md
Normal file
54
tasks/implementation/graph/subgraph-and-validation.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: graph/subgraph-and-validation
|
||||
name: Implement TaskGraph subgraph and validation methods
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/taskgraph-class
|
||||
- graph/queries
|
||||
- schema/input-schemas
|
||||
- schema/graph-schemas
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the `subgraph()` method and the three validation methods (`validateSchema`, `validateGraph`, `validate`) on `TaskGraph`.
|
||||
|
||||
Per [ADR-007](../../../docs/architecture/decisions/007-subgraph-internal-only.md), `subgraph` returns only edges where both endpoints are in the filtered set.
|
||||
|
||||
Per [errors-validation.md](../../../docs/architecture/errors-validation.md), validation methods collect issues and return arrays — never throw.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `subgraph(filter: (taskId: string, attrs: TaskGraphNodeAttributes) => boolean): TaskGraph`:
|
||||
- Uses `graphology-operators.subgraph` to extract matching nodes
|
||||
- Returns only edges where both endpoints are in the filtered set (internal-only) per ADR-007
|
||||
- Returns a new `TaskGraph` instance (not mutating the original)
|
||||
- [ ] `validateSchema(): ValidationError[]`:
|
||||
- Uses TypeBox `Value.Check()` and `Value.Errors()` on each node's attributes
|
||||
- Returns structured `ValidationError[]` with `type: "schema"`, `taskId`, `field`, `message`, `value`
|
||||
- [ ] `validateGraph(): GraphValidationError[]`:
|
||||
- Runs `findCycles()` and checks for dangling dependency references
|
||||
- Returns structured `GraphValidationError[]` with `type: "graph"`, `category`, `message`, optional `details`
|
||||
- Cycle category: `"cycle"` with cycle paths in `details`
|
||||
- Dangling reference category: `"dangling-reference"` with the referencing task ID
|
||||
- [ ] `validate(): ValidationError[]` — runs both `validateSchema()` and `validateGraph()`, returns combined array
|
||||
- [ ] `ValidationError` and `GraphValidationError` interfaces defined (may be in error module or co-located)
|
||||
- [ ] Unit tests: subgraph filtering, subgraph excludes external edges, validateSchema catches invalid enums, validateGraph catches cycles and dangling refs
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — validation API, subgraph
|
||||
- docs/architecture/errors-validation.md — validation levels, return types
|
||||
- docs/architecture/decisions/007-subgraph-internal-only.md — subgraph semantics
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
45
tasks/implementation/graph/taskgraph-class.md
Normal file
45
tasks/implementation/graph/taskgraph-class.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: graph/taskgraph-class
|
||||
name: Implement TaskGraph class skeleton with graphology DirectedGraph
|
||||
status: pending
|
||||
depends_on:
|
||||
- schema/enums
|
||||
- schema/input-schemas
|
||||
- schema/graph-schemas
|
||||
- error/error-hierarchy
|
||||
- setup/project-init
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Create the `TaskGraph` class in `src/graph/index.ts` that wraps `graphology.DirectedGraph`. This is the data class that holds the graph instance and provides the foundation for construction, mutation, and query methods. At this stage, implement the constructor, `raw` getter, and the overall class structure. Actual construction and analysis methods come in dependent tasks.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/graph/index.ts` exports `TaskGraph` class
|
||||
- [ ] Constructor creates an internal `graphology.DirectedGraph` with options `{ type: 'directed', multi: false, allowSelfLoops: false }`
|
||||
- [ ] `get raw(): Graph` returns the underlying graphology instance
|
||||
- [ ] Constructor accepts optional `TaskGraphSerialized` for initializing from serialized data (delegates to `fromJSON` pattern)
|
||||
- [ ] Class stores edge key format: `${source}->${target}` (per ADR-006)
|
||||
- [ ] No parallel edges constraint enforced by `multi: false` graph option
|
||||
- [ ] No self-loops constraint enforced by `allowSelfLoops: false` graph option
|
||||
- [ ] Internal `_edgeKey(source, target): string` method producing deterministic keys
|
||||
- [ ] Re-exported from `src/index.ts`
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — TaskGraph class API
|
||||
- docs/architecture/graph-model.md — construction paths, edge direction, constraints
|
||||
- docs/architecture/decisions/006-deterministic-edge-keys.md — edge key format
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
48
tasks/implementation/review/complete-library.md
Normal file
48
tasks/implementation/review/complete-library.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: review/complete-library
|
||||
name: Final review — validate full library against architecture docs
|
||||
status: pending
|
||||
depends_on:
|
||||
- api/public-exports
|
||||
- review/graph-complete
|
||||
- frontmatter/file-io-and-serialize
|
||||
- cost-benefit/workflow-cost
|
||||
- cost-benefit/risk-analysis
|
||||
scope: broad
|
||||
risk: low
|
||||
impact: project
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Final review of the complete library. Verify the full API surface matches architecture docs, all analysis functions produce correct results, and the library achieves its stated purpose: pure TypeScript task graph library with graphology, replicating and extending the essential graph algorithms and cost-benefit math from the Rust CLI.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Public API matches [api-surface.md](../../../docs/architecture/api-surface.md) exactly — no missing exports, no extra exports
|
||||
- [ ] All construction paths work: fromTasks, fromRecords, fromJSON, incremental
|
||||
- [ ] DAG-propagation cost model produces results consistent with Python research model examples
|
||||
- [ ] Independent model available as degenerate case (set `propagationMode: 'independent'` or `defaultQualityRetention: 1.0`)
|
||||
- [ ] Frontmatter parsing round-trips correctly: `parseFrontmatter(serializeFrontmatter(task))` ≈ task
|
||||
- [ ] `Value.Clean()` and `Value.Errors()` used correctly throughout (no `Value.Assert()` where structured errors needed)
|
||||
- [ ] No gray-matter, no js-yaml, no Zod anywhere in the dependency tree
|
||||
- [ ] `npm pack` produces a valid package with correct exports
|
||||
- [ ] All tests pass: `npm test`
|
||||
- [ ] TypeScript strict mode compilation succeeds with no errors
|
||||
- [ ] Build output (`dist/`) is correct: ESM + CJS + declarations
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/README.md
|
||||
- docs/architecture/api-surface.md
|
||||
- docs/architecture/build-distribution.md
|
||||
- docs/architecture/cost-benefit.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
53
tasks/implementation/review/graph-complete.md
Normal file
53
tasks/implementation/review/graph-complete.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: review/graph-complete
|
||||
name: Review TaskGraph class implementation for correctness and API compliance
|
||||
status: pending
|
||||
depends_on:
|
||||
- graph/construction
|
||||
- graph/mutation
|
||||
- graph/queries
|
||||
- graph/subgraph-and-validation
|
||||
- graph/export
|
||||
- review/schemas-and-errors
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Review the TaskGraph class implementation before building analysis functions on top of it. The graph layer is the foundation for all analysis — incorrect behavior here propagates everywhere.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Construction methods match [graph-model.md](../../../docs/architecture/graph-model.md) error handling table exactly:
|
||||
- `fromTasks`: silent orphan nodes, `DuplicateNodeError`, idempotent duplicate edges
|
||||
- `fromRecords`: `TaskNotFoundError` for dangling edges, `DuplicateNodeError`, `DuplicateEdgeError`
|
||||
- `fromJSON`: validated against schema, orphans preserved
|
||||
- `addTask`: `DuplicateNodeError`
|
||||
- `addDependency`: `TaskNotFoundError`, `DuplicateEdgeError`
|
||||
- [ ] Edge direction is prerequisite→dependent throughout (matches Rust CLI convention)
|
||||
- [ ] Deterministic edge keys `${source}->${target}` used via `addEdgeWithKey` (ADR-006)
|
||||
- [ ] `topologicalOrder` throws `CircularDependencyError` with `cycles` populated (ADR-003)
|
||||
- [ ] `findCycles` returns actual cycle paths (not just SCCs)
|
||||
- [ ] `subgraph` returns internal-only edges (ADR-007)
|
||||
- [ ] Validation methods return arrays, never throw
|
||||
- [ ] Mutation error semantics match [errors-validation.md](../../../docs/architecture/errors-validation.md) table (no-op for remove, throws for update on nonexistent)
|
||||
- [ ] `export()`/`toJSON()` round-trips correctly
|
||||
- [ ] `raw` getter exposed, warning about direct mutation documented in code comments
|
||||
- [ ] All tests pass, including edge cases (empty graphs, single-node, cyclic graphs, disconnected components)
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/graph-model.md
|
||||
- docs/architecture/api-surface.md
|
||||
- docs/architecture/errors-validation.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
49
tasks/implementation/review/schemas-and-errors.md
Normal file
49
tasks/implementation/review/schemas-and-errors.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: review/schemas-and-errors
|
||||
name: Review schema, enum, and error implementations for consistency
|
||||
status: pending
|
||||
depends_on:
|
||||
- schema/enums
|
||||
- schema/input-schemas
|
||||
- schema/graph-schemas
|
||||
- schema/result-types
|
||||
- schema/numeric-methods-and-defaults
|
||||
- error/error-hierarchy
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Review the schema and error layer implementations before building the graph and analysis layers on top. This is a critical checkpoint because everything downstream depends on these types being correct and consistent with the architecture docs.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All TypeBox schemas match [schemas.md](../../../docs/architecture/schemas.md) exactly
|
||||
- [ ] All `Static<typeof>` type aliases correctly derived — no manual type definitions
|
||||
- [ ] Nullable helper used consistently in TaskInput (not in TaskGraphNodeAttributes)
|
||||
- [ ] Enum values match DB/frontmatter conventions exactly
|
||||
- [ ] Numeric method tables match spec tables exactly
|
||||
- [ ] `resolveDefaults` correctly separates "nullable categorical→default" from "label-only nullable→stays nullable"
|
||||
- [ ] Error class hierarchy is correct: all extend TaskgraphError, all have proper `name` and typed fields
|
||||
- [ ] `InvalidInputError` can be constructed from `Value.Errors()` output
|
||||
- [ ] `CircularDependencyError.cycles` type is `string[][]`
|
||||
- [ ] No Zod, no gray-matter, no js-yaml in any dependency
|
||||
- [ ] `package.json` lists only approved dependencies
|
||||
- [ ] All tests pass
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/schemas.md
|
||||
- docs/architecture/errors-validation.md
|
||||
- docs/architecture/frontmatter.md — supply chain constraints
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
44
tasks/implementation/schema/enums.md
Normal file
44
tasks/implementation/schema/enums.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: schema/enums
|
||||
name: Define TypeBox categorical enum schemas and type aliases
|
||||
status: pending
|
||||
depends_on:
|
||||
- setup/project-init
|
||||
scope: narrow
|
||||
risk: trivial
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Define all categorical enum schemas using `Type.Union([Type.Literal(...)])` pattern per [schemas.md](../../../docs/architecture/schemas.md). Each enum gets a schema constant (PascalCase + `Enum` suffix) and a `Static<typeof>` type alias (PascalCase, no suffix).
|
||||
|
||||
The six enums: `TaskScopeEnum`, `TaskRiskEnum`, `TaskImpactEnum`, `TaskLevelEnum`, `TaskPriorityEnum`, `TaskStatusEnum`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/schema/enums.ts` exports all six enum schemas and their type aliases
|
||||
- [ ] Each enum uses `Type.Union([Type.Literal("value"), ...])` pattern per [typebox-patterns.md](../../../docs/research/typebox-patterns.md)
|
||||
- [ ] `TaskScopeEnum`: `"single" | "narrow" | "moderate" | "broad" | "system"`
|
||||
- [ ] `TaskRiskEnum`: `"trivial" | "low" | "medium" | "high" | "critical"`
|
||||
- [ ] `TaskImpactEnum`: `"isolated" | "component" | "phase" | "project"`
|
||||
- [ ] `TaskLevelEnum`: `"planning" | "decomposition" | "implementation" | "review" | "research"`
|
||||
- [ ] `TaskPriorityEnum`: `"low" | "medium" | "high" | "critical"`
|
||||
- [ ] `TaskStatusEnum`: `"pending" | "in-progress" | "completed" | "failed" | "blocked"`
|
||||
- [ ] Type aliases derived via `Static<typeof>`: `TaskScope`, `TaskRisk`, `TaskImpact`, `TaskLevel`, `TaskPriority`, `TaskStatus`
|
||||
- [ ] Naming convention matches spec: `Enum` suffix on schema constants only, never on type aliases
|
||||
- [ ] `src/schema/index.ts` re-exports all schemas and types
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/schemas.md — enum definitions, naming convention
|
||||
- docs/research/typebox-patterns.md — TypeBox enum patterns, naming convention
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
42
tasks/implementation/schema/graph-schemas.md
Normal file
42
tasks/implementation/schema/graph-schemas.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
id: schema/graph-schemas
|
||||
name: Define TaskGraphNodeAttributes, TaskGraphEdgeAttributes, and SerializedGraph
|
||||
status: pending
|
||||
depends_on:
|
||||
- schema/enums
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Define graph attribute schemas and the serialized graph generic in `src/schema/graph.ts`. `TaskGraphNodeAttributes` carries only analysis-relevant metadata (no tags, assignee, due, etc.). `SerializedGraph` is a generic factory parameterized with node and edge attribute types.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/schema/graph.ts` exports:
|
||||
- `TaskGraphNodeAttributes` schema: `name: Type.String()`, optional categorical enums (scope, risk, impact, level, priority, status) — **not** nullable on the graph (absent = not stored)
|
||||
- `type TaskGraphNodeAttributes` derived
|
||||
- `TaskGraphNodeAttributesUpdate = Type.Partial(TaskGraphNodeAttributes)` and type alias
|
||||
- `TaskGraphEdgeAttributes` schema: `qualityRetention: Type.Optional(Type.Number())`
|
||||
- `type TaskGraphEdgeAttributes` derived
|
||||
- `SerializedGraph` generic factory parameterized with `<N extends TSchema, E extends TSchema, G extends TSchema>`
|
||||
- `TaskGraphSerialized = SerializedGraph(TaskGraphNodeAttributes, TaskGraphEdgeAttributes, Type.Object({}))` and type alias
|
||||
- [ ] `SerializedGraph` generic follows graphology JSON format: `attributes`, `options: { type: "directed", multi: false, allowSelfLoops: false }`, `nodes: [{ key, attributes }]`, `edges: [{ key, source, target, attributes }]`
|
||||
- [ ] No schema version field on `TaskGraphSerialized` per spec
|
||||
- [ ] Re-exported from `src/schema/index.ts`
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/schemas.md — graph attribute schemas, SerializedGraph
|
||||
- docs/research/typebox-patterns.md — section 6 (generic schema factories)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
39
tasks/implementation/schema/input-schemas.md
Normal file
39
tasks/implementation/schema/input-schemas.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: schema/input-schemas
|
||||
name: Define TaskInput, DependencyEdge, and Nullable helper
|
||||
status: pending
|
||||
depends_on:
|
||||
- schema/enums
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Define the `TaskInput` and `DependencyEdge` input schemas in `src/schema/task.ts`, plus the `Nullable` generic helper. `TaskInput` uses `Type.Optional(Nullable(...))` for categorical fields to support both absent and explicitly-null values (YAML frontmatter distinction).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/schema/task.ts` exports `Nullable` helper: `const Nullable = <T extends TSchema>(T: T) => Type.Union([T, Type.Null()])`
|
||||
- [ ] `TaskInput` schema defined with all fields per [schemas.md](../../../docs/architecture/schemas.md):
|
||||
- `id: Type.String()`, `name: Type.String()`, `dependsOn: Type.Array(Type.String())`
|
||||
- Categorical fields: `Type.Optional(Nullable(TaskXxxEnum))` for status, scope, risk, impact, level, priority
|
||||
- Metadata fields: `tags`, `assignee`, `due`, `created`, `modified`
|
||||
- [ ] `DependencyEdge` schema: `from: Type.String()`, `to: Type.String()`, `qualityRetention: Type.Optional(Type.Number({ default: 0.9 }))`
|
||||
- [ ] Type aliases derived: `type TaskInput = Static<typeof TaskInput>`, `type DependencyEdge = Static<typeof DependencyEdge>`
|
||||
- [ ] Re-exported from `src/schema/index.ts`
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/schemas.md — TaskInput, DependencyEdge, Nullable definitions
|
||||
- docs/research/typebox-patterns.md — section 6 (Nullable helper pattern)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
48
tasks/implementation/schema/numeric-methods-and-defaults.md
Normal file
48
tasks/implementation/schema/numeric-methods-and-defaults.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: schema/numeric-methods-and-defaults
|
||||
name: Implement categorical numeric functions and resolveDefaults
|
||||
status: pending
|
||||
depends_on:
|
||||
- schema/enums
|
||||
- schema/graph-schemas
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the standalone numeric functions that map categorical enum values to their numeric equivalents, plus `resolveDefaults` which fills in defaults for unassessed fields and computes derived numeric values. These live in `src/analysis/defaults.ts` per the project structure.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/analysis/defaults.ts` exports:
|
||||
- `scopeCostEstimate(scope: TaskScope): number` — maps to 1.0–5.0 per table
|
||||
- `scopeTokenEstimate(scope: TaskScope): number` — maps to 500–10000 per table
|
||||
- `riskSuccessProbability(risk: TaskRisk): number` — maps to 0.50–0.98 per table
|
||||
- `riskWeight(risk: TaskRisk): number` — maps to 0.02–0.50 (equals 1 - successProbability)
|
||||
- `impactWeight(impact: TaskImpact): number` — maps to 1.0–3.0 per table
|
||||
- `resolveDefaults(attrs: Partial<TaskGraphNodeAttributes> & Pick<TaskGraphNodeAttributes, 'name'>): ResolvedTaskAttributes`
|
||||
- [ ] All numeric mapping tables match [schemas.md](../../../docs/architecture/schemas.md) exactly:
|
||||
- Scope: single=1.0/500, narrow=2.0/1500, moderate=3.0/3000, broad=4.0/6000, system=5.0/10000
|
||||
- Risk: trivial=0.98/0.02, low=0.90/0.10, medium=0.80/0.20, high=0.65/0.35, critical=0.50/0.50
|
||||
- Impact: isolated=1.0, component=1.5, phase=2.0, project=3.0
|
||||
- [ ] `resolveDefaults` handles null/undefined categorical fields by falling back to: risk→medium, scope→narrow, impact→isolated
|
||||
- [ ] `resolveDefaults` populates derived fields: costEstimate, tokenEstimate, successProbability, riskWeight, impactWeight
|
||||
- [ ] Label-only fields (level, priority, status) remain nullable after resolution — no default value assigned
|
||||
- [ ] `riskWeight(risk)` equals `1 - riskSuccessProbability(risk)` — guaranteed by implementation
|
||||
- [ ] Unit tests covering every enum value's numeric mapping and resolveDefaults with mixed null/present inputs
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/schemas.md — numeric method tables, ResolvedTaskAttributes definition
|
||||
- docs/architecture/graph-model.md — categorical field defaults
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
42
tasks/implementation/schema/result-types.md
Normal file
42
tasks/implementation/schema/result-types.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
id: schema/result-types
|
||||
name: Define analysis result TypeBox schemas (RiskPathResult, DecomposeResult, WorkflowCostResult, etc.)
|
||||
status: pending
|
||||
depends_on:
|
||||
- schema/enums
|
||||
scope: narrow
|
||||
risk: trivial
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Define all analysis function return type schemas in `src/schema/results.ts`. These are the structured outputs of the cost-benefit and analysis functions. Each schema has both a TypeBox constant and a `Static<typeof>` type alias.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/schema/results.ts` exports all result schemas and types:
|
||||
- `RiskPathResult`: `{ path: string[], totalRisk: number }`
|
||||
- `DecomposeResult`: `{ shouldDecompose: boolean, reasons: string[] }`
|
||||
- `WorkflowCostOptions`: `{ includeCompleted?, limit?, propagationMode?, defaultQualityRetention? }`
|
||||
- `WorkflowCostResult`: `{ tasks: [...], totalEv, averageEv, propagationMode }`
|
||||
- `EvConfig`: `{ retries?, fallbackCost?, timeLost?, valueRate? }` with defaults (0, 0, 0, 0)
|
||||
- `EvResult`: `{ ev, pSuccess, expectedRetries }`
|
||||
- `RiskDistributionResult`: `{ trivial, low, medium, high, critical, unspecified }` — each `string[]`
|
||||
- [ ] All schemas use `Static<typeof>` for type aliases, no manual interface definitions
|
||||
- [ ] `WorkflowCostOptions.propagationMode` is `Type.Union([Type.Literal("independent"), Type.Literal("dag-propagate")])`
|
||||
- [ ] Re-exported from `src/schema/index.ts`
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/api-surface.md — return type definitions
|
||||
- docs/architecture/schemas.md — result schema definitions
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
51
tasks/implementation/setup/project-init.md
Normal file
51
tasks/implementation/setup/project-init.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: setup/project-init
|
||||
name: Initialize project with package.json, tsconfig, and build tooling
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: project
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Set up the TypeScript project from scratch. This is a greenfield project — the repo currently has only `AGENTS.md` and `docs/`. Initialize everything needed for a pure TypeScript ESM library: package.json, tsconfig.json, gitignore, and the src/ directory skeleton.
|
||||
|
||||
Per [build-distribution.md](../../../docs/architecture/build-distribution.md):
|
||||
- Package name: `@alkdev/taskgraph`
|
||||
- ESM primary, CJS compat
|
||||
- Targets: Node 18+, Deno, Bun (pure JS, no native addons)
|
||||
- Build: `tsc` for declarations + bundler for distribution
|
||||
- Dependencies: `graphology`, `graphology-dag`, `graphology-metrics`, `graphology-components`, `graphology-operators`, `@alkdev/typebox`, `yaml`
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `package.json` exists with name `@alkdev/taskgraph`, ESM primary (`"type": "module"`), CJS compat config
|
||||
- [ ] All production dependencies listed per [build-distribution.md](../../../docs/architecture/build-distribution.md) dependencies table
|
||||
- [ ] Dev dependencies include: `typescript`, `vitest` (or agreed test runner), `@types/node`
|
||||
- [ ] `tsconfig.json` configured for Node 18+ target, ESM module resolution, strict mode, declaration output
|
||||
- [ ] `.gitignore` covers `node_modules/`, `dist/`, `*.js.map`, `.env`
|
||||
- [ ] `src/` directory skeleton created per [build-distribution.md](../../../docs/architecture/build-distribution.md) project structure:
|
||||
- `src/index.ts`
|
||||
- `src/schema/index.ts`, `src/schema/enums.ts`, `src/schema/task.ts`, `src/schema/graph.ts`, `src/schema/results.ts`
|
||||
- `src/graph/index.ts`, `src/graph/construction.ts`, `src/graph/queries.ts`, `src/graph/mutation.ts`
|
||||
- `src/analysis/index.ts`, `src/analysis/critical-path.ts`, `src/analysis/bottleneck.ts`, `src/analysis/risk.ts`, `src/analysis/cost-benefit.ts`, `src/analysis/decompose.ts`, `src/analysis/defaults.ts`
|
||||
- `src/frontmatter/index.ts`, `src/frontmatter/parse.ts`, `src/frontmatter/serialize.ts`
|
||||
- `src/error/index.ts`
|
||||
- [ ] `test/` directory created with placeholder test files per build-distribution spec
|
||||
- [ ] `npm install` succeeds without errors
|
||||
- [ ] `npx tsc --noEmit` succeeds (empty source files, but config is valid)
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/build-distribution.md — project structure, dependencies, targets
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
42
tasks/implementation/setup/test-infrastructure.md
Normal file
42
tasks/implementation/setup/test-infrastructure.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
id: setup/test-infrastructure
|
||||
name: Configure test runner and shared test fixtures
|
||||
status: pending
|
||||
depends_on:
|
||||
- setup/project-init
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: project
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Set up the test infrastructure: configure Vitest (or chosen runner), create shared test fixtures and helpers for graph construction that all downstream test files will use. This avoids every test file building graphs from scratch.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Test runner configured in `package.json` scripts (`"test"`, `"test:watch"`, `"test:coverage"`)
|
||||
- [ ] Vitest config (or equivalent) exists with ESM support and TypeScript path resolution
|
||||
- [ ] Shared test fixture file created (e.g., `test/fixtures/graphs.ts`) with:
|
||||
- A simple linear chain graph (3-4 tasks, A→B→C→D)
|
||||
- A diamond dependency graph (A→B, A→C, B→D, C→D)
|
||||
- A graph with mixed categorical fields (some assessed, some null)
|
||||
- A graph with cycles for testing cycle detection
|
||||
- A larger graph (20+ nodes) for performance/bottleneck testing
|
||||
- [ ] Helper function to create a `TaskGraph` from `TaskInput[]` for one-liner test setup
|
||||
- [ ] Test runner executes successfully against placeholder test files
|
||||
- [ ] CI-compatible output format (no watch mode in default script)
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/build-distribution.md — test directory structure
|
||||
- docs/architecture/graph-model.md — graph construction examples for fixtures
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in New Issue
Block a user