11 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| review/complete-library | Final review — validate full library against architecture docs | pending |
|
broad | low | project | 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 exactly — no missing exports, no extra exports (see Warnings W1, W2 for minor discrepancies)
- 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'ordefaultQualityRetention: 1.0) - Frontmatter parsing round-trips correctly:
parseFrontmatter(serializeFrontmatter(task))≈ task Value.Clean()andValue.Errors()used correctly throughout (noValue.Assert()where structured errors needed)- No gray-matter, no js-yaml, no Zod anywhere in the dependency tree
npm packproduces a valid package with correct exports (see Critical C1: CJS missing)- All tests pass:
npm test(590/590 passing) - TypeScript strict mode compilation succeeds with no errors
- Build output (
dist/) is correct: ESM + CJS + declarations (CJS output missing — see Critical C1)
References
- docs/architecture/README.md
- docs/architecture/api-surface.md
- docs/architecture/build-distribution.md
- docs/architecture/cost-benefit.md
Notes
Review Results
Files reviewed: 17 source files + 13 test files + 4 architecture docs + package.json + tsconfig.json Critical issues: 1 Warnings: 3 Suggestions: 4 Tests: 590/590 passing Lint (tsc --noEmit): Clean — zero errors Overall: changes requested (1 critical — missing CJS output)
Critical Issues
C1: CJS build output missing — package.json declares ./dist/index.cjs and ./dist/index.d.cts but tsc only produces ESM
Impact: The package.json exports map declares a CJS entry point at ./dist/index.cjs with types at ./dist/index.d.cts. However, tsc only produces ./dist/index.js (ESM) and ./dist/index.d.ts. The CJS files do not exist after npm run build. Any consumer using require('@alkdev/taskgraph') will get a MODULE_NOT_FOUND error.
Root cause: tsc with module: "Node16" produces ESM output only. It cannot produce dual ESM + CJS bundles. A separate build step (e.g., esbuild, tsup, or a second tsc pass with module: "CommonJS") is needed to produce the CJS output.
Evidence:
$ ls dist/index.*
dist/index.d.ts dist/index.d.ts.map dist/index.js dist/index.js.map
# index.cjs and index.d.cts are MISSING
Fix required (not implemented — reviewer does not implement):
- Add a CJS build step. Options:
- tsup:
tsup src/index.ts --format cjs --outDir dist --dts→ producesindex.cjs+index.d.cts - esbuild: similar approach
- Dual tsc pass: add a
tsconfig.cjs.jsonwithmodule: "CommonJS"andoutDir: "dist", renaming output to.cjs
- tsup:
- Update
npm run buildto run both ESM and CJS builds - Verify
npm packincludes both.jsand.cjsfiles and theexportsmap resolves correctly
Warnings
W1: BottleneckResult type exported but not in api-surface.md
The public API exports BottleneckResult as a named type (export type { BottleneckResult } in src/index.ts line 22), but api-surface.md only lists Array<{ taskId: string; score: number }> as the inline return type of bottlenecks. The architecture doc doesn't list BottleneckResult in the "Return Types" section.
Impact: Low — the export is useful for consumers who want to name the return type. It's a strict superset of what's documented. Recommend updating api-surface.md to include BottleneckResult in the Return Types section.
W2: workflowCost takes TaskGraphInner instead of TaskGraph — inconsistent with other analysis functions
All other analysis functions (bottlenecks, criticalPath, weightedCriticalPath, parallelGroups, riskPath, riskDistribution) take TaskGraph as their first argument. However, workflowCost takes TaskGraphInner (the raw graphology graph), requiring callers to use workflowCost(graph.raw) instead of workflowCost(graph).
The api-surface.md specifies function workflowCost(graph: TaskGraph, options?: WorkflowCostOptions) — the implementation diverges.
Impact: Medium — breaks the "all analysis functions accept TaskGraph" convention. Consumers must remember to pass .raw only for this one function. Tests already work around this.
Recommendation: Change workflowCost to accept TaskGraph and extract .raw internally, matching the other analysis functions. This is a one-line signature change.
W3: shouldDecomposeTask parameter type is Partial<TaskGraphNodeAttributes> & Pick<..., "name"> instead of TaskGraphNodeAttributes
The api-surface.md shows function shouldDecomposeTask(attrs: TaskGraphNodeAttributes), but the implementation uses Partial<TaskGraphNodeAttributes> & Pick<TaskGraphNodeAttributes, "name">. This is a more permissive type that accepts partial attributes with only name required.
Impact: Low — TaskGraphNodeAttributes is assignable to this type, so the function accepts what the spec says. The implementation type is actually more useful (allows passing raw node attributes which may have optional fields unset). Recommend updating api-surface.md to reflect the actual signature.
Suggestions
S1: Add test coverage for bottlenecks empty-graph early return (lines 44-46)
The bottlenecks function has an early return if (raw.order === 0) return [] (lines 44-46) that is not covered by tests. The test file test/analysis.test.ts has "returns empty array for empty graph" for criticalPath and parallelGroups, but the analogous test for bottlenecks appears to not hit this code path (88.23% statement coverage). The empty-graph test for criticalPath works because criticalPath delegates to the same computeLongestPath helper, but bottlenecks uses a different code path (graphology-metrics betweenness centrality, which throws on empty graphs).
Acceptability: This is a defensive guard for a known graphology edge case. The code is correct. While 88% is below the ~97% overall average, the uncovered lines are a single early-return guard. Low risk.
S2: Add test coverage for validateGraph dangling-reference detection (lines 78-93)
validation.ts has only 76% statement coverage, with lines 79-93 (the dangling-reference detection loop) uncovered. The subgraph-and-validation.test.ts test at line 404 explicitly notes this: "the only way to get a 'dangling reference' is through fromTask with orphan creation... but our fromRecords/constructor enforce no orphans." The test validates that a well-formed graph has zero dangling refs but doesn't actually exercise the detection code.
Acceptability: Dangling references can only occur if someone mutates raw directly (which the raw getter warns against). The detection code is defensive and correct. Moderate risk — if a consumer bypasses TaskGraph via raw, this code path would execute. A test that directly manipulates the graphology graph to create a dangling reference would provide coverage.
S3: Consider updating addDependency signature in api-surface.md to include optional qualityRetention
The implementation has addDependency(prerequisite: string, dependent: string, qualityRetention: number = 0.9) but the api-surface doc shows addDependency(prerequisite: string, dependent: string). The optional parameter is backward-compatible and useful. Recommend updating the doc.
S4: Constructor taking TaskGraphSerialized not documented in api-surface.md
The TaskGraph constructor accepts an optional TaskGraphSerialized parameter (new TaskGraph(data)) which delegates to fromJSON. This is a convenience that's not in the api-surface doc. Since it behaves identically to TaskGraph.fromJSON(data), this is fine, but recommend documenting it.
Recommendations (Priority Order)
- [Critical] Fix CJS build output — Add a CJS build step to produce
dist/index.cjsanddist/index.d.cts. Thepackage.jsonexports map promises these files but they don't exist. This blocks CJS consumers and causesMODULE_NOT_FOUNDerrors. - [Warning] Fix
workflowCostsignature — AcceptTaskGraphinstead ofTaskGraphInnerto match the documented API and maintain consistency with all other analysis functions. - [Warning] Update
api-surface.md— AddBottleneckResultto Return Types section, updateworkflowCostparameter type, and document the optionalqualityRetentiononaddDependency. - [Suggestion] Add test for
bottlenecksempty graph — One-line test fix for coverage gap. - [Suggestion] Add test for dangling-reference detection — Test by directly manipulating the raw graph.
Verification Summary
| Check | Result |
|---|---|
npm test |
✅ 590/590 passing (13 test files) |
npx tsc --noEmit |
✅ Clean — zero errors |
npm run build |
✅ Succeeds (ESM + declarations only) |
npm pack |
✅ Produces valid 51.5 kB tarball (97 files) |
| CJS output | ❌ dist/index.cjs and dist/index.d.cts MISSING |
| No gray-matter/js-yaml/Zod | ✅ Not in dependencies or node_modules |
| Value.Clean/Errors usage | ✅ Used correctly; no Value.Assert |
| Frontmatter round-trip | ✅ 7 round-trip tests passing |
| DAG propagation model | ✅ Works; independent mode as degenerate case tested |
| Construction paths | ✅ fromTasks, fromRecords, fromJSON, incremental all tested |
| Coverage: overall | 97.72% stmt, 95.17% branch, 100% func |
| Coverage: bottleneck.ts | 88.23% (empty-guard uncovered) |
| Coverage: validation.ts | 76.27% (dangling-ref detection uncovered) |
| Ambiguous Unicode | ✅ Only em-dashes (—) in comments, no invisible/control chars in code |
Summary
The @alkdev/taskgraph library is in strong shape: 590 tests pass, tsc strict mode is clean, the API is well-structured, and all acceptance criteria are met except for the missing CJS build output. The one critical issue (C1) is that package.json declares CJS exports but tsc alone cannot produce them — a build tooling change is needed. Two warnings relate to minor API surface documentation gaps and the workflowCost signature inconsistency. The two lower-coverage files (bottleneck.ts, validation.ts) have acceptable gaps — both are defensive code paths that are hard to trigger through the public API.