.codex/skills/surface-parity/SKILL.md
Internal closed-loop skill: drive CLI/API/MCP to 100% shape parity (same service methods) AND behaviour parity (same response shapes). Uses ESLint rules tx/require-surface-parity + tx/interface-parity. NOT shipped to tx users.
npx skillsauth add jamesaphoenix/tx surface-parityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Internal skill (not shipped to users). Drives two dimensions of parity across all surfaces (CLI/API/MCP):
Checked by two ESLint rules:
tx/require-surface-parity — shape (service method coverage + variable naming)tx/interface-parity — behaviour (response shapes, serializer dedup, field types)STEP 1 → Audit both rules
STEP 2 → Fix naming violations (enables accurate shape detection)
STEP 3 → Fix shape gaps (implement missing service methods on surfaces)
STEP 4 → Fix behaviour gaps (align response shapes, use shared serializers)
STEP 5 → Re-audit → if clean, done; else goto STEP 2
# Shape parity: naming + method coverage
bunx eslint --no-cache apps/cli/src/commands/task.ts 2>&1 | grep 'require-surface-parity'
# Behaviour parity: response shapes + serializers
bunx eslint --no-cache apps/cli/src/commands/task.ts 2>&1 | grep 'interface-parity'
# Combined count (loop exit condition: 0)
bunx eslint --no-cache apps/cli/src/commands/task.ts 2>&1 | grep -c 'require-surface-parity\|interface-parity'
Naming violations look like:
23:11 warning Service binding "svc" should be "taskService' or 'taskSvc" (from TaskService).
Fix by renaming the variable AND all its usages within the same Effect.gen scope:
// BEFORE
const svc = yield* TaskService
yield* svc.create(input)
// AFTER
const taskService = yield* TaskService
yield* taskService.create(input)
Fix naming FIRST — the shape parser relies on consistent naming for accurate method attribution.
Shape gaps look like:
Surface parity gap: LearningService.count is missing from cli (implemented in: api, mcp)
For each gap, implement the missing operation on the missing surface. Follow the existing patterns in:
apps/cli/src/commands/<domain>.ts — Effect.gen + yield* service.method() + toJson()/console.log()apps/api-server/src/routes/<domain>.ts — .handle() + Effect.gen + serializeX() + mapCoreErrorapps/mcp-server/src/tools/<domain>.ts — runEffect(Effect.gen(...)) + JSON text content blocksBehaviour gaps look like:
Interface parity violation: done response missing required field "nowReady".
Interface parity violation: Local serializeTask() duplicates shared function.
Fixes:
{ task, nowReady })serializeTask(), import from @jamesaphoenix/tx-types.map(serializeTask) not .map(t => t.id)bunx eslint --no-cache apps/cli/src/commands/task.ts 2>&1 | grep -c 'require-surface-parity\|interface-parity'
If 0: done. If >0: go back to Step 2.
Shape gaps (19):
Behaviour gaps: run bunx eslint --no-cache apps/ 2>&1 | grep 'interface-parity' to check current count.
Shape rule in eslint.config.js:
'tx/require-surface-parity': ['warn', { ... }]
Behaviour rule in eslint.config.js:
'tx/interface-parity': ['error', { ... }]
development
Implement and verify design doc invariants by annotating tests and source code with [INV-*] / @spec tags, then driving tx spec coverage from BUILD toward HARDEN (100% FCI). Works with any design doc that has an invariants block.
data-ai
Link tasks to paired PRD/design specs, export all open work to markdown, and keep Ralph-style loops moving by creating tasks, subtasks, and dependency updates through tx primitives.
development
Refresh bundled tx Claude Code and Codex skills in a project from the canonical tx source without manual copy and paste.
development
Run Ralph against either the full repo queue or tasks linked to one design doc, with injected task/spec/queue context for Codex or Claude runtimes.