← 패치노트

수정 2026-10-02

v1.36.296

2026-10-02

핵심 변경

Review control and UI review in deep project folders on Windows now stop with a clear code instead of a Git failure, and Leerness never writes a ref the user's own Git cannot read (T-0326). Every ref Leerness keeps for them (refs/leerness/<family>/v1/project-<64 hex>/<64 hex>: the authority floor, review runs, attempts, dispatches, results, proof use, provider observations, locators and review-control batches) has 166 to 176 characters. On Windows, Git without core.longpaths cannot lock a ref whose lock file path reaches 260 characters, nor read one whose file path does (measured with Git for Windows 2.54.0: update-ref works up to a 259-character lock path, a read up to a 259-character ref path), so with an ordinary .git the first calls failed from a 73-character project folder and every one from 88, as git_failed or publication_uncertain. Before such a call, Leerness now asks the user's own Git to read a ref name of the same path length that Leerness never creates (git symbolic-ref -q, which writes nothing); if Git cannot, the call fails git_long_paths_required before Git runs and writes no ref. Because Git answers itself, this follows how it combines configuration levels, the reftable format and the path spelling it uses. With core.longpaths on (git config core.longpaths true; in Git for Windows 2.54.0 a global false wins over the repository's true) those calls work. Leerness adds no long-path option to its own Git calls: measured with the same Git, the user's own Git without core.longpaths cannot read a ref written that way, and git log --all (exit 128), git fsck (exit 2), git gc, git repack -a -d and git prune (exit 128) and git maintenance run --task=gc (exit 1) then fail in that repository. Turning core.longpaths off again later brings those failures back for refs already written. In a narrow range of folder depths, where some families fit and a longer one does not, a flow can still stop partway, now with this code. Other platforms are unchanged.
Found while running the full test suite from a temporary folder 11 characters deeper than usual (the authority-floor suite failed 13/13 and passed from the default folder). Tests, with the user's global and system Git configuration replaced by test files: in a project deep enough that every ref is past the limit, an authority-floor initialization and opening (scripts/ui-review-authority-floor-probe.js), a review-control claim and read (scripts/review-control-probe.js) and a UI review run observation (scripts/ui-review-run-probe.js) are refused on Windows without changing any file in the project, and work once core.longpaths is on in the repository (the first two) or in the global configuration (the third), after which the user's own git log --all and git fsck succeed (and git gc for the floor). At the exact boundary, without the setting, a claim whose lock path has 259 characters (with a capital dotted I in the folder name) works, one whose lock path has 260 is refused without writing a ref, and a claim whose ref path has 259 and lock path 264 characters, written with the setting on, can still be read but not advanced. With a global true and a repository false, and with a global false and a repository true, a claim is refused exactly when the user's own Git cannot lock a ref of the same length, and what is written is readable by that Git. A reftable repository in a deep folder needs no setting, a path given to ls-tree after -- is not taken for a ref, and a damaged control ref in a deep folder with the setting on gets the same code as in a shallow one. A provider observation whose ref alone is past the limit (scripts/openai-ui-review-observation-probe.js) is refused with this code rather than publication_uncertain, and a later GET publishes it once the setting is on, without a second POST. On other platforms every case works without the setting. docs/review-control-api.md lists the new code and the limits.
lesson drop and decision drop no longer refuse entries whose text the Markdown archive could not carry, so a lesson saved with surrounding spaces, only spaces, several lines or a tab can be removed and restored (T-0328). The archive is one line per field with control characters made spaces, and its reader trims, so such entries failed the lossless-archive check (archive_lossy) and stayed in the store for good, although lesson save keeps text as given. The owner chose exact preservation: an entry whose known fields hold a string (or null) the plain lines do not give back gets one more archive line right after its heading, - Exact JSON: {...}, holding all its known fields; DEL, C1 controls and the line and paragraph separators are escaped as well. Its plain lines follow as before for other readers, and entries the plain lines carry get no extra line, so existing archives and ordinary drops keep their bytes. memory restore reads such an entry from the exact line alone and brings it back exactly; a cut that removes the line also removes the plain lines after it. Both checks run on what a restore reads, the archive as stored in UTF-8 (a lone surrogate would become U+FFFD) and the selected block trimmed. A selection is refused as archive_entry_invalid without writes, also when its other entries are intact, when any heading section in it gives no entry: an exact line that is damaged or cut off at the marker, an exact section that differs from what the writer wrote for its entry (an edited heading or plain line, or the next entry's lines after its heading was removed), a plain section holding two entries, an entry that lost its lines, an exact line that lost its heading, or a section that is not an entry. Before, such a section was left out while the whole drop block was removed. This changes older archives with a ### Notes section added by hand inside a drop block, or a note line starting with - Exact JSON: the restore now stops and names the repair (move the notes out of the block, rename the line). A bare ### (an empty date or title whose trailing space an editor stripped, or a cut right after such a heading) starts its own section instead of joining the entry before it. A block with an exact line is read from it when the section matches what the writer wrote, line endings and trailing spaces aside; a plain decision block headed "Template" is still skipped. Hand-edited or cut archives are protected only in the tested cases: a cut right after a decision's heading still restores a heading-only decision, an indented exact line is read as plain text, and a cut that removes whole entries or falls inside a heading line cannot be noticed (the archive does not record how many entries a drop removed); T-0331 closes this class for new drops with an integrity record (below). Untouched drops of several entries whose non-final entry ends with an NBSP or another space JavaScript trims restore exactly (they were refused during review round 4). Conflict checks read the entries the restore will write, rendered like the active side, so a cut after an exact line no longer hides a duplicate; for decisions they keep titles starting with "Template", so an undated "Template x" decision, which now drops through its exact line, is not restored over an active one without --force. A drop by a target containing quotes is selectable, listed by memory archive list and counted by memory status, health and session close (one header reading for all of them and brainstorm). Unknown fields and values that are neither strings nor null are still refused. T-0193's two tests that pinned the refusal of multi-line values now check drop and exact restore instead.

GitHub 릴리스 v1.36.296 →