v0.68.2

feder-cr/invisible_dotsv0.68.2Sep 16, 2026by github-actions[bot]

Upgrading?

AI Summary

This release consolidates result text handling by introducing `link.answer_of` to unify logic between the agent loop and routes. It also enforces a structural gate to prevent multiple readers of error flags and fixes a bug where `Conversation.known` could raise an AttributeError. Additionally, it corrects outdated comments and reorganizes stylesheet files to maintain the exact byte-for-byte assembly of the page.

Key Highlights

  • Introduced `link.answer_of` to unify result text reading between the agent loop and routes.
  • Enforced a structural AST gate to prevent multiple readers of `isError` or no-text literals.
  • Fixed `Conversation.known` initialization to handle cases where tool definitions arrive differently.
  • Corrected comments in the present tense that referenced code no longer present.
  • Reorganized stylesheet files to maintain exact byte-for-byte assembly of the page.

New Features

  • New internal function `link.answer_of` for unified result handling.
  • AST structural validation to prevent drift in wire format readers.

Full Release Notes

`link.text_of` carried a docstring saying it was "shared with the agent loop rather than written twice: a tool result is read in two places now, and two readers of one wire format drift". It was not shared. `agent._result_text` held the same five lines, the `[non-text result]` literal included, and imported nothing from `link`, and `/live/frame` was a third reader spelling out `getattr(result, "isError", False)` for itself beside a call to `text_of`. Three readers, two of them with their own tests, so either copy could have moved alone and stayed green.

`link.answer_of` answers both halves now, and the loop and the route read it. The name is not `said`, which is already a function in `agent.py`, the name of an event the page draws, and a local in two routes. The tests that held `_result_text` moved with the function.

The gate that keeps it that way is structural rather than nominal, because the surface gate added in 0.67.0 cannot see this class: a duplicated reader is named in `src` and answers yes. It walks the AST and asserts that only `link.py` reads `isError` or the no-text literal, so a comment can neither trip it nor satisfy it. Four known-bad inputs, all killed, including `link` no longer reading the flag at all, which would otherwise have left the first assertion vacuous.

Two smaller things in the same branch. `Conversation.known` was the only attribute of that class not declared in `__init__`: it was assigned inside `if self.tool_defs is None`, so a conversation whose definitions arrived any other way raised AttributeError on the line that tells a model it asked for a tool nobody has. It is read off `tool_defs` now; an empty list in `__init__` would have been worse, because an empty list is a legal answer that refuses every tool quietly. And two comments named code that has not existed for weeks in the present tense, `registry.py` in `mcp/server.py` and `registry.peek` reached from a `looking` helper in `link.py`, the second inside a paragraph that opens by warning about stale reasoning. The past-tense mentions stay.

Separately, `07-fleet.css` went in 0.53.0 and left the stylesheet folder reading 01 to 06 and then 08, in a module whose docstring says the order is load-bearing. `08-stage.css` is `07-stage.css` and the assembled page is byte for byte what it was.

0.68.2 because six files that go into the wheel changed and 0.68.1 is already on the index, which `test_version_is_not_taken` refused until it was bumped.

Verified locally: 703 passed, ruff clean, `invisible_core.english` clean, `check_content.py` clean, the version gate green against the real index, and the wheel builds at 0.68.2 with the renamed stylesheet inside. Both opt-in suites still collect.

Related Repositories