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
2 changes: 1 addition & 1 deletion .github/workflows/release-pypi.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Publish sdist + wheel to PyPI when a SemVer tag is pushed (e.g. v1.1.1).
# Publish sdist + wheel to PyPI when a SemVer tag is pushed (e.g. v1.1.2).
# Configure "trusted publishing" on PyPI for this workflow + repository + optional GitHub environment.
# https://docs.pypi.org/trusted-publishers/

Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ All notable changes to FlightDeck will be documented in this file.

This project follows [Semantic Versioning](https://semver.org/). From **v1.0.0**, documented CLI behavior (**[README.md](https://github.com/flightdeckdev/flightdeck/blob/main/README.md)** on the canonical **`main`** branch), committed **`schemas/v1/`**, and **`POST /v1/events`** payloads with **`api_version` `v1`** are treated as stable public contracts unless a release notes a semver-major bump.

## Unreleased
## 1.1.2 - 2026-05-03

### Added

- **`trace_id` filter** on `GET /v1/runs`, `flightdeck runs list --trace-id`, and SDK `list_runs(trace_id=…)` — exact match on ingested `RunEvent.request.trace_id`.
- **`flightdeck runs export`** — write the same filtered run-event slice as **`runs list`** as **JSONL** (optional **`-o`** file; default stdout); **`--limit`** defaults to **500** (max **500**); prints a **stderr** warning when results are truncated.

### Changed

- **Examples / CI snippets:** **`flightdeck-ai>=1.1.2`** where version pins apply.

## 1.1.1 - 2026-05-02

Expand Down
2 changes: 1 addition & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Merging to **`main` does not publish packages** — PyPI uploads are **tag-drive
1. **PyPI:** add a **trusted publisher** for **[github.com/flightdeckdev/flightdeck](https://github.com/flightdeckdev/flightdeck)** — workflow **`release-pypi.yml`**. If PyPI offers **Environment name: (Any)**, you can still use a GitHub **Environment** named **`pypi`** for approval gates; otherwise match whatever you register on PyPI ([trusted publishers](https://docs.pypi.org/trusted-publishers/)).
2. **GitHub:** Settings → **Environments** → create **`pypi`** (optional: required reviewers / wait timer before OIDC publish).
3. Bump **`version`** in **`pyproject.toml`** and **`src/flightdeck/__init__.py`**, update **`CHANGELOG.md`**, merge to **`main`**.
4. **`git tag vX.Y.Z`** (must match **`pyproject.toml`** exactly, e.g. **`v1.1.1`**) then **`git push origin vX.Y.Z`**.
4. **`git tag vX.Y.Z`** (must match **`pyproject.toml`** exactly, e.g. **`v1.1.2`**) then **`git push origin vX.Y.Z`**.

The workflow runs **ruff**, **pytest**, schema drift, **`uv build`**, publishes **sdist + wheel** to **PyPI** via **OIDC** (no long-lived API token in repo secrets), enables **publish attestations**, and creates a **GitHub Release** with generated notes and **`dist/*`** assets.

Expand Down
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ High-level notes for **shipping FlightDeck**. Detailed history: **[CHANGELOG.md]

Narrative docs (including the CLI reference) are maintained on **[github.com/flightdeckdev/flightdeck](https://github.com/flightdeckdev/flightdeck)** `main`; this file and **`schemas/`** ship in minimal clones.

## v1.1.2 — Forensics filters, JSONL export, Phase 1 closure slice

Patch release (see **[CHANGELOG.md](CHANGELOG.md)**): optional **`trace_id`** filter on **`GET /v1/runs`**, **`flightdeck runs list --trace-id`**, and SDK **`list_runs(trace_id=…)`** (exact match on **`RunEvent.request.trace_id`**); **`flightdeck runs export`** writes the same filtered slice as JSONL (stdout or **`-o`**, **`--limit`** up to **500**, stderr warning when truncated). **Stable contracts:** additive HTTP query param and CLI command only.

## v1.1.1 — Workspace discovery, web approval UX, docs + CI cookbook

Patch release (see **[CHANGELOG.md](CHANGELOG.md)**): **`GET /v1/workspace`** exposes non-secret workspace flags (**`promotion_requires_approval`**, **`pricing_catalog_configured`**, **`server_version`**) for scripting and the **Actions** page; web **Promote** follows the two-step request/confirm path when approval is required; expanded operator docs and **`examples/ci/promote_with_approval.sh`** + README workflow snippet. **Stable contracts:** additive HTTP and JSON Schema only.
Expand Down
16 changes: 12 additions & 4 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ This roadmap is meant to be clear from **what is already shipped** to **near-ter
- **Release registry and verification:** versioned `release.yaml` artifacts with checksums, `flightdeck release verify`.
- **Economic + operational governance:** immutable pricing imports, trusted `release diff`, policy-gated `promote` and `rollback`.
- **Audit trail:** promotion/rollback history with stable sequencing (`audit_seq`) and integrity checks via `doctor`.
- **Evidence ingestion:** `runs ingest` from JSONL/JSON arrays plus stable `POST /v1/events` contracts (`schemas/v1/`).
- **Evidence ingestion:** `runs ingest` from JSONL/JSON arrays plus stable `POST /v1/events` contracts (`schemas/v1/`); **`GET /v1/runs`**, **`runs list`**, optional **`trace_id`** filter, and **`runs export`** (JSONL) for operator forensics.
- **Local API + UI:** `flightdeck serve` routes and web UI (Overview with ledger metrics, Diff, Promote) in `src/flightdeck/server/static/`.
- **SDK and tooling:** Python sync/async clients with retries/batching and `flightdeck-quickstart-verify`.

---

## Next release

Further **Phase 1** work after **v1.1.1** (deeper forensics / replay UX, OTLP-oriented telemetry per gaps table). Track **[CHANGELOG.md](CHANGELOG.md)**.
Further work after **v1.1.2** — **OTLP-oriented** telemetry, **replay-style web** forensics, deeper **catalog lifecycle** governance, and **cross-workspace / fleet** product surfaces stay on **Phase 2 / mid-term** (see gaps table below). Track **[CHANGELOG.md](CHANGELOG.md)**.

**v1.1.2** (patch, shipped): **`trace_id`** filter on **`GET /v1/runs`**, **`flightdeck runs list --trace-id`**, and SDK **`list_runs`**, plus **`flightdeck runs export`** (JSONL, stderr warning when truncated); **[examples/README.md](examples/README.md)** adds a **Phase 1 readiness checklist**; **Phase 1 status** (this document) records closure of the scoped productization tranche. See **[CHANGELOG.md](CHANGELOG.md)** and **[RELEASE_NOTES.md](RELEASE_NOTES.md)**.

**v1.1.1** (patch, shipped): **`GET /v1/workspace`** (read-only flags + **`server_version`**); web **Actions** page uses it for **direct promote** vs **request / pending list / confirm**; operator docs refresh (**README**, **release-artifact**, **examples**, **web-ui**, **http-api**, **sdk**, **`docs/pricing-catalog.md`**, **SECURITY**); **`examples/ci/promote_with_approval.sh`** and **`github-actions/promote-approval-twostep.yml`**; CI runs a second Playwright pass with **`FD_E2E_FORCE_APPROVAL`**. See **[CHANGELOG.md](CHANGELOG.md)** and **[RELEASE_NOTES.md](RELEASE_NOTES.md)**.

Expand Down Expand Up @@ -92,13 +94,13 @@ Shipped on **`main`**:

Goal: move from solid local tooling to repeatable production usage patterns.

**v1.1.0** ships the first tranche: catalog + hints on diffs, approval-gated promote (HTTP + CLI), read-only runs listing, Helm + fleet reference docs, and migration **v4**. **v1.1.1** closes the **web + discovery** gap for approval (**`GET /v1/workspace`**, Actions UX) and refreshes team docs / CI examples. Remaining bullets below are still in scope for later minors/patches.
**v1.1.0** ships the first tranche: catalog + hints on diffs, approval-gated promote (HTTP + CLI), read-only runs listing, Helm + fleet reference docs, and migration **v4**. **v1.1.1** closes the **web + discovery** gap for approval (**`GET /v1/workspace`**, Actions UX) and refreshes team docs / CI examples. **v1.1.2** adds **CLI/HTTP forensics** filters and **JSONL export**; see **Phase 1 status** below.

### Phase 1 progress (post–v1.1.0 / v1.1.1)

- **Approval workflow:** **v1.1.0** added HTTP + CLI + **`GET /v1/promotion-requests`**. **v1.1.1** adds **`GET /v1/workspace`** and a first-class **web** path (request → pending table → confirm) keyed off `promotion_requires_approval`, plus **`examples/ci/promote_with_approval.sh`** and a **`workflow_dispatch`** sample workflow.
- **Operator narrative:** README / **examples** index / **web-ui** / **release-artifact** / **http-api** / **sdk** / optional **`docs/pricing-catalog.md`** describe catalog fields, runs listing, and the two promote modes.
- **Still open:** **Forensics** beyond read-only runs list and **`trace_id`-scoped listing** (replay-style views, JSONL exports), richer catalog lifecycle governance, OTLP-oriented telemetry — see gaps table above.
- **Forensics (v1.1.2):** **`trace_id`** filter and **`runs export`** (JSONL). **Still open** for replay-style **web** views and richer export semantics if needed; **OTLP** and **catalog lifecycle** depth remain mid-term (gaps table).

### Build in this phase

Expand All @@ -115,6 +117,12 @@ Goal: move from solid local tooling to repeatable production usage patterns.
- At least two provider pricing sources compare cleanly in one diff workflow.
- Teams can stand up and operate `flightdeck serve` with documented deployment guidance.

### Phase 1 status

**Phase 1 is closed** as of **v1.1.2** for the **scoped local-first productization tranche** shipped across **v1.1.0–v1.1.2**: catalog-aware diffs + hints, approval-gated promote (HTTP + CLI + web), read-only runs forensics (**`GET /v1/runs`**, **`runs list`**, **`trace_id`**, **`runs export`**), reference **Helm** / **Compose** / **fleet** examples, SQLite migration **v4**, **`GET /v1/workspace`**, and a discoverability **checklist** for the Phase-1 **readiness signals** in **[examples/README.md](examples/README.md)**.

**Carried forward to Phase 2 / mid-term** (explicitly not claimed by this closure): **OTLP-oriented** correlated telemetry; **cross-workspace / fleet** governance products; **replay-style** forensics UI beyond CLI/HTTP listing; **enterprise-grade** identity beyond documented bearer + loopback patterns; deeper **catalog lifecycle** governance (for example version skew hints).

---

## Phase 2: Scale and platform options (long term, conditional)
Expand Down
12 changes: 11 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ If no policy has been set, prints the default policy (all constraints `null`/dis

## `flightdeck runs`

Subgroup for ingesting and listing run events.
Subgroup for ingesting, listing, and exporting run events.

### `flightdeck runs list`

Expand All @@ -458,6 +458,16 @@ flightdeck runs list RELEASE_ID --window WINDOW [--env ENV] [--tenant …] [--ta

`--trace-id` filters to events whose ingested `request.trace_id` equals the given string (exact match), same as the `trace_id` query parameter on `GET /v1/runs`.

### `flightdeck runs export`

Write the same filtered slice as `runs list` (newest first) as **JSONL** — one `RunEvent` JSON object per line. Default **`--limit`** is **500** (maximum **500**). If more events match the window and filters, only the first **`--limit`** lines are written and a **`WARNING:`** line is printed to **stderr** with `exported` / `matching` counts.

```bash
flightdeck runs export RELEASE_ID --window WINDOW [-o export.jsonl] [--env ENV] [--tenant …] [--task …] [--trace-id ID] [--limit N]
```

With **`-o` / `--output`**, writes UTF-8 JSONL to that path; without it, writes to **stdout** (suitable for pipes).

### `flightdeck runs ingest`

Ingest `RunEvent` records from a JSONL or JSON array file.
Expand Down
2 changes: 1 addition & 1 deletion docs/http-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Read-only flags derived from `flightdeck.yaml` plus the running package version.
"kind": "WorkspacePublic",
"promotion_requires_approval": false,
"pricing_catalog_configured": false,
"server_version": "1.1.1"
"server_version": "1.1.2"
}
```

Expand Down
14 changes: 12 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,19 @@ This folder holds **copy-pasteable** references for wiring FlightDeck into a rea
2. **Ingest** evidence: `flightdeck runs ingest <file.jsonl>` (or JSON array file), or HTTP `POST /v1/events` while `flightdeck serve` is running.
3. **Register** a release bundle: `flightdeck release register <bundle-dir>` then **`flightdeck release verify`** against the same tree before you trust the checksum.
4. **Diff and gate** in CI: `flightdeck release diff …` with **`--fail-on-policy`** when you want a non-zero exit without mutating promotion — see [ci/](ci/README.md) and `ledger_gate.py` / GitHub Actions templates. Optional **`pricing_catalog_path`** in `flightdeck.yaml` adds **`pricing.catalog`** / **`pricing.hints`** on diffs (see [docs/pricing-catalog.md](../docs/pricing-catalog.md)).
5. **Promote or rollback** via CLI (`flightdeck release promote` / `rollback`) or HTTP `POST /v1/promote` and `POST /v1/rollback` (token + loopback rules apply). When **`promotion_requires_approval: true`**, use **`release promote-request`** / **`promote-confirm`** or **`POST /v1/promote/request`** then **`POST /v1/promote/confirm`** — see [ci/promote_with_approval.sh](ci/promote_with_approval.sh).
5. **Promote or rollback** via CLI (`flightdeck release promote` / `rollback`) or HTTP `POST /v1/promote` and `POST /v1/rollback` (token + loopback rules apply). When **`promotion_requires_approval: true`**, use **`release promote-request`** / **`promote-confirm`** or **`POST /v1/promote/request`** then **`POST /v1/promote/confirm`** — see [ci/promote_with_approval.sh](ci/promote_with_approval.sh) and [ci/README.md](ci/README.md) (GitHub Actions patterns).
6. **Run the server** in a container or compose stack — see [deploy/](deploy/README.md). The bundled UI calls **`GET /v1/workspace`** to choose direct promote vs request/confirm.
7. **Triage runs** with **`flightdeck runs list`** or **`GET /v1/runs`**, and **observe** aggregate ledger size with **`GET /v1/metrics`** (JSON counters; read-only, same access tier as other `GET /v1/*` routes).
7. **Triage runs** with **`flightdeck runs list`** / **`runs export`** or **`GET /v1/runs`**, and **observe** aggregate ledger size with **`GET /v1/metrics`** (JSON counters; read-only, same access tier as other `GET /v1/*` routes).

## Phase 1 readiness (quick checklist)

Use this as a **discoverability** pass for the **[ROADMAP.md](../ROADMAP.md)** Phase-1 readiness signals (not a product guarantee):

| Signal | Where to start |
|--------|----------------|
| **Approval-gated promote in CI** | [ci/promote_with_approval.sh](ci/promote_with_approval.sh), [ci/README.md](ci/README.md), [ci/github-actions/promote-approval-twostep.yml](ci/github-actions/promote-approval-twostep.yml) |
| **Two-provider (or catalog) pricing on a diff** | [docs/pricing-catalog.md](../docs/pricing-catalog.md); tests **`test_diff_cross_provider_releases`** and **`test_catalog_comparable_cost_on_cross_provider_diff`** in `tests/` |
| **Operate `flightdeck serve` with deployment guidance** | [deploy/README.md](deploy/README.md) (**Compose healthcheck**, **`FLIGHTDECK_LOCAL_API_TOKEN`**, **SQLite backup** via **`flightdeck doctor --backup`**); optional **Helm** under [deploy/chart/flightdeck/](deploy/chart/flightdeck/) |

## Subfolders

Expand Down
2 changes: 1 addition & 1 deletion examples/ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ uv run python examples/ci/ledger_gate.py
Example (**PyPI** install):

```bash
pip install "flightdeck-ai>=1.1.1"
pip install "flightdeck-ai>=1.1.2"
export WORKSPACE="$(mktemp -d)"
export QUICKSTART_ROOT=/path/to/flightdeck/examples/quickstart
python /path/to/flightdeck/examples/ci/ledger_gate.py
Expand Down
2 changes: 1 addition & 1 deletion examples/ci/github-actions/policy-gate-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
env:
# Pin to a tag or SHA that matches your installed flightdeck-ai version when possible.
FLIGHTDECK_REF: main
FLIGHTDECK_AI_SPEC: ">=1.1.1"
FLIGHTDECK_AI_SPEC: ">=1.1.2"

jobs:
ledger-gate:
Expand Down
2 changes: 1 addition & 1 deletion examples/deploy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
FROM python:3.14-slim

RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir "flightdeck-ai>=1.1.1"
&& pip install --no-cache-dir "flightdeck-ai>=1.1.2"

WORKDIR /workspace

Expand Down
3 changes: 2 additions & 1 deletion examples/deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ Compose sets a **`healthcheck`** on **`/health`** plus restart policies; for sys
## Related

- **[examples/integration/README.md](../integration/README.md)** — emit `RunEvent` traffic into a running server.
- **[examples/ci/README.md](../ci/README.md)** — CI policy gates without `serve`.
- **[examples/ci/README.md](../ci/README.md)** — CI policy gates without `serve`; **approval-gated promote** script [promote_with_approval.sh](../ci/promote_with_approval.sh) and workflow samples.
- **[SECURITY.md](../../SECURITY.md)** — trust boundaries before exposing **`/v1/*`** beyond loopback.
2 changes: 1 addition & 1 deletion examples/deploy/chart/flightdeck/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ name: flightdeck
description: Optional Helm chart for flightdeck serve (single-replica reference)
type: application
version: 0.1.0
appVersion: "1.1.1"
appVersion: "1.1.2"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "flightdeck-ai"
version = "1.1.1"
version = "1.1.2"
description = "Ship AI agents safely with release diffs, runtime evidence, and policy gates."
readme = "README.md"
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion src/flightdeck/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""FlightDeck — ship AI agents safely with release diffs, runtime evidence, and policy gates."""

__version__ = "1.1.1"
__version__ = "1.1.2"
63 changes: 62 additions & 1 deletion src/flightdeck/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import json
import os
import sys
from pathlib import Path
from uuid import uuid4

Expand Down Expand Up @@ -312,7 +313,7 @@ def policy_show() -> None:

@cli.group()
def runs() -> None:
"""Ingest run events."""
"""Ingest, list, and export run events."""


@runs.command("list")
Expand Down Expand Up @@ -369,6 +370,66 @@ def runs_list(
click.echo(json.dumps(ev, sort_keys=True))


@runs.command("export")
@click.argument("release_id")
@click.option("--window", required=True, help="Time window like 7d, 24h, 30m.")
@click.option("--env", "environment", default=None)
@click.option("--tenant", "tenant_id", default=None)
@click.option("--task", "task_id", default=None)
@click.option("--trace-id", "trace_id", default=None, help="Filter to events whose request.trace_id matches (exact).")
@click.option("--limit", default=500, show_default=True, type=click.IntRange(1, 500))
@click.option(
"-o",
"--output",
"output_path",
type=click.Path(path_type=Path),
default=None,
help="Write JSONL to this file; default is stdout.",
)
def runs_export(
release_id: str,
window: str,
environment: str | None,
tenant_id: str | None,
task_id: str | None,
trace_id: str | None,
limit: int,
output_path: Path | None,
) -> None:
"""Export run events as JSONL (newest first), same filters as ``runs list`` (truncated to --limit, max 500)."""
cfg = load_config()
storage = Storage(cfg.db_path)
storage.migrate()
try:
payload = query_run_events_page(
cfg=cfg,
storage=storage,
release_id=release_id,
window=window,
environment=environment,
tenant_id=tenant_id,
task_id=task_id,
trace_id=trace_id,
limit=limit,
)
except OperationError as e:
raise click.ClickException(str(e)) from e
stream = output_path.open("w", encoding="utf-8", newline="\n") if output_path else None
try:
out = stream or sys.stdout
for ev in payload["events"]:
out.write(json.dumps(ev, sort_keys=True) + "\n")
finally:
if stream is not None:
stream.close()
if payload["truncated"]:
click.echo(
f"WARNING: exported {payload['returned']} of {payload['matched_total']} matching events "
f"(cap --limit {limit}).",
err=True,
)


@runs.command("ingest")
@click.argument("path", type=click.Path(exists=True, path_type=Path))
def runs_ingest(path: Path) -> None:
Expand Down
Loading
Loading