Repository navigation
v0.23.0
Added
-
TangleBrain now tells you when the v0.21.0 state move left your usage log short
(#197). Every command checks, at
startup, whether~/.cache/tanglebrainstill holds usage records the current log does not, and
says so on one line of stderr if it does. The fix for the underlying race shipped separately; it
stops the truncation happening again and does nothing for a store where it already happened,
which had no signal at all — the re-run guard is "does the destination exist", so no later run
ever retried a short file, and the lifetime spend-avoided figure understated in silence.This release only reports. It does not repair, and it never writes to your data. Putting the
legacy records back means merging them underneath everything logged since the move; a copy
would delete that history. Repair is therefore an explicitly invoked command with a dry run, in a
later release. Until then the one thing to do is what the notice says: keep
~/.cache/tanglebrain. It holds the only copy of the missing records, and nothing can be
recovered from it once it is deleted.Records are compared, never file sizes — after the move the current log grows on every run while
the legacy one is frozen, so a log truncated at migration and used for a week is larger than the
file it is missing records from, and a size check calls that healthy. It allows for compaction
too, which folds the oldest rows out of a healthy log and would otherwise make a heavy user's
intact store look robbed; where a fold has gone deep enough that the two cannot be told apart, the
notice says so rather than claiming data loss.What it costs, and two things it deliberately does not do. An intact store whose log has not
folded past the migration boundary costs two small reads, independent of log size; after such a
fold every command pays a scan, permanently, and an inconclusive notice repeats on every
invocation with no flag to silence it — suppressing it would mean remembering it fired, and
remembering means writing to the store. A legacy root you have already deleted cannot be
reported on at all, being indistinguishable from a machine that never had one. And a legacy log
that cannot be read says that, rather than letting an unreadable file look like a clean bill.The full reasoning — why each of those holds, and what the detector rests on — is in
docs/design/observability.md§ Migrated-log integrity, so it stays in one place as the detector
changes.
Changed
-
The knob panel is two views behind a sidebar, not one scrolling column (#166).
Chat holds the run box; Settings holds the spend-avoided rollup, the roster table and the
pricing reference. Views are addressable (#/chat,#/settings), so the browser's back button
and a bookmarked view both work.The single column was the right shape for a read-only dashboard and is the wrong one for what
comes next — a chat feed, roster modals, execution traces. Nothing about what the cards contain
changed and no endpoint moved; this is layout, and the panel is still one packaged file served by
a stdlib handler with no framework, no CDN and no build step, so it still renders with the machine
offline.The nav is the panel's first interactive navigation surface, so it ships keyboard-operable: real
anchors, a visible focus ring, a skip link, andaria-currenton the active view rather than
colour alone. The router owns the#/prefix and nothing else, so a plain in-page anchor — the
skip link is one — moves the fragment without moving the reader out of the view they are in.
Fixed
-
tanglebrain-gui --helpcalled the panel "read-only". It has not been read-only since the
pricing and roster cards became editable — it writespricing.yamlandroster.yaml. The help
now says what the panel actually does. -
A security report had nowhere correct to go.
CONTRIBUTING.mdforbade opening a public issue
for a vulnerability and pointed at the contact inCODE_OF_CONDUCT.md— which is the
conduct-enforcement address, offered there for harassment reports. A reporter who followed the
trail landed on the wrong channel; if they guessed it doubled as security, the report arrived as
plain email rather than a private advisory, with no embargo and no acknowledgement path. Worse
than an empty pointer, because it looks answered. The private route now lives in the mirrored
security rules and that line points at it. -
Two console scripts starting at once could corrupt or lose the state migration
(#139). Every console script migrates
the pre-0.21 state root on startup, and each staged its copy under a fixed name — so both
processes computed the same staging path, and each could act on the other's file. Two
interleavings were possible, and the quiet one is the worse:- Loud. The second process fails part-way and cleans up the shared path, deleting the
completed copy the first had just staged; the first's move into place then fails on a file
that is no longer there. Both processes printcould not move stateand nothing migrates,
telling the operator their history is stranded and--statsmay read low, on a machine where
nothing is wrong. The next run does repair it. - Silent, and permanent. The second process's cleanup unlinks the first's staging file
while it is still being written, then creates a new one at that same path. The first
process finishes writing into the file it no longer has a name for, and then moves the second
one's still-partial copy into place. Nothing raises, nothing is printed, and the migration
reports success — but because the re-run guard is "does the destination exist", the truncated
file is never retried and understates the lifetime figure for good.
If you installed 0.21.0 or 0.22.0 and
--statsreads lower than you expect, compare
~/.local/share/tanglebrain/usage.jsonlwith~/.cache/tanglebrain/usage.jsonl. The migration
copies and never deletes, so the original is still there: delete the short file at the new path
and run anytanglebraincommand to migrate it again. If you also find a file named like
usage.jsonl.<hex>.tmpthere, it is an abandoned staging copy from an interrupted migration —
inert, read by nothing, and safe to delete.Staging now uses
atomic.staging_path, which is unique per process, so no process can reach
another's staging file at all. Cleanup moved into afinallyin the same change: a unique name
is unfindable by any later run, so an interrupt — Ctrl-C during the first-run migration — would
otherwise leave a staging entry in the data root permanently.Found and reported by @be-student, who also identified
atomic.staging_pathas the fix. As
CONTRIBUTING.mdexplains, external contributions are re-implemented rather than merged: the
diagnosis and the approach are theirs, the bytes here are ours. - Loud. The second process fails part-way and cleans up the shared path, deleting the
Internal
-
CONTRIBUTING.md's mirrored security rules now match upstream again. The Clean Room clause
said a reconstruction "credits you as the author", which pulled against the standard it sits in —
the whole point of the clean room is that we author the bytes. Upstream
Jason-Vaughan/.github
resolved it to "ensures you receive full credit and an @mention for your contribution (while
preserving the Clean Room distinction that we author the final bytes)", which promises the
contributor more, not less, and no longer contradicts the paragraph above it. This copy is a
byte-for-byte mirror, so it follows rather than diverges. One sentence; no other line moved. -
The weekly CI canary's comment now names both extras it resolves —
".[delegate,dev]", not
".[delegate]". The job runsmake test, which builds the venv frompip install -e ".[delegate,dev]", so the canary has always resolvedruffandmypytoo and would catch an
upstream break in either. The comment understated its own coverage in the direction that costs
something: a reader deciding whether the dev toolchain needs a separate watch was told it was
uncovered when it was not, and the likely result is machinery built to solve a solved problem.
Comment only; no workflow behaviour changes. Closes
#146.Credit to @7487, who found the same discrepancy and opened
#167 against it. That PR was not merged
because external contributions are re-implemented rather than merged and because it modified
.github/workflows/, a boundary this project enforces — a boundary that, as it happens, was only
published inCONTRIBUTING.mdearlier today. The observation was theirs; the bytes here are ours. -
CONTRIBUTING.mdnow mirrors the full seven-point security checklist from
Jason-Vaughan/.github,
replacing the five-bullet condensation that predated it. Contributors are now told, in the
project's own words, that CI workflows and build scripts are off-limits, that no new dependency
may be added, and that obfuscated content is an immediate ban — three rules this repo already
enforced without publishing.Extracted programmatically and verified byte-identical between the
mirrored-security-rules
markers, so the drift checker reads what upstream wrote. Two repo-specific lines the upstream
block does not carry — the vulnerability-reporting route and the pointer back to the full guide —
moved outside the markers rather than being dropped: inside, they would have broken the
byte-identity the mirror exists to guarantee. -
CONTRIBUTING.mdcarries the account-wide security rules, byte-identical to the copy in the
other repositories and extracted programmatically rather than retyped, between the
mirrored-security-rulesmarkers a drift checker
(.github#1) reads.A repository shipping its own
CONTRIBUTING.mdinherits none of the account-wide one —
GitHub's community-health fallback is per-file and all-or-nothing — so without this the standards
reached every repository except the ones that take pull requests.