Repository navigation
fix: panel round 1 on #777 (src) — restore status flags, PoolConfig.validate and the no-route message; save(only=) in field order; docstring fences marked - #779
Merged
Conversation
A docstrings commit set negative="" on status's two bool flags, which removed the --no-steps / --no-json spellings that shipped. Drop it, and pin both spellings and their place in --help. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
The bounds validator was renamed _check_bounds, so PoolConfig(...).validate() fell through to pydantic's deprecated classmethod and raised TypeError. Restore the public name, give it a reference docstring, and pin that it returns the instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
The message was reworded in a docs commit; callers may match on it. Restore it, pin it exactly, and quote it as sent on the Connections page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
Model.select and Query.select raise AttributeError for a column the model does not have; Model.select's entry said ValueError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
save(only=) and save(exclude=) built their write set as a Python set, so the UPDATE listed its columns in hash order, which changes per process. A fetched instance also dumped in the SELECT's (alphabetical) order, and bytes and secret columns were appended last. save_bind_payload now returns its map in field declaration order and the write-set filter keeps that order, so save(), save(only=), save(exclude=) and bulk_create emit one statement per call on every run. The docs SQL gate no longer pins PYTHONHASHSEED=0, the Mutations page's save-only and save-exclude blocks are regenerated, and its sentence saying the order is not fixed is gone. Closes #776 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
…oop module Add the 0.26 section: Session.query now takes type[M] and returns Query[M], and ferro.migrations.chunked (never documented) is now the private ferro.migrations._chunk_loop; the chunked decorator is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
Nineteen Python fences in module and class docstrings carried no {test=...}
marker. Each is now {test=compile reason="..."} with why it cannot run
against the reference prelude, and the two that listed bare values
(StatusReport's fields, FerroSettings' database) are written as code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
Implicit default-connection routing was removed in v0.14.0 (a328cfc), and the sessions migration guide no longer exists, so the shipped text named the wrong release and a missing page. Use the accurate wording, pin it exactly, quote it on the Connections page, and correct that page's Changed note to 0.14. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP
0x054
commented
Oct 21, 2026
0x054
left a comment
Contributor
Author
There was a problem hiding this comment.
Spec: PASS
Spec = the panel findings this slice owns on #777 (F1, F3, F4, F5, F6, F12/#776, F16, Coherence).
- F1 ADDRESSED · src/ferro/cli/migrate.py:557-565 at 88ae5e5 ·
negative=""gone;tests/test_cli_help.py::test_status_accepts_both_spellings_of_its_flags/::test_status_help_lists_the_negative_flagsfail 3/5 on base src, pass on head. - F3 ADDRESSED · src/ferro/init.py:307 at 88ae5e5 ·
validaterestored with a Google-style docstring;tests/test_connection.py::test_pool_config_validate_returns_the_instancefails on base src (TypeError), passes on head. - F4 ADDRESSED · src/ferro/models.py:794-798, src/ferro/query/builder.py:787 at 88ae5e5 · ran
Author.select("zzz"),select(lambda author: author.zzz),Query.select("zzz")→ AttributeError; empty lambda tuple, duplicate output name,"author.name"→ ValueError, each as the new entries say. - F5 ADDRESSED · docs/pages/upgrading.md:72-77 at 88ae5e5 · typed
Session.querynoted in the 0.26 section. - F6 ADDRESSED · src/ferro/state.py:54-59 (unchanged from base, already the v0.14 wording), docs/pages/guide/connections.md:119 quotes it byte for byte, :187 now "Changed in 0.14"; CHANGELOG v0.14.0 carries the routing removal; declared under
Interfaces changed:; pinned exactly bytests/test_routing_errors.py::test_no_route_message_is_the_shipped_text(passes on base too, since the text did not change in this PR). - F12 / #776 ADDRESSED · src/ferro/_bind_payload.py:150-172, :270-271 at 88ae5e5 ·
save_bind_payloadreturns field order with raw (bytes/secret) columns in place;_keep_write_setwalks the payload.tests/test_save_column_order.pyfails 10/14 on base src (sqlite and postgres: save(only=), save(exclude=), full save UPDATE + INSERT, fetched instance, bulk_create), passes on head. PYTHONHASHSEED pin removed (tests/test_docs_sql.py:417-428); mutations.md blocks regenerated and the "no fixed order" sentence gone (:328).Query.updatekeeps keyword order (pinned). FK shadow column checked by hand:author_idlands after declared fields, identically for new and fetched instances. - F16 ADDRESSED · docs/pages/upgrading.md:76 at 88ae5e5.
- Coherence ADDRESSED · 19 fences in src/ferro now
{test=compile reason="…"};grep -rn '```python$' src/ferroandgrep -rn '```py' src/ferro | grep -v test=both empty; tests/test_docstring_examples.py passes. - Upgrade guide: pyproject.toml version 0.25.0, CHANGELOG top v0.25.0,
major_on_zero = false→ next feat release is 0.26; the section name and table are consistent.
Cannot verify: none.
Quality: PASS
- Minor · PR body
Interfaces changed:second bullet at 88ae5e5 · says "the no-route message [is] restored to its shipped form", contradicting the first bullet (the message is the new v0.14 text, not 0.25.0's v0.13 text). · The merge reader gets two answers. · Drop "and the no-route message" from the second bullet. - Minor · docs/pages/upgrading.md:74 at 88ae5e5 · "0.26 asks nothing of code that runs today" omits the no-route
RuntimeErrortext change relative to 0.25.0 (v0.13 + "sessions migration guide" → v0.14 + "upgrade guide"), which the PR itself declares as an interface change; code matching the full string breaks. · One bullet in the 0.26 section. - Minor · commit c05df7a at 88ae5e5 · "restore the shipped no-route RuntimeError text" is reverted by 88ae5e5; net zero in state.py. Squash or leave; history only.
Depth check: no module added and no interface changed (save_bind_payload's signature is unchanged;PoolConfig.validateis a restoration), so nothing to run it over.
Verify: PR head 88ae5e5 passed (named set incl. test_connection with --db-backends=sqlite,postgres 581 passed, Postgres accepting; PYTHONHASHSEED=3 … test_docs_sql.py -k mutations sqlite+postgres 35 passed, seed 7 sqlite passed; SQLite capture of mutations.py byte-identical under seeds 0/3/11; ruff check/format clean; ty clean; zensical build --clean --strict no issues; _docs_checks exit 0; full suite 3735 passed, 449 skipped, 2 xfailed; Postgres write subset 460 passed, 1 skipped)
Trial merge: not run (base unchanged, feat/docs-rewrite at 2a0205b = merge base)
This was referenced Oct 21, 2026
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.
Panel round 1 on #777, the
src/findings. Each section says what changed, the test that pins it, and the command that ran it.F1 ·
ferro migrate status --no-steps/--no-jsonrestorednegative=""came offstatus'sstepsandjson_. No other verb's parameters gained it in the epic: every othernegative=""insrc/ferro/cli/migrate.pypredates the branch (checked againstf3e46f8).$ ferro migrate status --no-steps # before: UnknownOptionError: Unknown option: --no-steps. Did you mean --steps?Pinned by
tests/test_cli_help.py::test_status_accepts_both_spellings_of_its_flags(all four spellings parse to the right bool) and::test_status_help_lists_the_negative_flags. RED before the fix:3 failed(--no-steps,--no-json, help).docs/pages/reference/cli.md:245lists only--steps; that page belongs to the docs worker this round.F3 ·
PoolConfig.validaterestoredThe validator is
validateagain, with a short reference docstring (Returns / Raises). Pinned bytests/test_connection.py::test_pool_config_validate_returns_the_instance. RED before the fix, GREEN after.F4 ·
select()namesAttributeErrorAuthor.select("zzz")andAuthor.select(lambda author: author.zzz)raiseAttributeError: Author has no queryable column 'zzz'….Model.select'sRaises:now lists it, and itsValueErrorentry names the cases that actually raise one (empty selection, duplicate output name, a string naming a related column).Query.selectdid not claimValueErrorfor a missing column, but it left out theAttributeError, so that entry is added there too. I checked by running each call.F5 ·
Session.querytyped: kept, noteddocs/pages/upgrading.mdgains anUpgrading to 0.26section:Session.querynow takestype[M]and returnsQuery[M], so a type checker may flag a call that passes a non-Modelclass. The "Which sections apply" table and the intro range now run to0.26. The next release is assumed to be0.26, sincemainis at0.25.0plusfeatcommits; rename the section if release tooling picks another number.F6 · The no-route
RuntimeErrortext now names the release that removed routingThe rewrite reworded
_NO_ROUTE_MESSAGEunder a docs commit, and the panel caught it. Restoring the shipped text was not an option either, because that text is wrong: implicit default-connection routing was removed in v0.14.0, not v0.13. The removal isa328cfc(feat(ff-d)!: … single-handle routing (#201), 2026-07-03). CHANGELOG lists it under v0.14.0,git tag --contains a328cfcstarts atv0.14.0, and the upgrade guide files "Session routing" underUpgrading to 0.14. The "sessions migration guide" the old text pointed to no longer exists either.The message now reads
…removed in v0.14 (deprecated since v0.12); see the upgrade guide.This PR declares the change openly (seeInterfaces changed).docs/pages/guide/connections.md:119quotes it exactly, and the page's!!! note "Changed in 0.13"(line 187) now says0.14. Pinned bytests/test_routing_errors.py::test_no_route_message_is_the_shipped_text, which checks the full string.F12 / #776 · Writes list columns in field declaration order
There were three causes, and all three are fixed in
src/ferro/_bind_payload.py:only=/exclude=filtered the payload through aset, so columns came out in hash order. The filter now walks the payload in order.__dict__order. Hydration fills__dict__in theSELECT's alphabetical order, so even a fullsave()of a fetched row was alphabetical, not declared.save_bind_payloadnow returns its map in field declaration order. That coverssave(),save(only=),save(exclude=)andbulk_create.Query.update(**fields)keeps the call's keyword order. No other write takes a set.Pinned by
tests/test_save_column_order.py. Its models declare their fields out of alphabetical order, with a bytes and a secret column in the middle, and it runs on both dialects:save(only={"a", "b"})→SET "b", "a"in field order;only=with 7 columns;exclude=; a fullsave()andINSERT; a fetched instance with no raw columns;bulk_create; andupdate()keyword order. Before the fix, 10 of 12 failed. The fetched-instance case was added once the docs gate showed alphabetical output, and it failed 2/2 until the third cause was fixed.Then:
tests/test_docs_sql.py: thePYTHONHASHSEED=0envline and its docstring sentence are removed. The now-unusedimport oswent with them, because ruff F401 required it; nothing else in the file changed.docs/pages/guide/mutations.md: the foursave-only/save-excludeSQL blocks are regenerated (SQLite and Postgres), and the sentence saying the order is not fixed is deleted. Exactly these four blocks differed on the gate.mutations.pyunderPYTHONHASHSEED=1,2,3gives byte-identical output (same md5).F16 ·
ferro.migrations._chunk_loop: kept, notedThe rename stays. The 0.26 upgrade section says the never-documented
ferro.migrations.chunkedmodule is now the privateferro.migrations._chunk_loop, and thatfrom ferro.migrations import chunkedstill imports the decorator, unchanged.Coherence · Every docstring fence in
src/ferrois markedgrep -rn '```python$' src/ferrofound 19 fences. None can run against the reference prelude: they need a migrations project, a data-step context, Alembic or Postgres row security, or they redeclare the prelude's own models. Each is now{test=compile reason="…"}with the reason. Two fences listed bare values (report.head_applied.name # "0006_add_teams",db.directory # …) and are rewritten as code:assert report.head_applied.name == "0006_add_teams", anddirectory = db.directory.grep -rn '```py' src/ferro | grep -v 'test='is now empty.Verify
Postgres was accepting connections on localhost:5432; the Postgres halves above ran.
Interfaces changed:
RuntimeErrortext for an operation with no route now reads: "No database route for this operation. Open a session (async with ferro.engines.session(...)) or passusing=/session=explicitly. Implicit default-connection routing was removed in v0.14 (deprecated since v0.12); see the upgrade guide." The old text named v0.13 (routing was removed in v0.14.0,a328cfc) and a page that no longer exists. Code matching on"No database route"is unaffected.PoolConfig.validate,migrate status --no-steps/--no-jsonand the no-route message are restored to their shipped form.save()/save(only=)/save(exclude=)/bulk_createnow list columns in field declaration order (the statement text changes; the rows written do not).Verify block corrected: no
Exit steps
Closes #776below; the save(only=) / save(exclude=) emit columns in set order, so the UPDATE text changes between runs #776 progress comment is current.reference/cli.mdstatus table may list--no-steps/--no-json.Fix report: F6 correction (88ae5e5)
The coordinator corrected the F6 decision: the goal was no silent behaviour change under a docs commit, not keeping the old wording. This commit sets the no-route message to the accurate v0.14 / upgrade-guide wording, updates the exact-text pin, quotes it at
connections.md:119, fixesChanged in 0.13→0.14atconnections.md:187, and declares the change underInterfaces changed.Closes #776
Part of #698 (panel round 1 on PR #777)
🤖 Generated with Claude Code
https://claude.ai/code/session_01Hb9U9A7Zu9ypdHTsDvdScP