feat(contracts): API tab showing declared operations and what each stage serves #568

Merged
gmackie merged 2 commits from feat/contract-operations-tab into main 2026-09-14 06:03:33 +00:00
Owner

What

Phase 2b: read the ingested contract back. The tab is sourced only from the compiled contract, so it cannot drift from the code that produced it — which is the whole point of Phase 1.

Stacked on #567 (which is stacked on #564). Base is feat/contract-ingest, so this diff is only the read side.

Plan: https://7n04n7hhuesf.postplan.dev — Phase 2, tasks 2.5–2.6.

The stage join is the commit

contract.forApp returns the newest snapshot, its operations grouped for display, and one row per stage saying which contract that stage is actually serving. The join key is the commit, not a foreign key: a contract is a fact about a revision, and a deployment records the revision it runs, so the two meet without either side knowing about the other.

That gives three states per stage, and the middle one is the interesting one:

state meaning
current serving the newest published contract
behind serving an older contract — the API in prod is not the API in main
no contract serving a commit that never published one

forApp returns latest: null rather than throwing when nothing has published. An app with no Effect contract is normal, not an error.

UI

A new "API" tab listing operations by group with method, route, visibility, resolved authentication, p99 target and status codes. It flags an operation whose declared authentication its middleware does not back, which is the drift Phase 1's compiler detects.

It is a lazy tab, so app-detail-tabs.behavior.test.tsx gains its mock — without that the existing test breaks.

Verification

22 new tests. The projection helpers (groupOperations, resolveStageContracts) are exported and tested directly rather than through a mocked database; grouping sorts rather than trusting read order, because Postgres does not promise one. Typecheck clean across api and web.

🤖 Generated with Claude Code

## What Phase 2b: read the ingested contract back. The tab is sourced **only** from the compiled contract, so it cannot drift from the code that produced it — which is the whole point of Phase 1. **Stacked on #567** (which is stacked on #564). Base is `feat/contract-ingest`, so this diff is only the read side. Plan: https://7n04n7hhuesf.postplan.dev — Phase 2, tasks 2.5–2.6. ## The stage join is the commit `contract.forApp` returns the newest snapshot, its operations grouped for display, and one row per stage saying which contract that stage is *actually serving*. The join key is the commit, not a foreign key: a contract is a fact about a revision, and a deployment records the revision it runs, so the two meet without either side knowing about the other. That gives three states per stage, and the middle one is the interesting one: | state | meaning | |---|---| | `current` | serving the newest published contract | | `behind` | serving an older contract — the API in prod is not the API in main | | `no contract` | serving a commit that never published one | `forApp` returns `latest: null` rather than throwing when nothing has published. An app with no Effect contract is normal, not an error. ## UI A new "API" tab listing operations by group with method, route, visibility, resolved authentication, p99 target and status codes. It flags an operation whose declared authentication its middleware does not back, which is the drift Phase 1's compiler detects. It is a lazy tab, so `app-detail-tabs.behavior.test.tsx` gains its mock — without that the existing test breaks. ## Verification 22 new tests. The projection helpers (`groupOperations`, `resolveStageContracts`) are exported and tested directly rather than through a mocked database; grouping sorts rather than trusting read order, because Postgres does not promise one. Typecheck clean across api and web. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(contracts): API tab showing declared operations and what each stage serves
Some checks failed
CI / gitleaks (pull_request) Successful in 6s
CI / storybook (pull_request) Successful in 2m3s
CI / ci (pull_request) Has been cancelled
271e1d07a6
Phase 2b: read the ingested contract back. The tab is sourced only from the
compiled contract, so it cannot drift from the code that produced it.

- packages/api: `contract.forApp` returns the newest snapshot, its operations
  grouped for display, and one row per stage saying which contract that stage
  is actually serving. The stage join is the COMMIT, not a foreign key: a
  contract is a fact about a revision and a deployment records the revision
  it runs, so the two meet without either side knowing about the other. A
  stage can therefore come back `behind` (serving an older contract) or
  `no contract` (serving a commit that never published one).
  `contract.operationHistory` returns one operation across snapshots, newest
  first, so a reader can see when its shape last moved.
- `forApp` returns `latest: null` rather than throwing when nothing has
  published — an app with no Effect contract is normal, not an error.
- apps/web: an "API" tab listing operations by group with method, route,
  visibility, resolved authentication, p99 target and status codes, and
  flagging an authentication its middleware does not back. It is a new lazy
  tab, so app-detail-tabs.behavior.test.tsx gains its mock.

The projection helpers (groupOperations, resolveStageContracts) are exported
and tested directly; Postgres does not promise row order, so grouping sorts
rather than trusting the read.

22 new tests. Stacked on feat/contract-ingest.

Plan: https://7n04n7hhuesf.postplan.dev (Phase 2, tasks 2.5-2.6)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gmackie force-pushed feat/contract-operations-tab from 271e1d07a6
Some checks failed
CI / gitleaks (pull_request) Successful in 6s
CI / storybook (pull_request) Successful in 2m3s
CI / ci (pull_request) Has been cancelled
to 38660c898f
Some checks failed
CI / gitleaks (pull_request) Successful in 10s
CI / storybook (pull_request) Successful in 1m46s
forgegraph/ci CI failed
CI / ci (pull_request) Failing after 6m58s
2026-09-09 23:53:40 +00:00
Compare
gmackie force-pushed feat/contract-operations-tab from 38660c898f
Some checks failed
CI / gitleaks (pull_request) Successful in 10s
CI / storybook (pull_request) Successful in 1m46s
forgegraph/ci CI failed
CI / ci (pull_request) Failing after 6m58s
to d17f328394
All checks were successful
CI / gitleaks (pull_request) Successful in 7s
CI / storybook (pull_request) Successful in 2m6s
forgegraph/ci CI passed
CI / ci (pull_request) Successful in 9m42s
2026-09-10 01:14:55 +00:00
Compare
gmackie changed target branch from feat/contract-ingest to main 2026-09-14 02:19:32 +00:00
merge: bring main into feat/contract-operations-tab
All checks were successful
CI / gitleaks (pull_request) Successful in 8s
CI / storybook (pull_request) Successful in 1m37s
forgegraph/ci CI passed
CI / ci (pull_request) Successful in 10m24s
d543851680
The branch's last CI ran on 2026-09-10, before #564, #567, #571, #574, #575
and #565 landed, and that run never built the web app. Merge rather than
rebase so the push is a fast-forward; there were no conflicts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gmackie/ForgeGraph!568
No description provided.