Repository navigation
chore(release): v0.78.0 - #870
Merged
Merged
Conversation
#848's three bad states came from an MCP server this repo tells people to install. An editor with gflow registered starts one `gflow mcp run` per session; they are long-lived and usually plural, and on Windows they hold files inside the install open, so a package manager can abort part-way and leave the environment broken. The advice to stop those processes already existed — in the remediation `gflow update` prints once the damage is done. Saying it in the upgrade section is the difference between a recovery step and not needing one. Also states why `gflow update` cannot pre-flight this itself: it is one of those processes. `structlog` imports `greenlet` unconditionally, so every `gflow` maps the venv's native modules, and refusing while any gflow holds the install would refuse every run. Mechanism reported with a reproduction by Cstanish127 on #824. Closes #848
…e holder is installed Council review of #856 converged on three findings. Three dimensions independently flagged that the new "if an upgrade does abort" paragraph restated the exit-11 / forced-reinstall paragraph fifteen lines above it, with less precision — the original also names the repair command and why a plain `upgrade` cannot do it. Replaced with a back-reference, so the mechanism is stated once and cannot drift. The block also contradicted the paragraph immediately before it: that one says a running `gflow.exe` holding its own launcher is benign and the upgrade lands anyway, this one said a running gflow leaves the environment broken. Same-sounding phenomenon, opposite guidance, adjacent. Named the distinguisher — the launcher you are running versus a different gflow holding the install's contents. `tasklist` matches image names only, so it finds the `gflow.exe` shim but not a server registered as `uv run gflow mcp run`; said so rather than implying parity with `pgrep -f`, which is now `-fa` so the command line that matched is visible before anything is stopped. Softened two frequency claims ("people actually hit", "usually more than one") that a single reproduction does not support, per the claims discipline in AGENTS.md, and pointed the citation at the comment carrying that reproduction instead of the issue body, which still carries the refuted chrome.exe attribution. docs/MCP.md is the page that installs the lock holder and never said registering it gates upgrades; one note at the end of § 4 now covers every client listed there. USAGE.md's own instruction landed 67 lines into the section, so the section head now carries a one-line pointer.
docs(update): name the lock holder before the upgrade, not after
…gle.com (#831) Ports `--end-frame` (start+end interpolation i2v) to the migrated host. Refs #639. Contributed by @stgmt, who found that the timeout was happening AFTER a successful server-side submit — the difference between a broken feature and one that bills you while reporting failure — and traced it to rpc `nprQif`. Verified on a second cohort (ffroliva/flow.google.com, 2026-09-17) at $0 via route-abort: rpcid and both media ids confirmed; model key is cohort-dependent (`omni_flash_i2v_8s_first_last` here vs `veo_3_1_interpolation_lite` there), so matching is on key shape. Findings in docs/superpowers/spikes/2026-09-17-migrated-end-frame-submit-contract.md. Maintainer follow-up: dropped adoption-by-content (dead for the veo key, and able to hard-fail an already-billed run), armed the billing guard for t2v, fixed a spike script that could not run, added the missing attach_end_frame coverage and a zero-credit e2e, de-vacuumed the BDD scenarios (ablation-verified), stopped an existing e2e from spending a credit and failing on a labs-only event name, and corrected seven doc sites. Offline suite 4489 passed; new e2e passes live.
…hored match (#860) (#866) `_pick_frame_by_name` matched a picker option with `_exact(name)` -- `^\s*<name>\s*$`. A picker tile is a `mat-icon` ligature followed by the file name, and a locator reads both nodes as one string, so the option offered for `hero-ab12cd34.png` reads `imagehero-ab12cd34.png`. The anchors could never hold: every i2v run on this cohort ended in exit 32, "the frame picker lists no asset named ...", while the picker had been listing it the whole time. The reported listing is what pinned it -- the `image` prefix is the icon, not a rename. Root cause at migrated_composer.py:1980, and only there. The other three `_exact` callers (a `mat-icon` ligature, two aspect-ratio radios) match a node's own text and are left alone. `_mention_by_name` never matches text at all -- it types `@name`, presses Enter and counts chips -- so the r2v mention misses seen on the same account are a different defect and stay open. The match is now containment on the display name, which `_unique_display_name` makes run-unique (#792), so it still cannot bind a stale copy of the same file. Formalized as e2e, not a mocked proxy: a fake locator returns the list the test wrote into it and matches the anchored pattern happily, so the mocked version of this test passes against the bug. `tests/features/frame_picker_binding.feature` serves the tile through route interception ($0, no Google, no profile, no credits) and real Chromium reports `imagehero-ab12cd34.png` -- the same shape as the `images1-cbbc098b.jpg` the issue was filed with. Red before green, measured both ways with the final test file: fix stashed -> 2 failed, 1 passed (62.22s; the miss is 3/3 searches) fix restored -> 3 passed (5.49s) The third scenario is the A/B control: an asset the picker genuinely does not list is still refused with exit 32, so containment is not "bind anything".
…nly on load (#859) (#867) A `role="dialog"` promo panel over a freshly loaded editor made `image t2i` fail as exit 23 -- ".settings-trigger-button did not accept a click within 5000 ms -- it is covered by div.cdk-overlay-backdrop" -- with a remediation telling the user to file a selector-drift bug, when one click on the page fixes it permanently and nothing had drifted. The issue was filed saying the dismissal was ordered AFTER the click. It is not: `ensure_editor` calls `_dismiss_dialog` at :804, before it waits for the trigger. It calls it one frame after `domcontentloaded`, before Angular has rendered anything, so `is_visible()` answers False and the method returns having done nothing. That is why a blocked run logs NEITHER `migrated.dialog_dismissed` nor `migrated.dialog_not_dismissed` -- the silence in the incident's event stream is the evidence, and the "wrong position" reading does not explain it. The dismissal now also runs in `_open_pane`, where the image and video paths take their FIRST click and where the `glue` consent bar is already cleared for the same reason. It stays best-effort: a modal that refuses to go falls through to the click post-mortem, which names the backdrop, rather than becoming a second competing error path. Formalized as e2e because the whole defect is WHEN the DOM has the dialog in it. A mocked page is whatever the test constructed before the test ran; it has no "later", so the broken ordering and the fixed one look identical to it. `tests/features/blocking_dialog_dismissal.feature` mounts the modal 250 ms after load through route interception ($0, no Google, no profile, no credits). Red before green: fix stashed -> 2 failed, 2 passed (22.64s) fix restored -> 4 passed (12.68s) The two that flip are the dismissals. The other two hold both ways on purpose: a modal that ignores its close button is still named rather than swallowed, and an editor with NO modal records no gesture at all -- the control that matters here, because `_dismiss_dialog` falls back to Escape, and an Escape pressed on a healthy page would close the settings pane the next line opens.
…rom the CLI (#861) (#869) * feat(docs): `gflow docs` — list, print and search the documentation from the CLI (#861) 126 pages in `docs/` and nothing in the CLI pointed at any of them, so at the moment of use the knowledge was unreachable and the reader guessed. The failure that filed this issue was `video r2v --duration 10` against a host that offers reference-to-video at 8 s only -- a rule that was already written down and never found. gflow docs every topic, with what it covers gflow docs usage one page, raw Markdown (it pipes) gflow docs --search "r2v duration" the line that answers the question Read-only and offline: no network, no account, no credits, no database. THE PAGES SHIP IN THE WHEEL. The user this was filed for installed from PyPI and has no checkout, so a command that could only print GitHub links would not solve it. `hatch_build.py` globs `docs/*.md` into `gflow_cli/_docs` (+1.3 MB). The two-line alternative -- `force-include` plus `exclude` -- was built and inspected first: hatchling ignores exclusions for forced inclusions, and 148 files it was told to exclude shipped anyway, 4.3 MB of plans, spikes and PNGs. The glob is also the list, so the topics cannot drift as pages are added. SEARCH RETURNS A LINE, NOT A FILE NAME. The rule above lives inside a 4 000-character bullet in `MCP.md`; answering "it is in MCP.md" leaves the reader where they started. Hits carry `docs/FILE.md:LINE` and the line windowed around the match. Query terms are ANDed, curated `INDEX.md` shortcut rows rank above raw body text, and a vague query reports how many hits it held back. Ranking's limit is recorded rather than papered over: `--search duration` alone has 133 hits and three orderings were measured, including one that reused `INDEX.md`'s routing as a reference-vs-record signal. That one is false -- `INDEX.md` links 93 of the 126 pages -- and was removed rather than kept as a knob that does nothing. `_score`'s docstring says so, so it is not reinvented. A topic resolves from its slug, file name, either case, or an unambiguous prefix, and NEVER by joining user input onto a path -- the argument is a key looked up in the enumerated page set, so `gflow docs ../../etc/passwd` is an ordinary unknown-topic refusal (exit 11, an existing class; no new exception, no new exit code). Every printed string round-trips through the console's own encoding, which is the #846 shape from one release ago. No MCP twin yet, registered in `_MCP_EXEMPT` so the decision is enforced rather than claimed in a docstring: documentation is a resources provider, not a tool. Every decision therefore lives in `docs_catalog`, Click-free and returning data, which is the upgrade path. Scenario + plan: docs/superpowers/plans/2026-09-17-gflow-docs-command/. Tests: 40 offline, plus an integration test that builds a wheel, installs it in a scratch venv and runs the command with the repository nowhere in sight -- the only one that can tell "it works here" from "it works for the user who filed it". * fix(ci): allowlist hatch_build.py as a canonical root file (#861) The repo-hygiene gate flags any tracked top-level *.py outside ROOT_DOC_ALLOWLIST as a stray artefact. `hatch_build.py` is not stray: hatchling resolves a build hook's `path` from the project root and defaults to that exact name, so it cannot live under scripts/ without the config pointing back out to the root anyway. Same category as the already-allowlisted `conftest.py` — a root Python file the tooling requires there. Caught by the council's D0 mechanical gate, which only turns red once the new files are tracked; the pre-commit run before `git add` was green and would have shipped this to CI. * fix(docs): apply the council review's findings to `gflow docs` (#861) Six review dimensions ran against bfc0afc. Security found nothing exploitable (19 hostile topic names, zero reads). The rest found real defects, all fixed here. CORRECTNESS - `_plain()` stripped single `_`, so every curated search hit printed a file name that does not exist -- `REFERENCE_STRATEGIES.md` came back as `REFERENCESTRATEGIES.md`, in the rows that rank FIRST, and 106 of the 126 pages carry an underscore. Only `**`, `__` and backticks are stripped now. - `_read()` passed `errors=` to `read_text`, which `Traversable.read_text` does not accept -- a TypeError on the one path that matters, a page read out of a zipimport, uncaught by `except (OSError, ValueError)`. Every test passed because an editable checkout exercises the *fallback* branch. Now reads bytes and decodes. - The heading bonus fired on `#` shell comments inside fenced code blocks. That is what put eight LIVE_VERIFICATION records on top of `--search duration` -- the noise the ranking was added to remove. Fence tracking, four lines. - `--search ""` defeated the topic/search mutual exclusion: the guard tested truthiness while the dispatch tested `is not None`, so the topic was silently discarded. Both test `is not None` now. - `_snippet` windowed on the first term typed; it now centres on the least frequent one, which is the selective half of a two-word query. - The search table's title reported the SHOWN count while the footer reported the total -- "20 match(es)" above "and 116 more". - Every refusal inherited ConfigurationError's default remediation: "Run `gflow config list-transports`", advice about a transport registry printed at someone who asked for a page. - An unknown topic now uses `difflib.get_close_matches`, so `usge` is offered `usage`. USAGE.md already promised this; a substring scan could not do it. BUILD - The hook refuses to build when it finds no pages. `Path.glob` on a missing directory returns empty, and a sdist with `docs/` removed produced a valid 767 KB wheel carrying zero pages, exit 0 -- and `release.yml` is `uv build` then publish with nothing in between that would notice. - The hook now skips editable installs, which were copying 126 pages into a `site-packages/gflow_cli/` that has no `__init__.py`, is never read, and goes stale on the next doc edit. TESTS - The cp1252 test proved nothing: a mutation replacing every `_console_safe(` call site with `str(` left 33/33 green, because the test called the helper directly and never ran the command. Replaced with a subprocess run under PYTHONIOENCODING=cp1252 across three invocations -- and `_console_safe` is gone, replaced by one `sys.stdout.reconfigure(errors="replace")` that also covers Rich's own box glyphs, which the helper never did. - Added: hostile names driven through the CLI (not just `resolve()`), the NUL case, `docs ""`, `--search ""`, the remediation text, the typo suggestion, and the code-fence scoring. - Loosened the acceptance assertion from "MCP.md is first" to "MCP.md is in the top three": a heading scores +10, so a future page could take first place without the feature being any worse, and an ordinary docs edit should not red the build. - Corrected this file's own claim that the wheel test is opt-in. `integration` is not in `addopts`' exclusion list, so it runs on every plain pytest and on PR CI -- which is the right outcome and was worth stating accurately. DOCS - `gflow docs` was missing from every place the command surface is enumerated: AGENTS.md, llms.txt, skills/gflow-cli/SKILL.md, and the synopsis block of the very USAGE.md section this feature added. - Two numbers in that section were wrong (133 lines, five hits) and one promise was false until difflib landed. - CHANGELOG said +1.3 MB; measured, the wheel goes 0.74 -> 1.32 MB, so the cost is +0.58 MB compressed. NOT FIXED, ON PURPOSE - `A_B.md` and `A-B.md` would collide on one slug. Measured: zero collisions across all 126 pages. A guard for a condition that does not exist is a guard nobody can test. * fix(ci): regenerate the plugin skills mirror after the SKILL.md edit (#861) CI's three `test` jobs failed in 11-14 seconds -- too fast to be tests. The cause was the "Plugin skills in sync" step, which runs BEFORE pytest: plugins/gflow/skills is stale -- regenerate with: python scripts/ci/generate_plugin_skills.py DRIFT: plugins/gflow/skills/gflow-cli/SKILL.md Adding the `gflow docs` line to `skills/gflow-cli/SKILL.md` left its plugin mirror behind. `website/docs` has the same shape and I did regenerate that one; this generator was simply not in the local gate I ran, so the drift reached CI.
Seven version sites, CHANGELOG migrated, PROJECT_STATUS rewritten, and the live-verification ledger extended to cover the three PRs that landed after this release branch was first cut. The branch sat at 3bf8ad5 with its prep already written. #866, #867 and #869 merged into develop after that, so develop was merged in and their CHANGELOG entries moved under [0.78.0] rather than left under a heading that no longer exists. Per step 12 of the release skill, newcomers each need their own 4b row: - #860 (Frames picker binding) is VERIFIED against live Flow. It is the one item in this release that could not be proven credit-free -- the only evidence that a start frame binds is a real generation -- so clips were generated and downloaded on a migrated account, every run logging migrated.frame_uploaded -> frame_bound -> submit_observed -> download. frame_bound is the event that could not fire before this release. - #859 (promo modal ordering) is recorded as NOT OBSERVED. Its dismissal path ran on every one of those runs and no modal ever appeared, so nothing here proves the ordering fix; the $0 route-intercepted e2e is what covers it. Recorded rather than folded into #860's green. - #861 (gflow docs) is verified from the built artefact, which is its whole contract: 127 topics, docs/FILE.md:LINE search hits, no network, no checkout. PROJECT_STATUS and the INDEX live-verified row both still claimed the release was verified at ZERO credits. That was true of the release as cut and is not true of the release being tagged; both are corrected rather than left flattering. Gates: ruff, ruff format, pyright, repo hygiene (1159 files), doc links (196 files), website PII, website mirror, plugin skills, check_release_artifacts -- all green. pytest 4567 passed, 7 skipped, coverage 92.47%. NOT done, said out loud: the shipped-planning-artifact consolidation in step 10. docs/superpowers carries ~100 plan/spec/spike files from many past releases, so a this-release-only sweep would be inconsistent, and hatch_build.py cites plans/2026-09-17-gflow-docs-command/SCENARIO.md as the record of a measurement.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
This branch was previously deployed
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.
Brings the v0.78.0 bump commit and the full integration history onto
main.The tag is already pushed and
Releasecompleted success — PyPI, the GitHub Releaseand the MCP Registry publish are all live. This PR is the
maincatch-up: merge commit,never squashed.
What shipped
project.createProjectis retired (404 with a labs token, 401 without, 0/12 across four profiles). Projects are now created onflow.google.com, keyed on the observed refusal rather than on which host serves an account.video i2v --end-frameruns on the migrated host; the submit body is asserted before Flow acts on it.i2v --initial-frameworks again on migrated accounts.gflow docslists, prints and searches the documentation offline, from pages shipped inside the wheel.--aspect 3:4images run onflow.google.com.Verification
docs/LIVE_VERIFICATION_v0.78.0.md, with the honest split:migrated.frame_uploaded→frame_bound→submit_observed→download, 10/10,frame_boundbeing the event that could not fire before this release.PROJECT_STATUS.mdand theINDEX.mdlive-verified row both still claimed this release was verified at ZERO credits — true of the release as first cut, not of the release being tagged. Both corrected.Gates
ruff · ruff format · pyright · repo hygiene (1160 files) · doc links (196 files) · website PII · website mirror · plugin skills ·
check_release_artifacts— all green.pytest: 4567 passed, 7 skipped, coverage 92.47%.
Not done, said out loud
The step-10 consolidation of shipped planning artifacts.
docs/superpowers/carries ~100 plan/spec/spike files from many past releases, so a this-release-only sweep would be inconsistent — andhatch_build.pycitesplans/2026-09-17-gflow-docs-command/SCENARIO.mdas the record of a measurement.