v0.36.0
feder-cr/AIHawkv0.36.0Sep 9, 2026by github-actions[bot]
AI Summary
v0.36.0 refactors the monolithic 3370-line `web.py` file into separate, modular files for CSS, JavaScript, HTML, and Python logic. The release ensures the refactored output remains byte-for-byte identical to the original, fixing line ending issues and improving JavaScript regex handling.
Key Highlights
- Split monolithic `web.py` into separate files (`ui/app.css`, `ui/app.js`, `ui/page.html`, etc.)
- Validated refactored output is byte-for-byte identical to the original
- Fixed CRLF vs. LF line ending issues and regex handling in JavaScript
- All 541 existing tests pass with 5 new validation gates added
New Features
- Modularized `web.py` into separate CSS, JS, HTML, and Python files
- Improved JavaScript regex handling by moving code to a `.js` file
- Fixed CRLF vs LF line ending handling issues
Full Release Notes
`web.py` was 3370 lines. It held three languages inside a single Python string - 1023 lines of stylesheet, 1399 of script, 165 of markup - and three unrelated jobs beside them: one conversation, the registry of all of them, and every route. No editor, formatter, linter or diff ever saw any of the three languages as what it was, and every change to any of the six had to walk through the other five. ui/app.css the stylesheet ui/app.js the script ui/page.html the markup, with a comment where each of the other two goes ui/__init__.py assembles them, once, at import chat.py one conversation sessions.py the registry, and the refusal for an id nobody declared routes.py the HTTP surface web.py 41 lines that re-export all of it Nothing moved. That is not a claim, it is the check the whole thing rests on: the assembled page was compared against the bytes of the string it replaced and is identical, character for character - so the dozens of gates that read `PAGE` as text kept passing untouched, the running server hands out the same bytes, and nothing on the screen could have changed. A refactor whose output differs by one character is not a refactor. Everything is still imported from `aihawk.web`, because that is where the product, the tests and the gates have always found it. Moving where something lives is not a reason to move where it is found. Two things the split turned up. The extraction, done byte for byte, produced CRLF files whose page was 2592 characters longer than the original - one per line - because Python reads its own source with universal newlines and a file is read as it is. The fold is now its own function, `ui.lf`, because a gate that reads the files cannot prove it: on a checkout where they are already LF, removing the fold changes nothing and the assertion passes on a broken reader. Measured: that mutation survived until the gate stopped reading files and started calling the function. And one class of bug went with the split. The script used to live in a raw Python literal, so every backslash in a regular expression was one careless edit away from being read by Python instead of by the browser. A `.js` file has no such reading. Five new gates, three known-bad mutations, all killed. 541 tests pass. The wheel was built and its three files were assembled back into the same page, because they are not Python and nothing that runs from a checkout would notice them missing from a build.