Skip to content

Reviewer Phase 1: extract Rails OpenAPI surface#46

Merged
dadachi merged 1 commit intomainfrom
reviewer-phase1-openapi-extract
May 2, 2026
Merged

Reviewer Phase 1: extract Rails OpenAPI surface#46
dadachi merged 1 commit intomainfrom
reviewer-phase1-openapi-extract

Conversation

@dadachi
Copy link
Copy Markdown
Contributor

@dadachi dadachi commented May 2, 2026

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)

  • 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 a well-formed ReviewerResult — no exceptions escape. Rename accidentally deleting the openapi.yaml is 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:

contractParity: pass
diffs:
  rails:openapi=3.1.0
  rails:title=VetClinicQueue API
  rails:paths=23
  rails:schemas=35

Test plan

  • npm run ci — 17/17 green.
  • Stub-mode reviewer passes without disk access (new test).
  • Existing dispatch e2e still passes (real-mode reviewer reads renamed out/<slug>/rails/docs/openapi.yaml if it exists, else falls through to fail-with-reason — but workers in stub mode set outDir: ./out/<slug>/rails which exists from prior runs in dev, so behavior depends on local state. CI uses NATIVEAPPTEMPLATE_STUB_ALL=1, so this is fine.)
  • After merge: confirm via real npm run dev that the trace surfaces sensible counts for new specs.

Out of scope (Phase 2+)

  • Parse iOS Swift networking code for endpoint shapes.
  • Parse Android Kotlin Retrofit interfaces.
  • Three-way diff against Rails surface.
  • Real YAML parser for structural inspection.

🤖 Generated with Claude Code

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>
@dadachi dadachi merged commit 08f323b into main May 2, 2026
1 check passed
@dadachi dadachi deleted the reviewer-phase1-openapi-extract branch May 2, 2026 23:13
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant