Part VII: V2 Roadmap
112/112 ACs at 100%. The system works. Now make it harder to break.
What V1 Achieved
The typed specifications system, as described in Parts I-VI, provides:
- 20 features defined as abstract classes
- 112 acceptance criteria as abstract methods
- 3 decorators linking 15 test files to features
- A compliance scanner producing console, JSON, and exit code output
- A quality gate that fails on critical coverage gaps
- A self-referential loop (the system tracks itself)
- 100% AC coverage
This is production-grade for a solo project. But for team adoption or long-term maintenance, gaps remain.
Gap 1: Workflow Integration
Current state: The compliance scanner is a standalone script. You have to remember to run it.
V2 goal: Integrate the scanner into the local workflow so it runs automatically.
The site's build and test workflow runs through scripts/workflow.js — a 1,000+ line interactive menu that orchestrates git, build, test, and deploy. The compliance scanner should be a step in this menu:
── Workflow ──
1. Build static site
2. Run unit tests
3. Run E2E tests
4. Run visual regression
5. Run accessibility tests
6. Run compliance scanner ← new
7. Push to main ── Workflow ──
1. Build static site
2. Run unit tests
3. Run E2E tests
4. Run visual regression
5. Run accessibility tests
6. Run compliance scanner ← new
7. Push to mainThe scanner should also run as part of npm run test:all:
{
"test:all": "npx vitest run && npx playwright test && npx tsx scripts/compliance-report.ts --strict"
}{
"test:all": "npx vitest run && npx playwright test && npx tsx scripts/compliance-report.ts --strict"
}This way, the full test cycle includes compliance verification. No separate step to remember.
Gap 2: Pre-Commit Hooks
Current state: Nothing prevents pushing code with missing decorator coverage.
V2 goal: A pre-commit hook that runs the compliance scanner and blocks the push if critical ACs are uncovered.
Using Husky:
# .husky/pre-push
npx tsx scripts/compliance-report.ts --strict# .husky/pre-push
npx tsx scripts/compliance-report.ts --strictThis catches the scenario where a developer adds a new AC to a feature definition but forgets to decorate a test with @Implements. The push fails locally — before it reaches Vercel.
Why pre-push and not pre-commit? Because the scanner reads test files, and you might commit feature definitions and tests in separate commits during development. The gate should enforce at the push boundary, not the commit boundary.
Gap 3: Historical Trend Tracking
Current state: The scanner output is ephemeral. Run it, see the result, it's gone.
V2 goal: Persist JSON reports locally, one per build, to track trends over time.
# After each successful build
npx tsx scripts/compliance-report.ts --json > \
docs/compliance/$(date +%Y%m%d-%H%M%S).json# After each successful build
npx tsx scripts/compliance-report.ts --json > \
docs/compliance/$(date +%Y%m%d-%H%M%S).jsonWith a directory of timestamped JSON files, you can:
- Chart AC coverage over time (should be monotonically non-decreasing)
- Detect when features were added (new entries in the report)
- Track when gaps were closed (coverage jumps from 90% to 100%)
- Archive compliance state for audits
A simple script could render these into a trend chart — or they could be served as a page on the site itself.
Gap 4: Reverse Mapping
Current state: The scanner maps Feature → AC → Tests. You can ask "which tests cover NAV?" but not "which features does navigation.spec.ts cover?"
V2 goal: Bidirectional mapping. Given a test file, show all features it impacts.
This is useful for:
- Impact analysis: "I'm about to refactor
navigation.spec.ts— which features will be affected?" - Code review: "This PR modifies tests in 3 files — which features are they linked to?"
- New developer onboarding: "I'm looking at
theme.spec.ts— what does it verify?"
Implementation: the cross-reference data already exists. The scanner just needs a second output mode:
npx tsx scripts/compliance-report.ts --by-testnpx tsx scripts/compliance-report.ts --by-test ── Test → Feature Mapping ──
test/e2e/navigation.spec.ts
NAV: tocClickLoadsPage, backButtonRestores, activeItemHighlights,
anchorScrollsSmoothly, directUrlLoads, f5ReloadPreserves,
deepLinkLoads, bookmarkableUrl
test/e2e/theme.spec.ts
THEME: toggleSwitchesDarkLight, persistsAcrossReload, ...
ACCENT: accentChangesColors, accentPersists, ... ── Test → Feature Mapping ──
test/e2e/navigation.spec.ts
NAV: tocClickLoadsPage, backButtonRestores, activeItemHighlights,
anchorScrollsSmoothly, directUrlLoads, f5ReloadPreserves,
deepLinkLoads, bookmarkableUrl
test/e2e/theme.spec.ts
THEME: toggleSwitchesDarkLight, persistsAcrossReload, ...
ACCENT: accentChangesColors, accentPersists, ...Gap 5: ESLint Enforcement
Current state: If a developer writes a test method without @Implements or @Exclude, nothing warns them. The scanner will report the gap, but only if they remember to run it.
V2 goal: An ESLint rule that flags test methods missing decorators.
Two rules:
require-feature-test: Every test class intest/must have@FeatureTest(SomeFeature).require-implements-or-exclude: Every method in a@FeatureTest-decorated class must have either@Implements<Feature>('ac')or@Exclude().
These rules catch the gap at edit time, in the IDE, before the scanner ever runs. They're the "shift left" complement to the scanner's "shift right" verification.
Gap 6: Dynamic Test Granularity
Current state: Some test files use the coversACs batch pattern instead of per-method @Implements:
@FeatureTest(VisualRegressionFeature)
class VisualTests {
static readonly coversACs: (keyof VisualRegressionFeature)[] = [
'desktopBaselinesMatch', 'mobileBaselinesMatch',
'themeVariantsMatch', 'fullPageStitching',
];
// ... dynamic test generation in loops
}@FeatureTest(VisualRegressionFeature)
class VisualTests {
static readonly coversACs: (keyof VisualRegressionFeature)[] = [
'desktopBaselinesMatch', 'mobileBaselinesMatch',
'themeVariantsMatch', 'fullPageStitching',
];
// ... dynamic test generation in loops
}The scanner sees 4 ACs covered. But it can't tell which specific test covers which AC. If mobileBaselinesMatch starts failing, you have to read the test code to find the relevant loop iteration.
V2 goal: Generate per-test @Implements decorators even for dynamic tests.
For the accessibility contrast matrix (8 accents x 3 modes), this means generating 24 test methods with individual decorators instead of one batch:
// Instead of: static coversACs = ['allAccentsPassDark', ...]
// Generate:
for (const accent of accents) {
for (const mode of modes) {
@Implements<ContrastFeature>(`${mode}PassesContrast`)
test(`${accent} passes contrast in ${mode}`, ...);
}
}// Instead of: static coversACs = ['allAccentsPassDark', ...]
// Generate:
for (const accent of accents) {
for (const mode of modes) {
@Implements<ContrastFeature>(`${mode}PassesContrast`)
test(`${accent} passes contrast in ${mode}`, ...);
}
}This requires either a code generation step or a creative use of decorator factories.
Gap 7: Compliance as a Feature
Current state: Infrastructure improvements (robots.txt, 404 page, PWA) are not tracked as typed specifications.
V2 goal: Define infrastructure features with ACs, just like user-facing features:
export abstract class InfrastructureFeature extends Feature {
readonly id = 'INFRA';
readonly title = 'Infrastructure & Deployment';
readonly priority = Priority.Medium;
abstract robotsTxtConfigured(): ACResult;
abstract custom404PageExists(): ACResult;
abstract cacheHeadersOptimized(): ACResult;
abstract sitemapIncludesAllPages(): ACResult;
}export abstract class InfrastructureFeature extends Feature {
readonly id = 'INFRA';
readonly title = 'Infrastructure & Deployment';
readonly priority = Priority.Medium;
abstract robotsTxtConfigured(): ACResult;
abstract custom404PageExists(): ACResult;
abstract cacheHeadersOptimized(): ACResult;
abstract sitemapIncludesAllPages(): ACResult;
}The same system that verifies "does the search feature work?" can verify "does the infrastructure meet standards?" — same abstract classes, same decorators, same compliance scanner.
Priority Order
| Gap | Effort | Impact | Priority |
|---|---|---|---|
| Workflow integration | Low | High | Do first |
| Pre-commit hooks | Low | High | Do first |
| Historical tracking | Low | Medium | Do second |
| Reverse mapping | Medium | Medium | Do second |
| ESLint enforcement | High | High | Do third |
| Dynamic test granularity | Medium | Low | Do later |
| Infrastructure as features | Low | Low | Do later |
The first two gaps are each under an hour of work and prevent the most common failure mode: forgetting to run the scanner.
The Bigger Picture
Typed specifications are not the end of the quality journey. They're a layer in a stack:
Each layer answers a different question:
- Code: "Does it compile?"
- Tests: "Does it work?"
- Coverage gates: "Did we regress?"
- Typed specifications: "Is every feature verified?"
- Compliance trends: "Are we getting better?"
V1 built layers 1-4. V2 closes the loop with layer 5.
Beyond V2: The tspec Product
The V2 roadmap above addresses gaps within the existing scanner. But a broader vision emerged from the comparison against eight alternative approaches: what if typed specifications became a full enterprise product?
The tspec product series designs the answer: an M3 meta-metamodel for custom requirement flavors, seven language backends (C#, TypeScript, Java, Groovy, Python, Go, Rust), a Diem CMF instance as the server (auto-generated API, dashboard, and workflow engine from ~200 lines of content model), and enterprise features (SSO, multi-tenant, audit trails, bidirectional Jira sync) targeting large industrial CAC40 companies.
Every gap in this V2 roadmap — and 35 more identified in the comparison series — is addressed in that design.
Previous: Part VI: The Compliance Scanner Back to: Series Index