chore: crush git history - reborn from consolidation on 2026-03-10
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
# FEEDBACK: APOSTL Parser Limitations Blocking Behavioral Contracts
|
||||
|
||||
**From:** Arbiter Team (opencode integration)
|
||||
**Date:** 2026-04-28
|
||||
**Severity:** High - prevents adoption of Silver/Gold behavioral contracts
|
||||
**Apophis Version:** 2.x (latest)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
We've spent significant effort upgrading our route contracts from Bronze (tautological) to Silver/Gold (behavioral with cross-operation causality, data integrity, and state transitions). However, **multiple documented APOSTL features fail at parse time**, forcing us to strip contracts back to Bronze level or remove features entirely.
|
||||
|
||||
We cannot leverage the full power of Apophis as documented. This feedback documents exact parser failures with minimal reproductions.
|
||||
|
||||
---
|
||||
|
||||
## Issue 1: `x-requires` Resource Identifier Syntax Fails to Parse
|
||||
|
||||
### Documented Syntax (from getting-started.md line 227)
|
||||
|
||||
```typescript
|
||||
'x-requires': ['users:id'] // requires a user resource to exist
|
||||
```
|
||||
|
||||
### Actual Behavior
|
||||
|
||||
**Parse Error:**
|
||||
```
|
||||
Parse error at position 5: (found ':')
|
||||
users:userKey
|
||||
^
|
||||
Unexpected token
|
||||
```
|
||||
|
||||
### Impact
|
||||
|
||||
We cannot declare route preconditions. This breaks:
|
||||
- Observer routes that need resources to exist before testing
|
||||
- Mutator routes that should only run on existing resources
|
||||
- Destructor routes that require resources to delete
|
||||
|
||||
### Workaround
|
||||
|
||||
We stripped ALL `x-requires` from our contracts. This means Apophis cannot know which routes depend on which resources, likely breaking stateful test generation.
|
||||
|
||||
### Minimal Reproduction
|
||||
|
||||
```typescript
|
||||
app.get('/users/:id', {
|
||||
schema: {
|
||||
'x-requires': ['users:id'], // FAILS
|
||||
'x-ensures': ['status:200']
|
||||
}
|
||||
}, handler)
|
||||
```
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
Either:
|
||||
1. The `resource:id` syntax should parse correctly, OR
|
||||
2. Documentation should show the correct APOSTL expression format for preconditions
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: `route_exists()` Inside Conditionals Fails to Parse
|
||||
|
||||
### Documented Syntax (from getting-started.md line 742)
|
||||
|
||||
```typescript
|
||||
'route_exists(this).controls.self.href == true'
|
||||
```
|
||||
|
||||
### Actual Behavior
|
||||
|
||||
When used inside an `if` conditional (which is necessary since we only want to check hypermedia on success):
|
||||
|
||||
```
|
||||
Parse error at position 31: (found '(')
|
||||
if status:200 then route_exists(this).controls.self.href == true else true
|
||||
^
|
||||
Expected "else"
|
||||
```
|
||||
|
||||
### Impact
|
||||
|
||||
We cannot validate hypermedia links in success cases. This breaks:
|
||||
- HATEOAS contract verification
|
||||
- Self-link validation
|
||||
- Action descriptor integrity checks
|
||||
|
||||
### Workaround
|
||||
|
||||
Strip all `route_exists()` calls from contracts.
|
||||
|
||||
### Minimal Reproduction
|
||||
|
||||
```typescript
|
||||
app.get('/users/:id', {
|
||||
schema: {
|
||||
'x-ensures': [
|
||||
// FAILS - parser chokes on route_exists inside conditional
|
||||
'if status:200 then route_exists(this).controls.self.href == true else true'
|
||||
]
|
||||
}
|
||||
}, handler)
|
||||
```
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
`route_exists()` should be valid inside `if` expressions, or the docs should show the correct nesting syntax.
|
||||
|
||||
---
|
||||
|
||||
## Issue 3: `response_body(GET /path/{id})` Inside Conditionals May Fail
|
||||
|
||||
### Observed Pattern
|
||||
|
||||
Cross-operation calls like:
|
||||
```typescript
|
||||
'response_code(GET /users/{response_body(this).id}) == 200'
|
||||
```
|
||||
|
||||
Work fine as top-level expressions. But we suspect nesting them inside conditionals may also fail (we haven't tested extensively due to Issues 1 and 2 blocking progress).
|
||||
|
||||
### Question for Apophis Team
|
||||
|
||||
Are cross-operation calls valid inside `if` expressions? Example:
|
||||
```typescript
|
||||
'if status:201 then response_code(GET /users/{response_body(this).id}) == 200 else true'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issue 4: Lack of Clear Error Context
|
||||
|
||||
### Problem
|
||||
|
||||
Parse errors show:
|
||||
```
|
||||
Parse error at position 5: (found ':')
|
||||
users:userKey
|
||||
```
|
||||
|
||||
But they do NOT show:
|
||||
- Which route file caused the error
|
||||
- Which route definition (path/method)
|
||||
- Which specific contract clause failed
|
||||
|
||||
With 100+ routes, debugging requires binary search through files.
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
```
|
||||
Parse error in route GET /tenant/users/:userKey
|
||||
File: src/routes/user-directory/index.js:150
|
||||
Contract: x-requires[0]
|
||||
Expression: 'users:userKey'
|
||||
Parse error at position 5: (found ':')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What We Had to Remove
|
||||
|
||||
Here's the complete list of behavioral contracts we WROTE but had to DELETE due to parser failures:
|
||||
|
||||
### From `user-directory/index.js`:
|
||||
```javascript
|
||||
// All x-requires (6 routes affected):
|
||||
'x-requires': ['users:userKey']
|
||||
|
||||
// Hypermedia validation (2 routes affected):
|
||||
'if status:200 then route_exists(this).controls.self.href == true else true'
|
||||
```
|
||||
|
||||
### From `billing/subscriptions.js`:
|
||||
```javascript
|
||||
// x-requires (2 routes):
|
||||
'x-requires': ['subscriptions:subscriptionId']
|
||||
```
|
||||
|
||||
### From `billing/invoices.js`:
|
||||
```javascript
|
||||
// x-requires (3 routes):
|
||||
'x-requires': ['invoices:invoiceId']
|
||||
```
|
||||
|
||||
### From `notifications/email-routes.js`:
|
||||
```javascript
|
||||
// x-requires (3 routes):
|
||||
'x-requires': ['notifications:notificationId']
|
||||
|
||||
// Hypermedia:
|
||||
'if status:200 then route_exists(this).controls.self.href == true else true'
|
||||
```
|
||||
|
||||
### From `webhooks-management/index.js`:
|
||||
```javascript
|
||||
// x-requires (12 routes):
|
||||
'x-requires': ['webhooks:id']
|
||||
```
|
||||
|
||||
### From `sessions-management/index.js`:
|
||||
```javascript
|
||||
// x-requires (3 routes):
|
||||
'x-requires': ['sessions:jti']
|
||||
```
|
||||
|
||||
### From `devices/*.js`:
|
||||
```javascript
|
||||
// x-requires (4 routes):
|
||||
'x-requires': ['devices:id']
|
||||
```
|
||||
|
||||
### From `workflow/index.js`:
|
||||
```javascript
|
||||
// x-requires (3 routes):
|
||||
'x-requires': ['workflow_handoffs:id']
|
||||
'x-requires': ['workflow_lineages:lineageId']
|
||||
```
|
||||
|
||||
**Total: 39 routes had behavioral contracts stripped due to parser limitations.**
|
||||
|
||||
---
|
||||
|
||||
## Current State After Workarounds
|
||||
|
||||
We've kept the behavioral contracts that DO work:
|
||||
|
||||
✅ **Cross-operation causality** (top-level):
|
||||
```javascript
|
||||
'response_code(GET /resource/{response_body(this).data.id}) == 200'
|
||||
```
|
||||
|
||||
✅ **Data integrity** (top-level):
|
||||
```javascript
|
||||
'response_body(GET /resource/{response_body(this).data.id}).data.name == request_body(this).name'
|
||||
```
|
||||
|
||||
✅ **Collection consistency** (top-level):
|
||||
```javascript
|
||||
'exists item in response_body(GET /resource).data: item.id == response_body(this).data.id'
|
||||
```
|
||||
|
||||
✅ **State transitions** (top-level):
|
||||
```javascript
|
||||
'previous(response_body(GET /resource/{id}).data.status) != response_body(GET /resource/{id}).data.status'
|
||||
```
|
||||
|
||||
✅ **Tenant isolation** (top-level):
|
||||
```javascript
|
||||
'for item in response_body(this).data: item.tenantId == request_headers(this)["x-tenant-id"]'
|
||||
```
|
||||
|
||||
✅ **Deletion semantics** (top-level):
|
||||
```javascript
|
||||
'response_code(GET /resource/{request_params(this).id}) == 404'
|
||||
```
|
||||
|
||||
❌ **All `x-requires` removed** (39 routes affected)
|
||||
❌ **All `route_exists()` removed** (6 routes affected)
|
||||
❌ **Cannot nest cross-operation calls inside conditionals** (untested but suspected)
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Immediate (P0)
|
||||
|
||||
1. **Fix `x-requires` parsing**: Either support `resource:id` syntax or document the correct APOSTL expression format
|
||||
2. **Fix nested expression parsing**: Allow `route_exists()`, `response_code(GET ...)`, etc. inside `if` conditionals
|
||||
3. **Improve error messages**: Include file path, route method/path, and contract clause index in parse errors
|
||||
|
||||
### Short-term (P1)
|
||||
|
||||
4. **Add a contract validator CLI**: `npx apophis validate-contracts src/routes/**/*.js` that reports all parse errors without running tests
|
||||
5. **Document parser limitations**: Clearly state which APOSTL features work in which contexts (top-level vs nested)
|
||||
|
||||
### Long-term (P2)
|
||||
|
||||
6. **Consider JSON Schema integration**: Auto-derive `x-requires` from `required` params fields
|
||||
7. **Add IDE support**: VS Code extension that highlights invalid APOSTL expressions at write-time
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
We operate a large Fastify API (40+ route families, 200+ routes). Our goal is to have Gold-level behavioral contracts on every route. We've completed:
|
||||
|
||||
- ✅ Explicit JSON Schema on all routes
|
||||
- ✅ `x-category` classification (constructor/observer/mutator/destructor)
|
||||
- ✅ Bronze-level contracts (status codes, error consistency)
|
||||
- ✅ Silver/Gold cross-operation contracts (where parser allows)
|
||||
- ❌ `x-requires` preconditions (blocked by Issue 1)
|
||||
- ❌ Hypermedia validation (blocked by Issue 2)
|
||||
|
||||
We want to be an Apophis success story. These parser issues are the only blockers.
|
||||
|
||||
---
|
||||
|
||||
## Contact
|
||||
|
||||
This feedback was generated during active route decoration work. We're available to test fixes, provide more reproductions, or discuss syntax design.
|
||||
|
||||
**Priority:** Blocking production adoption of behavioral contracts
|
||||
**Impact:** 39 routes cannot express preconditions; 6 routes cannot validate hypermedia
|
||||
Reference in New Issue
Block a user