8.4 KiB
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)
'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
app.get('/users/:id', {
schema: {
'x-requires': ['users:id'], // FAILS
'x-ensures': ['status:200']
}
}, handler)
Expected Behavior
Either:
- The
resource:idsyntax should parse correctly, OR - 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)
'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
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:
'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:
'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:
// 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:
// x-requires (2 routes):
'x-requires': ['subscriptions:subscriptionId']
From billing/invoices.js:
// x-requires (3 routes):
'x-requires': ['invoices:invoiceId']
From notifications/email-routes.js:
// 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:
// x-requires (12 routes):
'x-requires': ['webhooks:id']
From sessions-management/index.js:
// x-requires (3 routes):
'x-requires': ['sessions:jti']
From devices/*.js:
// x-requires (4 routes):
'x-requires': ['devices:id']
From workflow/index.js:
// 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):
'response_code(GET /resource/{response_body(this).data.id}) == 200'
✅ Data integrity (top-level):
'response_body(GET /resource/{response_body(this).data.id}).data.name == request_body(this).name'
✅ Collection consistency (top-level):
'exists item in response_body(GET /resource).data: item.id == response_body(this).data.id'
✅ State transitions (top-level):
'previous(response_body(GET /resource/{id}).data.status) != response_body(GET /resource/{id}).data.status'
✅ Tenant isolation (top-level):
'for item in response_body(this).data: item.tenantId == request_headers(this)["x-tenant-id"]'
✅ Deletion semantics (top-level):
'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)
- Fix
x-requiresparsing: Either supportresource:idsyntax or document the correct APOSTL expression format - Fix nested expression parsing: Allow
route_exists(),response_code(GET ...), etc. insideifconditionals - Improve error messages: Include file path, route method/path, and contract clause index in parse errors
Short-term (P1)
- Add a contract validator CLI:
npx apophis validate-contracts src/routes/**/*.jsthat reports all parse errors without running tests - Document parser limitations: Clearly state which APOSTL features work in which contexts (top-level vs nested)
Long-term (P2)
- Consider JSON Schema integration: Auto-derive
x-requiresfromrequiredparams fields - 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-categoryclassification (constructor/observer/mutator/destructor) - ✅ Bronze-level contracts (status codes, error consistency)
- ✅ Silver/Gold cross-operation contracts (where parser allows)
- ❌
x-requirespreconditions (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