Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ A running server reports its version at `/health`.

## [Unreleased]

### Documentation

- Refresh the README for v0.4.6: SimBrief sign-in is on demand (no daily keep-alive), approval
gates generation while FlightAware research can be billed before approval, numbered
revisions are separately approved and retained, client journeys remain unverified, and
quality evidence reflects the current workflow suite and release acceptance.
- Update the agent, SimBrief-tools and deployment guides to describe the deployed remote MCP
server, the generation-free stdio tool server and the legacy cron's current behavior.

## [0.4.6] - 2026-11-12

### Fixed
Expand Down
85 changes: 59 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@
Tell it the flight you want to fly, like *"Dulles to Denver tomorrow evening in the A321neo"*.
It finds a real airline flight, generates your SimBrief plan once you approve, and hands back a
briefing that starts with where to spawn and the takeoff N1 to set. It runs on your own keys and
your own ChatGPT plan, from the terminal or any MCP client.
your own ChatGPT plan, from the terminal or an MCP client connected to your own server.

[Get started](#get-started) · [Agent guide](docs/implementation/agent-usage.md) ·
[Self-host on Cloudflare](docs/implementation/deployment.md) ·
[Aircraft coverage](docs/aircraft-coverage.md) · [Changelog](CHANGELOG.md)

> **Early release.** It works end to end locally and on the self-hosted Cloudflare
> deployment. See the [changelog](CHANGELOG.md) for the current version. Thrust targets are simulation-grade estimates. For flight simulation only: never
> use any output of this project for real-world flight planning or aircraft operation. See
> [status and limits](#status-and-limits).
> **Early release, self-hosted, single owner.** It works end to end locally and on the
> self-hosted Cloudflare deployment; the current release is listed in the
> [changelog](CHANGELOG.md). Thrust targets are simulation-grade estimates, and no aircraft is
> verified yet. For flight simulation only: never use any output of this project for
> real-world flight planning or aircraft operation. See [status and limits](#status-and-limits).

## From flight idea to briefing

Expand All @@ -32,17 +33,23 @@ where to spawn and what thrust to set.
[FlightAware AeroAPI](https://www.flightaware.com/commercial/aeroapi/) and never invents a
flight number. If today's gate isn't assigned yet, it gives the gate from the most recent
operation and labels it historical.
- **Approve before anything happens.** You see the exact flight, airports, departure time,
connection, aircraft and thrust status before anything is generated or spent.
- **Approve before anything is generated.** You see the exact flight, airports, departure
time, connection, aircraft and thrust status before a SimBrief plan is generated or a cloud
browser is opened. Flight research happens before approval, so FlightAware AeroAPI requests
(billed to your key) can occur while it looks for options.
- **Get your SimBrief plan generated.** After you approve, it fills SimBrief's dispatch form
in a cloud browser, reads every field back, and generates exactly one plan per leg.
in a cloud browser, reads every field back, and clicks Generate once for that approved
revision. It never repeats a Generate whose outcome is uncertain.
- **Revise without losing earlier plans.** A change to a saved leg becomes a new numbered
revision with its own approval, generation and briefing; earlier revisions are kept. When
you ask for several numbered revisions, they are completed in order.
- **Know your takeoff thrust.** N1 targets come from SimBrief's own takeoff solution and the
lapse shape of FlyByWire's open A32NX thrust model, each with a range. Aircraft it can't
model get a clear "unavailable" instead of a guess.
- **Plan whole trips.** Each leg of a multi-leg trip is dispatched and kept separately, and
must leave at least 45 minutes after the previous leg lands.
- **Use it where you already chat.** Use the terminal chat, or connect ChatGPT, Claude or
Codex to the MCP server.
- **Use it where you already chat.** Use the terminal chat, or connect an MCP client such as
ChatGPT, Claude or Codex to your self-hosted server (per-client acceptance is still pending).

## What you get

Expand Down Expand Up @@ -97,16 +104,22 @@ setting, resuming conversations, and MCP client setup.
| Path | Best for | Start |
|---|---|---|
| Terminal chat | Trying it out on one machine | `uv run ifd-chat` |
| Local MCP server | Claude Desktop, Codex or other MCP clients on your machine | [MCP setup](docs/implementation/simbrief-usage.md) |
| Self-hosted remote MCP | ChatGPT or your phone, behind Cloudflare Access | [Cloudflare deployment](docs/implementation/deployment.md) |
| Self-hosted remote MCP | The full agent from an MCP client, behind Cloudflare Access | [Cloudflare deployment](docs/implementation/deployment.md) |
| Local SimBrief tools (stdio) | Retrieving, reading and estimating from existing SimBrief plans; no generation | [SimBrief tools](docs/implementation/simbrief-usage.md) |

The remote MCP server exposes `plan_flight`, `approve_dispatch`, `turn_status`,
`conversation_status`, `list_saved_trips` and `browser_status`
([tool reference](docs/implementation/agent-usage.md#deployed-mode-self-hosted-single-user)).
It is built for ChatGPT, Claude and Codex as MCP clients; each client's own end-to-end journey
has not yet passed acceptance, so treat client compatibility as unverified until it does.

| Component | Local | Self-hosted on Cloudflare |
|---|---|---|
| Agent and MCP server | Your machine | A Cloudflare Worker and container, signed in through Cloudflare Access |
| Conversations and saved plans | SQLite in `data/` | Neon Postgres |
| Plan screenshots | `data/screens/` | Cloudflare R2 |
| Secrets | `.env` | Doppler, synced to Worker secrets on release |
| SimBrief login | Saved Browserbase profile | The same, refreshed by a daily keep-alive |
| SimBrief login | Saved Browserbase profile | The same; signs in again on demand when a session finds it signed out |

The self-hosted deployment is built for one owner. It is not a shared or resold service.

Expand All @@ -131,19 +144,28 @@ lands. See the [agent guide](docs/implementation/agent-usage.md) and the

## Quality

- **Evaluations.** The agent is checked against
[100 scripted first messages](evals/dispatch_prompts.jsonl) in 11 categories, including
vague requests, multi-leg trips, private flights and unsupported aircraft. On October 31,
2026, two consecutive full runs passed every safety check and fully passed 92 and 91 of 100
cases. The safety checks are: no invented flight numbers, no assumed dates, correct airports,
and unsupported thrust disclosed before approval
- **Workflow evaluations.** 28 whole-conversation scenarios drive the production MCP tools
over local HTTP with controlled FlightAware, SimBrief and browser services: planning,
routing, provider failures, approved revisions and recovery after a restart. The original
eight regression cases stay unchanged alongside them. Controlled-service results, matched
replays on the configured model and live-provider runs are reported separately
([evaluation guide](evals/README.md), [v0.4.6 release record](docs/evidence/v046-release.md)).
- **Fixes come from traces.** For example, WF-025 found that numbered revisions were treated
as alternatives; v0.4.6 completes them in order, each separately approved
([fix report](docs/evidence/wf025-revision-sequence.md)).
- **First-message checks.** [100 scripted first messages](evals/dispatch_prompts.jsonl) in 11
categories cover vague requests, multi-leg trips, private flights and unsupported aircraft.
The last full runs (October 31, 2026) passed every safety check
([results](docs/implementation/agent-usage.md#full-100-case-runs--october-31-2026)).
- **No FlightAware fees for routine runs.** Evaluations replay synthetic or recorded FlightAware
responses. Live provider checks are opt-in and capped at $1 per batch by default
([how to run them](evals/README.md)).
- **Live acceptance.** The Product validation check passes only while a recorded live run on
the deployed code shows generation through MCP, a real neo plan, two legs kept separately,
and an approval wait and resume ([evidence](docs/evidence/live-acceptance.json)).
- **Live acceptance per release.** Each release candidate's exact image runs a two-leg trip
(KIAD→KEWR, then KEWR→KIAD) on a temporary staging Worker, with a separate approval per leg,
before production approval. For v0.4.6 each leg generated once, the two browser sessions
used 151 seconds in total, and no browser was left running
([release record](docs/evidence/v046-release.md), [acceptance data](docs/evidence/live-acceptance.json)).
These are single runs, not performance statistics.

## Releases

Expand All @@ -155,9 +177,15 @@ Worker version with the same name. A running server reports its version at `/hea
## Status and limits

- **Early, personal-use software.** It works end to end locally and on the self-hosted
Cloudflare deployment, which planned its first live flights on November 1, 2026.
Cloudflare deployment, which planned its first live flights on November 1, 2026. Each
deployment is built for one owner and is not a shared service.
- **One cloud browser at a time.** A deployment runs one browser workflow at a time, with up to
two waiting for 30 seconds, and each browser session ends within 180 seconds.
- **Costs are yours.** FlightAware AeroAPI, Browserbase and ChatGPT plan usage are billed to
your own accounts. FlightAware research can happen before you approve a dispatch.
- **Thrust targets are simulation-grade estimates**, shown with a range. No aircraft is yet
verified against reference data ([aircraft coverage](docs/aircraft-coverage.md)). The model
verified against reference data: 0 of the 51 current Infinite Flight aircraft meet the
verified-support criteria ([aircraft coverage](docs/aircraft-coverage.md)). The model
covers pressure altitudes from -2,000 to 16,600 ft, FlyByWire's full takeoff table. Above
2,000 ft, such as at Denver, the engine's behavior is extrapolated, so the range is wider and
large FLEX reductions there may be flagged as suspect.
Expand All @@ -177,8 +205,13 @@ uv run ruff check src tests
uv run pytest -q
```

Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md). The
[roadmap](docs/roadmap.md) and [backlog](docs/backlog.md) show what's next.
Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).

## What's next

v0.5.0 adds durable trip progress and safe cancellation, more dispatch inputs such as cruise
altitude and alternates, sourced private-flight configurations, and measured aircraft expansion.
The [roadmap](docs/roadmap.md) and [backlog](docs/backlog.md) track it.

## License

Expand Down
4 changes: 2 additions & 2 deletions docs/implementation/agent-usage.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dispatcher agent: setup, use, and evaluation

Status October 31, 2026: local Deep Agent running on GPT-6 Luna through Sign in with ChatGPT. SimBrief plans are generated by filling the dispatch form in a logged-in Browserbase cloud browser (first live generation succeeded October 31). Not deployed.
Status November 12, 2026 (v0.4.6): the Deep Agent runs on GPT-6 Luna through Sign in with ChatGPT, locally (`ifd-chat`) and as a self-hosted remote MCP server on Cloudflare behind Cloudflare Access ([deployed mode](#deployed-mode-self-hosted-single-user), [deployment](deployment.md)). SimBrief plans are generated by filling the dispatch form in a logged-in Browserbase cloud browser after exact dispatch approval. Sections below are dated; the evaluation tables record the October 2026 runs, and current release evidence is in [`docs/evidence/`](../evidence/). Per-client ChatGPT, Claude and Codex journeys are not yet acceptance-verified.

## Shape

Expand Down Expand Up @@ -174,7 +174,7 @@ The first lists active provider sessions and admission limits. `cleanup` recheck
release only for dispatcher-owned sessions at least 180 seconds old; unrelated and younger
sessions are left alone. `browser_status` provides the read-only inventory over remote MCP.
The old daily keepalive endpoint returns `skipped`; normal browser work still signs in when
needed. The deployed v0.4.1 behavior changes only after an authorized deployment.
needed.

Usage telemetry includes queue wait, elapsed session time, release-request versus confirmed
release, cleanup failures, deadline/idle outcomes, and an explicitly labeled minimum-adjusted
Expand Down
2 changes: 1 addition & 1 deletion docs/implementation/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ Cloudflare Access does the OAuth for MCP clients; the Worker does not implement
## Workflows

- `ci.yml`: pull requests, pushes to `main`, and release checks. Python: `uv sync --locked`, pytest, ruff, dead-code check. Worker: `npm ci`, `wrangler types --check`, `tsc --noEmit`, auth-gate unit tests (`node --test`).
- `release.yml` (manual): refuses anything but the current `main` commit, runs CI on it, syncs secrets from Doppler into a temporary file, then `wrangler versions upload --secrets-file …` builds the image with Docker and uploads a version with no traffic. The version id is in the job summary, and each job uploads a `release-*` evidence artifact (Wrangler output, `wrangler versions view` of the candidate, deployment status before and after, commit and version id). With `promote=true`, the `production` environment approval gates `wrangler versions deploy <id>@100% -y`, followed by `wrangler triggers deploy` to apply the daily SimBrief keep-alive cron (version uploads do not change triggers). The release takes its name from the version in `pyproject.toml`: the guard job requires a matching `CHANGELOG.md` section and refuses a version already tagged on another commit. The uploaded Worker version is tagged `vX.Y.Z`, and promotion publishes the `vX.Y.Z` Git tag and GitHub Release with that changelog section as its notes.
- `release.yml` (manual): refuses anything but the current `main` commit, runs CI on it, syncs secrets from Doppler into a temporary file, then `wrangler versions upload --secrets-file …` builds the image with Docker and uploads a version with no traffic. The version id is in the job summary, and each job uploads a `release-*` evidence artifact (Wrangler output, `wrangler versions view` of the candidate, deployment status before and after, commit and version id). With `promote=true`, the `production` environment approval gates `wrangler versions deploy <id>@100% -y`, followed by `wrangler triggers deploy` to apply the daily cron (version uploads do not change triggers). The cron still calls the container's legacy keep-alive route, which now returns `skipped` and opens no browser; SimBrief sign-in happens on demand during browser work. The release takes its name from the version in `pyproject.toml`: the guard job requires a matching `CHANGELOG.md` section and refuses a version already tagged on another commit. The uploaded Worker version is tagged `vX.Y.Z`, and promotion publishes the `vX.Y.Z` Git tag and GitHub Release with that changelog section as its notes.
- `emergency-stop.yml` (manual, type `STOP`): sets `IFD_PAID_OPERATIONS_ENABLED=false` with `wrangler secret put`.

`scripts/sync-secrets.sh` can also be run locally (`DOPPLER_PROJECT=… DOPPLER_CONFIG=… scripts/sync-secrets.sh`). Without arguments it is a dry run that checks the required keys and lists the names it would sync; `--apply` pushes them with `wrangler secret bulk`, which deploys a new version of the current code immediately. It prints key names only.
Expand Down
8 changes: 3 additions & 5 deletions docs/implementation/simbrief-usage.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Local SimBrief MCP usage

Status: preparation, explicit retrieval, and immutable local snapshots implemented. Generation and the complete BUILD-04 acceptance remain blocked on developer access and live evidence. No Deep Agents coordinator, remote hosting, or automatic Infinite Flight import is implemented.
Scope (updated November 12, 2026): this page covers the **local stdio tool server** (`ifdispatcher`), which prepares SimBrief requests, retrieves existing plans into immutable local snapshots and estimates takeoff N1. It does not generate plans. Plan generation, the dispatcher agent and the remote MCP server are documented in the [agent guide](agent-usage.md) and [deployment guide](deployment.md); BUILD-04 live acceptance passes on the current release ([evidence](../evidence/live-acceptance.json)). Automatic import into Infinite Flight is not implemented.

## Install and start

Expand Down Expand Up @@ -66,7 +66,7 @@ This saves the agreed request locally; it does not generate a flight.
- Changed request or OFP bytes require a new revision. Existing snapshots are never overwritten. Even an identical plan fetched again may have different provider metadata and require a new revision.
- A missing-input result creates no complete request. Prepare again with the missing fields.
- Network/provider errors leave saved snapshots intact. Explicitly retry retrieval only when requested; there is no polling loop.
- Authentication/generation waiting and timeout reconciliation are not implemented because the provider contract is unavailable. No submission tool is exposed, and there is no fake success path.
- This stdio server exposes no generation tool. Browser generation, its waiting and uncertain-outcome handling belong to the dispatcher agent ([agent guide](agent-usage.md#bounded-browser-management)).
- Thrust output is an estimate only: displayed N1, not proof of equal real or simulator thrust. A provider-reported target would remain unverified.

## Verification
Expand All @@ -77,6 +77,4 @@ bash scripts/check-dead-code.sh
uv run --locked python scripts/validate.py
```

The validator executes local tests, lint, and pinned Vulture. It writes a timestamped source digest, check output, and result under `artifacts/validation/` on every run. Its current overall outcome is intentionally blocked: live generation/neo/two-leg/authentication acceptance and exact-commit evidence are missing. The live validator must be completed alongside generation; this scaffold is not a finished release gate.

The product CI workflow checks out the actual PR head and uploads evidence even on failure. It has not run on GitHub. Commits/pushes are not authorized; candidate evidence therefore identifies the local working tree. MCP SDK 1.27.0 is pinned to the inspected v1 interface matching the upstream reuse candidate; SDK 2.3.0 exists but has a changed API. All resolved runtime/development dependencies are locked in uv.lock.
The validator executes local tests, lint and pinned Vulture, and writes a timestamped source digest, check output and result under `artifacts/validation/`. Its overall outcome also requires the database-derived live acceptance record ([`live-acceptance.json`](../evidence/live-acceptance.json)) to match the source being validated. The Product validation workflow runs it in CI on every pull request; see [CONTRIBUTING.md](../../CONTRIBUTING.md). MCP SDK 1.27.0 is pinned; all resolved runtime and development dependencies are locked in `uv.lock`.
Loading