# 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 } // 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