feat(cli): fg contract publish — send an app's compiled contract to ForgeGraph #569

Merged
gmackie merged 2 commits from feat/contract-cli into main 2026-09-14 06:13:58 +00:00
Owner

What

Closes the Phase 2 loop. CI generates the contract from the app's Effect HttpApi, this ships it, and the server records it and satisfies the api_contract gate.

Stacked on #568 → #567 → #564. Merge bottom-up.

Plan: https://7n04n7hhuesf.postplan.dev — Phase 2, task 2.4.

In CI it is one line

fg contract publish

Everything defaults from the repo:

input default
file sdks/contract/contract.json, then contract.json
app .forgegraph.yaml (honouring sub_app)
commit the working copy's commit, via jj or git
changeset the current VCS change

--no-gate publishes without attaching to a changeset, for a branch build that should record the contract without moving a gate.

Failing locally beats failing at the server

The command checks the file exists and is JSON before sending. A missing or half-written generator output fails with a message naming the command to run, instead of coming back as a 400 from the API.

Verification

go test ./cmd/fg/commands/ passes (full package), go vet clean, gofmt clean. New tests cover both path defaults (the generator's path wins over a stale root copy), the actionable error text, flag-over-config precedence, sub_app outranking app, and short() not padding or panicking on a short sha.

It reuses the package's existing chdir helper rather than redeclaring it — the first draft did, and the package failed to build.

Follow-up

Wiring this into create-gmacko-app's sdk.yml belongs with that repo's PR #5; its CI is currently blocked on runner capacity, unrelated to this change.

🤖 Generated with Claude Code

## What Closes the Phase 2 loop. CI generates the contract from the app's Effect `HttpApi`, this ships it, and the server records it and satisfies the `api_contract` gate. **Stacked on #568 → #567 → #564.** Merge bottom-up. Plan: https://7n04n7hhuesf.postplan.dev — Phase 2, task 2.4. ## In CI it is one line ```sh fg contract publish ``` Everything defaults from the repo: | input | default | |---|---| | file | `sdks/contract/contract.json`, then `contract.json` | | app | `.forgegraph.yaml` (honouring `sub_app`) | | commit | the working copy's commit, via jj or git | | changeset | the current VCS change | `--no-gate` publishes without attaching to a changeset, for a branch build that should record the contract without moving a gate. ## Failing locally beats failing at the server The command checks the file exists and is JSON before sending. A missing or half-written generator output fails with a message naming the command to run, instead of coming back as a 400 from the API. ## Verification `go test ./cmd/fg/commands/` passes (full package), `go vet` clean, `gofmt` clean. New tests cover both path defaults (the generator's path wins over a stale root copy), the actionable error text, flag-over-config precedence, `sub_app` outranking `app`, and `short()` not padding or panicking on a short sha. It reuses the package's existing `chdir` helper rather than redeclaring it — the first draft did, and the package failed to build. ## Follow-up Wiring this into create-gmacko-app's `sdk.yml` belongs with that repo's PR #5; its CI is currently blocked on runner capacity, unrelated to this change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(cli): fg contract publish — send an app's compiled contract to ForgeGraph
Some checks failed
CI / gitleaks (pull_request) Successful in 7s
CI / ci (pull_request) Failing after 1s
CI / storybook (pull_request) Successful in 1m57s
611ae1ad0a
Closes the Phase 2 loop: CI generates the contract from the app's Effect
HttpApi, this ships it, and the server records it and satisfies the
api_contract gate.

Everything defaults from the repo, so in CI the call is just
`fg contract publish`: the file from sdks/contract/contract.json (falling
back to contract.json), the app from .forgegraph.yaml (honouring sub_app),
the commit from the working copy via jj or git, and the changeset from the
current VCS change. `--no-gate` publishes without attaching to a changeset.

The command validates the file is JSON before sending, so a missing or
half-written generator output fails locally with a message naming the
command to run rather than as a server-side 400.

`fg contract show` reads one back.

Tests cover the two path defaults (the generator's path wins over a stale
root copy), the actionable error text, flag-over-config precedence, sub_app
outranking app, and short() not padding or panicking on a short sha. Reuses
the package's existing chdir helper rather than redeclaring it.

Plan: https://7n04n7hhuesf.postplan.dev (Phase 2, task 2.4)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gmackie force-pushed feat/contract-cli from 611ae1ad0a
Some checks failed
CI / gitleaks (pull_request) Successful in 7s
CI / ci (pull_request) Failing after 1s
CI / storybook (pull_request) Successful in 1m57s
to 4f2fad00cd
Some checks failed
CI / gitleaks (pull_request) Successful in 7s
CI / storybook (pull_request) Successful in 1m45s
forgegraph/ci CI failed
CI / ci (pull_request) Failing after 10m15s
2026-09-10 01:25:31 +00:00
Compare
gmackie force-pushed feat/contract-cli from 4f2fad00cd
Some checks failed
CI / gitleaks (pull_request) Successful in 7s
CI / storybook (pull_request) Successful in 1m45s
forgegraph/ci CI failed
CI / ci (pull_request) Failing after 10m15s
to 30acfb3d59
All checks were successful
CI / gitleaks (pull_request) Successful in 7s
CI / storybook (pull_request) Successful in 1m50s
forgegraph/ci CI passed
CI / ci (pull_request) Successful in 9m38s
2026-09-10 01:38:39 +00:00
Compare
merge: bring #568's branch (with main) into feat/contract-cli
All checks were successful
CI / gitleaks (pull_request) Successful in 9s
CI / storybook (pull_request) Successful in 2m10s
forgegraph/ci CI passed
CI / ci (pull_request) Successful in 10m21s
3bf905b3c7
#569 is stacked on #568. #568's branch now includes main up to #565, so
merging it here brings #569 current too, and once #568 lands this tip only
needs its base retargeted to main. No conflicts; `go vet` and `go test` for
cmd/fg and internal/deploy pass on the merged tree with HOME isolated and
FG_* cleared.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gmackie changed target branch from feat/contract-operations-tab to main 2026-09-14 06:03:56 +00:00
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!569
No description provided.