feat: Own and auto-sync the OpenAPI spec extras (RES-14) - #4691
Open
ArmagedonFlamer wants to merge 8 commits into
Open
feat: Own and auto-sync the OpenAPI spec extras (RES-14)#4691ArmagedonFlamer wants to merge 8 commits into
ArmagedonFlamer wants to merge 8 commits into
Conversation
The published cognee_openapi_spec.json had two owners that disagreed. This script generated it on release; the docs repo's generate-api-docs.sh regenerated it on a Wednesday cron and post-processed in the extras FastAPI does not emit - servers, tag descriptions, request examples, security schemes. Both rewrote the whole file, so every release stripped what the cron had added and every cron put it back. Port that post-processing here as enhance_spec(), making this the single generator. Verified against a faithful replica of the bash step: the entire diff is one line, the CookieAuth cookie name. The docs cron published "fastapiusersauth" (fastapi-users' default) while cognee's transport sets "auth_token", so the reference documented a cookie that never existed; enhance_spec reads it off default_transport instead. The extras live in spec_extras.json rather than as literals because two of them are machine-maintained (see RES-21) - JSON is a load-mutate-dump with nothing for the formatter to disagree with. request_examples is keyed by "METHOD /path" rather than FastAPI's operationId, which embeds the handler function name and so orphaned an example on any rename. The docs-repo generator has to be deleted in the same window, or the next cron overwrites this output again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The extras enhance_spec() applies are hand-maintained and keyed off things that move: the tags routes carry, and the routes themselves. Nothing watched them. router_docstring_sync does not - both its commit steps stage only `cognee`, describe_router_params is scoped to cognee/api, and a tag blurb is not derivable from a docstring or Pydantic metadata anyway. The drift was already real: 21 of 32 route tags had no description, covering add/cognify/ search while remember/recall/forget/improve rendered bare, and one entry described a tag no route used. Add a detect-fix-PR automation in the shape of router_docstring_sync. Dead tag descriptions are deleted and missing ones get a placeholder mechanically; Claude then writes the blurbs from the endpoints carrying each tag. An example pinned to a route that no longer exists, or a malformed server URL, is reported in the PR body rather than guessed at - picking a route for a stale sample would attach a wrong body to a live endpoint. Three things differ from router_docstring_sync deliberately, because each caused a real bug there: - A freshness re-check after dependency install. That job checked out dev, installed for ~5 minutes, and pushed a fix built from a tree that had moved on, opening #4688 - which would have reverted COG-6292. - The existing-PR lookup uses --state all and reopens a closed-but-unmerged PR. Looking only at open PRs is what turned a closed #4668 into a second identical #4672. - The content hash covers only the file the fixer writes. Hashing the whole cognee/ subtree meant any unrelated commit invalidated it, so the skip path never fired and every run force-pushed over the review. There is no pull_request trigger: adding one to test router_docstring_sync from its own PR is what opened #4668 against dev before it had merged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ArmagedonFlamer
requested review from
Vasilije1990,
dexters1 and
siillee
as code owners
August 27, 2026 10:39
ArmagedonFlamer
marked this pull request as draft
August 27, 2026 10:50
The first dispatch failed after 6m36s with "can't open file tools/check_spec_extras.py", reported as "Checker failed to import the app". The job checks out dev, and the scripts only exist on this branch - so the run was doomed at second zero but spent 6m14s preparing 490 packages before finding out. Dropping router_docstring_sync's second checkout kept the tree coherent but left no way to exercise this before merging, which is why a pull_request trigger got added to test it: exactly the mistake that opened #4668 against dev from an unmerged workflow. Give workflow_dispatch a `ref` input instead, selecting both the tree and the scripts so they stay the same checkout, plus a `dry_run` input that stops after showing the fixer's diff. Gate the write path on the tree being dev rather than on the trigger. A fix built from a non-dev tree is not what dev runs, so a non-dev ref is forced to a dry run - and because the gate reads the tree, it holds even if a pull_request trigger is added later. Remove the trigger added for testing; dispatching with `ref` replaces it. Move the machinery check ahead of the install so a misconfigured run fails in seconds with a message naming the cause, and stop reporting a missing file as an import failure. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both jobs watched pushes to dev, and the numbers do not support it. Over the last 30 days dev took 791 commits: 594 of them would have fired the docstring sync (it watches all of cognee/**) and 205 the extras sync, roughly 20 and 7 runs a day. Against that, a new route tag appears 2-3 times a month, and only 8 commits in 90 days touched a tags=[...] declaration at all. For the extras sync, pyproject.toml and uv.lock were pure noise: 92 commits in 90 days, 4 of which touched a router. The cost was not only runner time. Each push run force-pushes the fix branch and rewrites the PR body, so a review in progress moved under the reviewer. Weekly gives one stable PR to read. Pair them instead: docstring sync Monday 05:00 UTC, extras sync 06:00. Same morning, so both PRs land in one review session. Docstrings first because the data flows that way - they become the endpoint descriptions the extras sync feeds to Claude when writing tag blurbs - and because the second run then reuses a warm uv cache, which is the difference between a ~6 minute install and a ~45 second one. Dropping the push trigger invalidates the extras sync's freshness bail-out, which was justified by the push that moved dev having its own queued run. There is no such run now; the next one is a week away. Bailing is still right, since a fix built from a stale tree must never be pushed, but it now costs a whole week, so it emits a warning and a step summary instead of skipping silently. Trade-off worth naming: drift merged on a Tuesday now waits until the following Monday. That is a bare sidebar group for the extras sync, but wrong parameter documentation for the docstring sync - dispatch it manually ahead of a release. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both weekly sweeps exist to catch drift before a release ships it, so they belong just ahead of one. Of the last 50 stable releases, 35 went out Thursday through Sunday and Friday was the single most common day, with only 15 landing Monday through Wednesday. Monday was ahead of more releases in absolute terms but several days staler for the ones that matter. Wednesday 05:00 and 06:00 UTC keeps the hour-apart pairing and puts both PRs in the queue at the start of the European working day, leaving Wednesday to review and merge them. It is also the slot the docs repo's generate-api-docs cron used, for the same reason - RES-14 specified Wednesday and the original implementation drifted to Monday. A release cut Monday to Wednesday still works from the prior week's sweep. Closing that remaining gap needs a per-PR check rather than a schedule, which RES-14 already tracks as a follow-up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ArmagedonFlamer
marked this pull request as ready for review
August 27, 2026 11:46
ArmagedonFlamer
force-pushed
the
feature/res-14-merge-spec-enhancement
branch
from
August 27, 2026 11:57
1bb93fa to
95a1a9f
Compare
Contributor
|
@ArmagedonFlamer please keep PRs as draft until they pass base checks and ruff |
Contributor
Author
@Vasilije1990 It seems the ruff issue was from the dev branch. After merging updated dev, the ruff issue is gone. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gives
cognee_openapi_spec.jsona single owner, then keeps the parts of it thatFastAPI cannot generate honest automatically.
Closes the RES-14 acceptance criteria on spec ownership; the automation is RES-21.
Why
The published spec had two owners that disagreed.
tools/sync_release_docs.pygenerated it on release. The docs repo's
.github/scripts/generate-api-docs.shregenerated it on a Wednesday cron and post-processed in everything FastAPI does not
emit —
servers, tag descriptions, request examples, security schemes. Both rewrotethe whole file, so every release stripped what the cron added and every Wednesday
put it back.
The extras were also wrong in a way nobody could see. The docs cron published
CookieAuthwith cookie namefastapiusersauth— fastapi-users' default — whilecognee's transport sets
auth_token(default_transport.cookie_name). The referencedocumented a cookie that has never existed.
What changed
1.
enhance_spec()intools/sync_release_docs.py— the docs cron'spost-processing, ported. This script is now the only generator.
The extras live in
tools/spec_extras.jsonrather than as Python literals, becausetwo of the three are machine-maintained: the fixer does a JSON load-mutate-dump
instead of splicing source. That also sidesteps what forced
router_docstring_syncto pin
ruff@0.15.11"so the bot never fights the formatter" — there is no Pythonfor the formatter to reflow.
request_examplesis keyed by"METHOD /path", not FastAPI'soperationId. TheoperationId embeds the handler function name (
add_api_v1_add_post), so a renamesilently orphaned an example. Path and method are the API contract.
2.
spec_extras_sync.yml+check/fix_spec_extras.py— a detect → fix → PRautomation in the shape of
router_docstring_sync.serversentryThe last two are not guessed at: choosing a route for a stale sample would attach a
wrong body to a live endpoint, and a base URL is a deployment fact.
This matters because the drift is already real — 21 of 32 route tags have no
description. The 11 that do cover
add/cognify/search/delete, the legacysurface, while
remember/recall/forget/improverender as bare sidebar groups.The automation's first PR clears that backlog.
Verification
Port fidelity. A faithful Python replica of the bash step 3 run against the same
app.openapi()output. Entire diff, 12k lines in:Against the live published spec, every extra matches byte-for-byte:
The
enhance_specoutput stayed byte-identical through both the move to JSON and there-key.
Pipeline, real app, Claude stubbed:
Plus, on a cached schema so each case is hermetic: no-op rerun leaves the file
byte-identical, dead key removed, orphaned example reported (exit 1, not fixed),
--warn-onlynever fails. The PR-lookup logic was exercised against all five states —no PR / open / closed / merged-only / closed+merged →
CREATE,UPDATE,REOPEN+UPDATE,CREATE,REOPEN+UPDATE.🤖 Generated with Claude Code