contracts.md
description: QA API contracts between Go backend and Rescript/TS frontend agent: plan
model: opencode-go/kimi-k2.6
API Contracts Audit
Audit all shared types passed between the Go backend (server/) and Rescript/TypeScript frontend (client/) via the Yjs CRDT boundary. Ensure type compatibility and field parity.
Pre-Audit Setup
- Read audit history - Check
.opencode/contracts-audit.mdfor user-confirmed acceptable differences - Scan for inline annotations - Look for
// contracts:ignore <reason>comments in source files
Files to Analyze
Type Definitions (REQUIRED)
server/waypoint/type.go- Go Waypoint structclient/src/ffi/waypoint.ts- TypeScript WaypointAttributes interface
client/src/ffi/Waypoint.res- Rescript waypointAttributes type
Serialization/Processing Code (REQUIRED)
server/waypoint/parser.go- extractWaypoint() functionserver/waypoint/sync.go- SyncToArray() and related functionsclient/src/ffi/waypoint.ts- TipTap addAttributes() and node definitionclient/src/ffi/changeset.ts- Yjs changeset operations on waypoints
Audit Checklist
1. Field Parity Check
For each type definition, verify:
- [ ] All fields present on both sides - Every field in Go struct exists in TS/Rescript
- [ ] Field names match JSON tags - Go json:"fieldname" matches TS/Rescript property names
- [ ] No missing fields - No field exists on one side without equivalent on other
2. Type Compatibility Check
For each shared field, verify:
- [ ] String fields - All sides use string type
- [ ] Array fields - Element types match (e.g., []string vs string[] vs array<string>)
- [ ] Numeric fields - Check for semantic equivalence:
- Go float32 / []float32 ↔ TS number / [number, number] ↔ Rescript float / (float, float) = ✓ ACCEPTABLE
- Any other numeric type mismatches = ⚠️ INVESTIGATE
- [ ] Optional fields - Nullability matches (omitempty vs | null vs option<...>)
3. Yjs Attribute Handling
Verify TipTap extension in client/src/ffi/waypoint.ts:
- [ ] addAttributes() covers all fields - Every field from Go struct has parseHTML handler
- [ ] Attribute names match - HTML attribute names match Go JSON tags
- [ ] Default values make sense - Default values won’t break Go parsing
4. Serialization Pathways
Check data flow in both directions: - [ ] Go → Yjs: waypoint.SyncToArray() writes data TS can read - [ ] Yjs → Go: parser.extractWaypoint() reads data TS writes - [ ] Type conversions safe: All interface{} type assertions in parser.go handle TS output correctly
Issue Classification
🔴 CRITICAL (Breaking)
- Missing field on one side that the other expects
- Type incompatibility that causes runtime errors
- Attribute name mismatch that breaks parsing
🟡 WARNING (Potential Issue)
- Optional vs required field mismatches
- Different default values
- Nullable handling differences
✅ ACCEPTED (From Audit Log/Annotations)
- User-confirmed acceptable differences
- Semantic matches with different naming
- Language-equivalent types
Reporting Format
Produce output like:
=== Contracts Audit Results ===
SCANNED TYPES:
- Waypoint (server/waypoint/type.go ↔ client/src/ffi/waypoint.ts ↔ client/src/ffi/Waypoint.res)
NEW ISSUES:
🔴 CRITICAL: <issue description>
Files: <file1>:<line> vs <file2>:<line>
Impact: <what breaks>
Suggested Fix: <specific code change>
🟡 WARNING: <issue description>
Details: <explanation>
ACCEPTED (from audit log/annotations):
✓ <difference> — <reason>
===
Do you want to:
1. Add any of these issues to the audit log as "not an issue"?
2. Fix the critical/warning issues now?
3. Just report and exit?
Important Notes
- ALWAYS read
.opencode/contracts-audit.mdfirst - Don’t report known acceptable differences - Respect inline annotations -
// contracts:ignore <reason>means user has reviewed and accepted - Language type equivalence is OK: Go
float32, TSnumber, Rescriptfloatare the same underlying type - Ask before writing to audit log - Only add entries after user confirmation
- This is Waypoint-only for now - Future shared types will be added as the system grows
Example Known Acceptable Differences
If you find these, check audit log - they may already be accepted:
Point representation:
- Go:
[]float32(slice) - TS:
[number, number](tuple) - Rescript:
(float, float)(tuple) - Status: ✅ ACCEPTABLE - Same underlying data, different language idioms
- Go:
Field naming:
- Go/Rescript:
cons - TS:
constraint - Status: Check audit log - may be intentional semantic match
- Go/Rescript:
Optional/nullable representation:
- Go: pointer or
omitempty - TS:
| nullunion type - Rescript:
option<...>variant - Status: ✅ ACCEPTABLE - Language-specific null handling
- Go: pointer or