Reviewer Phase 1: extract Rails OpenAPI surface#46
Merged
Conversation
The reviewer sub-agent was a pure stub — emitted three trace lines
and returned `{contractParity: "pass", diffs: []}` without touching
disk. PR replaces that with a real Rails OpenAPI extraction step
that confirms the rename pipeline left the spec parseable and
surfaces structure metadata for downstream phases.
Phase 1 scope (Rails-only):
- Read out/<slug>/rails/docs/openapi.yaml from worker outDir
- Validate top-level shape via regex (openapi: <version>, info,
paths, components/schemas blocks)
- Count paths + schemas
- Surface { openapiVersion, title, pathCount, schemaCount } in
diffs[] for trace/judge consumption
- On read/parse failure: contractParity = "fail" with reason
Failure modes return well-formed ReviewerResult — no exceptions
escape (rename pipeline accidentally deleting the openapi.yaml is
a real failure mode the agent should recover from cleanly).
YAML parsing is regex-based for now to avoid pulling in a YAML
dep before we need full structure. Phase 2+ will swap to a real
parser when iOS Swift / Android Kotlin extraction lands and
three-way diff begins.
Stub mode (NATIVEAPPTEMPLATE_STUB_REVIEWER=1) preserves the
zero-disk-access placeholder behavior — important for tests and
fast offline iteration.
Real-mode smoke against out/vet-clinic-queue/rails:
contractParity: pass
diffs:
rails:openapi=3.1.0
rails:title=VetClinicQueue API
rails:paths=23
rails:schemas=35
Tests: 17/17 npm run ci green.
- Stub-mode reviewer passes without disk access ✓
- Existing dispatch e2e test still passes ✓
Out of scope (Phase 2+):
- Parse iOS Swift networking code for endpoint shapes
- Parse Android Kotlin Retrofit interfaces
- Three-way diff against Rails surface
- Add a real YAML parser dependency for structural inspection
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
dadachi
added a commit
that referenced
this pull request
May 2, 2026
Extends reviewer beyond Phase 1's Rails-only OpenAPI surface (#46) to extract API endpoints from all three platforms. Phase 3 will diff them; this PR proves rename left every platform's network layer parseable and produces the contract objects. New module: src/agents/contract-extract.ts extractRailsContract(railsDir) - Parse out/<slug>/rails/docs/openapi.yaml via the new `yaml` dep - Return {openapiVersion, title, endpoints, schemaCount} - Endpoint = {method: GET|POST|PUT|PATCH|DELETE, path: string} extractAndroidEndpoints(androidDir) - Walk app/src/main/kotlin/**/data/**/*Api.kt - Regex @method("path") Retrofit annotations - Return Endpoint[] extractIosEndpoints(iosDir) - Walk app's *Request.swift files (under Networking/, Login/, etc.) - Split each file on `struct ... {` boundaries - Within each struct, pair `var method: HTTPMethod { .METHOD }` with `var path: String { "..." }` - Return Endpoint[] Reviewer surfaces counts in trace + diffs[]: rails:openapi=3.1.0 rails:title=VetClinicQueue API rails:endpoints=42 rails:schemas=35 ios:endpoints=24 android:endpoints=23 Pass/fail policy stays "PASS unless extraction fails" for now — count mismatches don't fail the run because iOS/Android legitimately implement a Rails subset (admin endpoints, server-only namespaces). Phase 3 adds path-normalized drift detection with proper coupling to overallPass. Adds `yaml` (^2.8.4) as a runtime dep — first non-Anthropic-SDK runtime dep in the agent. Standard, no transitive deps, MIT. Tests: 17/17 npm run ci green. Real-mode smoke against out/vet-clinic-queue/ confirmed all three extractors land sensible numbers. Out of scope (Phase 3+): - Path normalization (strip {accountId} prefix, normalize {*} placeholders, handle Rails server base url) - Three-way diff (rails-only / ios-only / android-only / mismatch) - Wire reviewer FAIL into JudgeResult.overallPass Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The reviewer sub-agent was a pure stub — emitted three trace lines and returned
{contractParity: "pass", diffs: []}without touching disk. This PR replaces that with a real Rails OpenAPI extraction step that confirms the rename pipeline left the spec parseable and surfaces structure metadata for downstream phases.Phase 1 scope (Rails-only)
out/<slug>/rails/docs/openapi.yamlfrom worker outDir.openapi: <version>,info,paths,components/schemasblocks).{ openapiVersion, title, pathCount, schemaCount }indiffs[]for trace/judge consumption.contractParity = "fail"with reason.Failure modes return a well-formed
ReviewerResult— no exceptions escape. Rename accidentally deleting theopenapi.yamlis a real failure mode the agent should recover from cleanly.Why regex over a YAML parser
Avoiding a new dep until we need full structural inspection. Phase 2+ will swap to a real YAML parser when iOS Swift / Android Kotlin extraction lands and three-way diffing begins.
Stub mode
NATIVEAPPTEMPLATE_STUB_REVIEWER=1(or_STUB_ALL=1) preserves the zero-disk-access placeholder behavior — important for tests and fast offline iteration.Real-mode smoke
Against the existing
out/vet-clinic-queue/rails:Test plan
npm run ci— 17/17 green.out/<slug>/rails/docs/openapi.yamlif it exists, else falls through to fail-with-reason — but workers in stub mode setoutDir: ./out/<slug>/railswhich exists from prior runs in dev, so behavior depends on local state. CI usesNATIVEAPPTEMPLATE_STUB_ALL=1, so this is fine.)npm run devthat the trace surfaces sensible counts for new specs.Out of scope (Phase 2+)
🤖 Generated with Claude Code