Files
apophis-fastify/docs/attic/root-history/NEXT_STEPS_426.md
T

372 lines
15 KiB
Markdown
Raw Normal View History

# NEXT_STEPS_426.md — Post-v2.x APOSTL Restoration & Remaining Work
## Status: v2.2 Complete (2026-04-27)
**Test count**: 503 passing, 0 failures
**New in v2.x**: Justin removed, APOSTL restored as primary and only contract language, cross-operation behavioral contracts re-enabled, all documentation updated
## Completed (v2.x)
### Justin Removal & APOSTL Restoration
- [x] Removed `subscript` dependency from package.json
- [x] Deleted `src/formula/justin.ts` — Justin wrapper with compile cache
- [x] Deleted `src/formula/context-builder.ts` — EvalContext → Justin context mapping
- [x] Restored APOSTL types in `src/types.ts`
- [x] Updated `src/infrastructure/hook-validator.ts` — APOSTL-only evaluation
- [x] Updated `src/domain/contract-validation.ts` — APOSTL-only evaluation
- [x] Updated `src/domain/schema-to-contract.ts` — generates APOSTL syntax
- [x] Updated `src/domain/error-suggestions.ts` — matches APOSTL syntax
- [x] Restored parser/evaluator files for APOSTL
- [x] Hand-converted all test schema annotations (~40 test files) from Justin back to APOSTL
- [x] Fixed APOSTL cross-operation support (pure GET calls, `previous(...)`, guarded prefetch)
- [x] Fixed `validateRouteContracts` to iterate `fastify.routes` directly
- [x] Fixed build errors across all modules
- [x] **Fixed runtime validation**: Dynamic contract lookup from `routeContractStore` at request time
### Behavioral Contract Documentation
- [x] `README.md` — v2.x rewrite with behavioral contract focus
- [x] `docs/getting-started.md` — behavioral examples + APOSTL reference
- [x] `docs/PLUGIN_CONTRACTS_SPEC.md` — APOSTL syntax
- [x] `docs/extensions/QUICK-REFERENCE.md` — APOSTL extension predicates
- [x] `docs/extensions/EXTENSION-PLUGIN-SYSTEM.md` — APOSTL predicate examples
- [x] `skills.md` — behavioral contract focus
- [x] `CHANGELOG.md` — v2.1.0 section documenting Justin removal
### Critical Safety Fixes (from Expert Assessments)
- [x] **C1**: Chaos two-level probability bug removed
- [x] **C2**: `Math.random()` in corruption — now requires injected RNG
- [x] **C3**: Seed collision — FNV-1a hash combine
- [x] **H1**: Hook validator 500s — formulas validated at registration time
- [x] **H2**: env-guard runtime throws — now validated at plugin registration
- [x] **P4**: Promise.race leak — timer cleanup added
- [x] **P9**: safe-regex false positives — actual execution timeout test added
- [x] **P11**: PARAM_PATTERN injection — URL encoding + validation added
### Architecture Extraction
- [x] `src/test/command-generator.ts`
- [x] `src/test/precondition-checker.ts`
- [x] `src/test/result-deduplicator.ts`
- [x] `src/test/route-filter.ts`
- [x] `src/test/plugin-contract-composer.ts`
- [x] `src/test/result-formatter.ts`
- [x] `src/test/api-operations.ts` (shared between petit and stateful runners)
- [x] `src/plugin/swagger.ts`
- [x] `src/plugin/spec-builder.ts`
- [x] `src/plugin/contract-builder.ts`
- [x] `src/plugin/stateful-builder.ts`
- [x] `src/plugin/check-builder.ts`
- [x] `src/plugin/cleanup-builder.ts`
---
## Remaining from v1.3 (Carried Forward)
### Medium Priority
- [ ] **F6**: CI/CD examples (`docs/ci-cd.md`) — GitHub Actions, GitLab CI, CircleCI workflows
### Quality Features
- [x] **Flake Detection** — Auto-rerun failing tests with varied seeds
- [ ] **Mutation Testing** (`src/quality/mutation.ts`) — Synthetic bug injection, contract strength scoring
### Performance & Implementation (John Carmack)
- [x] **P2**: `hashSchema` truncated to 16 chars — use full SHA-256 (64 hex chars)
- [x] **P3**: `PARSE_CACHE` Map has no TTL — add LRU cache with configurable max size
- [x] **P5**: Streaming NDJSON loads entire response — add chunked processing with limits
- [x] **P6**: `request-builder.ts` uses `Math.random()` fallback — deterministic fallback + warning (already clean in production)
- [x] **P8**: `topologicalSort` re-sorts on every `register()` — lazy sorting
### Observability (Charity Majors)
- [ ] **O1**: Zero OpenTelemetry integration — add tracing, metrics, correlation (deferred — not appropriate for test framework)
- [x] **O2**: No per-route chaos granularity — route overrides, include/exclude patterns
- [x] **O3**: No resilience verification after chaos — recovery check post-injection
- [x] **O4**: Runtime hooks evaluate on every request — pre-filter routes with contracts
- [x] **O5**: Arbiter Bug #1 — ScopeRegistry default scope ignored configured `default`
- [x] **O6**: Arbiter Bug #2`routes` option dropped in plugin contract builder
### Type Safety (Uncle Bob)
- [ ] **T1**: `OperationHeader` union with `string` — use branded type for extensions
- [ ] **T2**: `RequestStructure.body?: unknown` — discriminated union for body types
### Category Inference (Martin Fowler)
- [ ] **Cat1**: Hardcoded exact paths miss prefixed variants — regex/prefix matching
---
## New for v2.2: Arbiter Integration Stabilization (2026-04-27)
### P0: Targeted Chaos Testing ✅ COMPLETE
- Per-route include/exclude patterns for chaos injection
- Route-level chaos config overrides global config
- Resilience verification (retry after chaos injection)
### P1: Arbiter Bug Fixes ✅ COMPLETE
- **Bug #1**: ScopeRegistry default scope — now respects configured `default` scope
- **Bug #2**: Plugin contract builder — `routes` option now propagated to test runner
- **Bug #3**: Configurable invariants — deferred to v2.3
### P2: Schema Inference Fixes ✅ COMPLETE
- Disabled aggressive array-of-objects schema inference (was generating invalid `[]` accessors)
- Reduced false-positive contract violations from inferred schemas
---
## New for v2.1: Cross-Route Relationships (Arbiter Feedback)
**Design Decision**: Relationships are expressed as APOSTL predicates inside `x-ensures`. No new schema annotation needed — relationships are just postconditions.
```typescript
schema: {
'x-ensures': [
// Parent consistency
'response_body(this).tenantId == request_params(this).tenantId',
// Hypermedia link validation
'route_exists(response_body(this).controls.tenant.href) == true',
// Relationship validation
'relationship_valid("parent", request_params(this).tenantId, response_body(this).tenantId) == true'
]
}
```
### P0: Core Relationship Predicates ✅ COMPLETE
#### R1: `route_exists()` Extension Predicate ✅
**File**: `src/extensions/relationships.ts`
**Description**: Check that a URL resolves to a registered route.
```apostl
// Basic: check route exists
'route_exists(response_body(this).controls.tenant.href) == true'
// With method check:
'route_exists(response_body(this).controls.edit.href, response_body(this).controls.edit.method) == true'
// Negative: ensure link is NOT a route (external URL)
'route_exists(response_body(this).externalUrl) == false'
```
**Implementation**:
- Use `discoverRoutes()` to get all registered routes
- Match concrete URLs against route patterns (`/users/:id` matches `/users/user:alice`)
- Support method validation
**Invariants**:
- MUST: Pattern matching handle `:param` syntax
- MUST: Return `false` for unregistered routes, never throw
- MUST: Cache route discovery results per test run
- MAY NEVER: Match against routes registered after the check
#### R2: Route Pattern Matcher ✅ COMPLETE
**File**: `src/infrastructure/route-matcher.ts`
**Description**: Utility to match concrete URLs against Fastify route patterns.
```typescript
function matchRoutePattern(pattern: string, concreteUrl: string): {
matched: boolean
params: Record<string, string>
}
// Example:
matchRoutePattern('/users/:id', '/users/user:alice')
// → { matched: true, params: { id: 'user:alice' } }
```
**Invariants**:
- MUST: Support Fastify's `:param` and `*` wildcard syntax
- MUST: Return extracted parameters
- MUST: Handle trailing slashes consistently
- MAY NEVER: Match partial segments (e.g., `/users/:id` should NOT match `/users/admin/settings`)
### P1: Relationship & Cascade Validation ✅ COMPLETE
#### R3: `relationship_valid()` Extension Predicate ✅
**File**: `src/extensions/relationships.ts`
**Description**: Validate parent-child consistency.
```apostl
// Verify resource belongs to parent from path
'relationship_valid("parent", request_params(this).tenantId, response_body(this).tenantId) == true'
// Verify arbitrary relationship type
'relationship_valid("owner", request_params(this).userId, response_body(this).ownerId) == true'
```
**Implementation**:
- Track resource creation/deletion in test state
- Check that child resources reference existing parents
**Invariants**:
- MUST: Track resource lifecycle across test commands
- MUST: Support arbitrary relationship types (not hardcoded)
- MAY NEVER: Report false positives due to test isolation issues
#### R4: `cascade_valid()` Extension Predicate ✅
**File**: `src/extensions/relationships.ts`
**Description**: Verify that deleting a parent resource makes children inaccessible.
```apostl
// After DELETE /tenants/:id, verify cascade
'cascade_valid("tenant", request_params(this).id, ["application", "user"]) == true'
```
**Implementation**:
- Track DELETE operations in test state
- For deleted resources, check child routes return 404
- Accept array of child resource types to validate
**Invariants**:
- MUST: Verify HTTP 404 for child resources after parent deletion
- MUST: Support soft-delete (200 with deleted flag) vs hard-delete (404)
- MUST: Accept list of child types to check
- MAY NEVER: Assume all DELETEs are hard deletes
#### R5: Hypermedia Validation Phase ✅ COMPLETE
**File**: `src/test/hypermedia-validator.ts`
**Description**: Post-test validation that checks all hypermedia links across responses.
```typescript
const result = await fastify.apophis.contract({ depth: 'standard' });
// Optional: Run hypermedia validation
const hypermediaReport = await fastify.apophis.validateHypermedia({
checkLinks: true, // verify hrefs resolve to routes
checkDescriptors: true, // verify action descriptors exist
checkMethods: true, // verify methods match route definitions
checkRelationships: true // verify parent-child consistency
});
```
**Output format**:
```json
{
"brokenLinks": [
{
"route": "GET /users/user:alice",
"control": "tenant",
"href": "/tenants/tenant:acme",
"issue": "route_not_found",
"suggestion": "Route GET /tenants/:id is not registered"
}
],
"orphanResources": [
{
"route": "GET /applications/app:123",
"field": "tenantId",
"value": "tenant:deleted",
"issue": "parent_not_found"
}
]
}
```
**Invariants**:
- MUST: Collect all hypermedia links from test responses
- MUST: Validate links against registered routes
- MUST: Report per-route summaries
- MAY NEVER: Fail the main contract test suite due to hypermedia issues (separate report)
### P2: Stateful Test Enhancement ✅ COMPLETE
#### R6: Automatic Path Substitution in Stateful Tests ✅
**File**: `src/domain/request-builder.ts`
**Description**: Infer path parameters from previously created resources.
```typescript
// Apophis generates:
// Step 1: POST /tenants → { id: 'tenant:acme' }
// Step 2: POST /tenants/tenant:acme/applications → { id: 'app:123' }
// Step 3: GET /tenants/tenant:acme/applications/app:123
```
**Implementation** ✅:
- Enhanced `substitutePathParams()` in `src/domain/request-builder.ts`
- Supports patterns: `tenantId`, `tenant_id`, `userId`
- Uses `inferResourceTypeFromParam()` to map param names to resource types
- Falls back to arbitrary generation if no matching resource in state
- Added test: `stateful runner substitutes path params from resource state`
**Invariants**:
- ✅ MUST: Only substitute when resource type matches param name
- ✅ MUST: Fall back to random/arbitrary generation if no matching resource
- ✅ MUST: Not break existing stateful tests
- ✅ MAY NEVER: Generate invalid URLs due to substitution errors
#### R7: Cascade Validation in Stateful Tests ✅
**File**: `src/test/cascade-validator.ts`
**Description**: After DELETE commands, automatically verify children are inaccessible.
```typescript
// Stateful test runs DELETE /tenants/tenant:acme
// Cascade validator then checks:
// - GET /tenants/tenant:acme → 404
// - GET /tenants/tenant:acme/applications → 404
// - GET /tenants/tenant:acme/users → 404
```
**Implementation** ✅:
- Created `src/test/cascade-validator.ts` with `createCascadeValidator()`
- `findChildRoutes()` discovers nested routes under a parent pattern
- `validateAfterDelete()` generates cascade checks with configurable depth
- `extractPathParamsFromUrl()` extracts params for URL substitution
- Added comprehensive tests for cascade validation
**Invariants**:
- ✅ MUST: Only trigger after DELETE commands that return 2xx/204
- ✅ MUST: Use route pattern matching to find child routes
- ✅ MUST: Configurable (on/off, max depth)
- ✅ MAY NEVER: Cause test suite to fail due to cascade check timing issues
- MAY NEVER: Cause test suite to fail due to cascade check timing issues
---
## Implementation Order
### Phase 1: Foundation (P0) ✅ COMPLETE
1. ✅ Create `src/infrastructure/route-matcher.ts` — pattern matching utility
2. ✅ Create `src/extensions/relationships.ts``route_exists()` predicate
3. ✅ Add tests for route pattern matcher
4. ✅ Add tests for `route_exists()` predicate
### Phase 2: Relationship Predicates (P1) ✅ COMPLETE
5. ✅ Add `relationship_valid()` predicate
6. ✅ Add `cascade_valid()` predicate
7. ✅ Create `src/test/hypermedia-validator.ts` — collect and validate links
8. ✅ Hypermedia validation via APOSTL `route_exists()` predicate (no imperative API needed)
9. ✅ Add tests for all predicates and hypermedia validation
### Phase 3: Stateful Enhancement (P2) ✅ COMPLETE
10. ✅ Enhanced `src/domain/request-builder.ts` — automatic path substitution from resource state
11. ✅ Created `src/test/cascade-validator.ts` — automatic cascade checks after DELETE
12. ✅ Added tests for automatic path substitution
13. ✅ Added tests for cascade validation
### Phase 4: Integration & Polish ✅ COMPLETE
14. ✅ Update documentation with relationship predicate examples
15. ✅ Update `FEEDBACK-cross-route-relationships.md` with implementation status
16. ⏳ Performance testing with Arbiter's 30+ route families (deferred)
17. ✅ Release v2.1
---
## Metrics
| Metric | v2.0 | v2.x | v2.1 |
|--------|------|------|------|
| Tests passing | 343 | 476 | **503** |
| Contract language | Justin | APOSTL | APOSTL |
| Cross-operation support | ❌ | ✅ | ✅ |
| Cross-route predicates | 0 | 0 | **3** (`route_exists`, `relationship_valid`, `cascade_valid`) |
| Hypermedia validation | ❌ | ❌ | **✅** |
| Automatic path substitution | ❌ | ❌ | **✅** |
| Cascade validation | ❌ | ❌ | **✅** |
---
## Reference
- **Cross-Route Feedback**: `FEEDBACK-cross-route-relationships.md`
- **Cross-Operation Feedback**: `FEEDBACK-cross-operation-expressiveness.md`
- **Previous Steps**: `NEXT_STEPS_425.md`
- **Plugin Contracts Spec**: `docs/PLUGIN_CONTRACTS_SPEC.md`
- **Extension System**: `docs/extensions/EXTENSION-PLUGIN-SYSTEM.md`
- **Arbiter Collaboration**: Contact via GitHub issues/PRs