Skip to content

fix: an app answers for itself, and an item belongs to its project - #137

Merged
thedancingdeveloper merged 1 commit into
mainfrom
fix/api-correctness
Aug 3, 2026
Merged

fix: an app answers for itself, and an item belongs to its project#137
thedancingdeveloper merged 1 commit into
mainfrom
fix/api-correctness

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Contributor

Closes #106. Closes #109. Closes #111. Closes #108.

#106 — preflight wiring leaked across create_api instances

create_api copied fleet, model client, session host and probes into a
module-level _APP_STATE. Every preflight/readiness call read that global, so
building a second app overwrote the first's wiring — app A could report app
B's worker pool, session host and probe results, while its own top-level
readiness fields still came from its own app.state. A response could say
mode: monitoring-only while every project claimed ready_to_start: true.

_preflight now takes the calling app's app.state. The global is gone.

Test builds two apps deliberately — A with no fleet and healthy probes, B with
a fleet and failing probes, B constructed second because that is the one
that used to win — and asserts A answers for itself.

#109 — latest events collided across projects

(project_id, item_id) is an item's identity, and two projects each having a
T1 is explicitly supported. _latest_by_item keyed on the item id alone, so
whichever project wrote most recently supplied latest for both. Worst part
is the deep link: session_id pointed at the other project's terminal.

Now scoped, in the audit query and the ingest-store fallback alike.

#111 — inception routes documented nothing

POST /api/inception and GET /api/inception/{id}/plan both declared
response_model=dict, which emits additionalProperties: true. A generated
client could not discover state, project_id or markdown. Now
InceptionDraft and InceptionPlan, every field described.

The test asserts against /openapi.json rather than the handler, since the
schema is the artefact that was wrong.

#108 — already fixed, never covered

WorkState gained exhausted in #120, so the 500s are gone. There was no
test, which is how it was missed in the first place. Added one: claim to the
attempt ceiling, then assert both GET /api/work and GET /api/work/{id}
answer 200 and report the state.

All four regression tests fail without their respective fix. Suite, ruff and
mypy . green.

Three API-contract defects, all found by reading the surface rather than
running it.

**One app's wiring is no longer another's (#106).** `create_api` copied its
fleet, model client, session host and probes into a module-level dictionary,
so a second app in the same process silently took over the first's readiness
gate: app A could report app B's worker pool and probe results while its own
top-level fields still said otherwise. Preflight now reads the calling app's
`app.state`. A gate an unrelated app can change is not a gate.

**Latest events are scoped to their project (#109).** `(project_id, item_id)`
is an item's identity and two projects each having a `T1` is supported, but
the projection keyed on the item id alone, so one project's newest event was
reported as the other's -- including the session deep link, which would put a
human in the wrong project's terminal.

**The inception routes publish a schema (#111).** Both answered
`response_model=dict`, which generates `additionalProperties: true` and
documents nothing. `InceptionDraft` and `InceptionPlan` name the fields a
client actually needs.

Also pins #108, already fixed by #120 but never covered: an `exhausted` item
is the one an operator most needs to look at, and both work routes used to
500 on it.
@thedancingdeveloper
thedancingdeveloper merged commit 515ffbe into main Aug 3, 2026
2 checks passed
@thedancingdeveloper
thedancingdeveloper deleted the fix/api-correctness branch August 3, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant