Repository navigation
Answer the Ubuntu install feedback: init, service install, show, --redact, subagent labels, 0.2.0 - #30
Open
doronp wants to merge 13 commits into
Open
Answer the Ubuntu install feedback: init, service install, show, --redact, subagent labels, 0.2.0#30doronp wants to merge 13 commits into
doronp wants to merge 13 commits into
Conversation
The README's install line did not install on Ubuntu 24.04, whose own Python is 3.12, and the same line with `--python 3.13` did. pyproject requires >=3.13, but no install command said so. Left to choose, uv can settle on the older interpreter and refuse (it does with Python downloads off), and pipx always installs into the Python it runs under. Every install command with a source argument now names the floor: the README quickstart and [serve] example, the skill's setup step, hook/README.md, the plugin description, and the derive extra's error hint. pipx survives as a README footnote that names a 3.13 interpreter. Where uv fetches Python only when asked, the README says to run `uv python install 3.13` first; where it never does, to put a 3.13 on PATH, because `uv python install` is refused there too. The floor itself is unchanged; DESIGN.md section 1 stays as locked. test_every_documented_install_names_the_python_floor reads requires-python with tomllib and fails on any tracked install line with a source that lacks the floor, spelled `--python X.Y`, `--python=X.Y` or uv's `-p X.Y` (pipx: `pythonX.Y`). A command ends at its code span, a `)` or `#`, the end of a line not continued by a backslash, or the next command on the same line, and the test checks that scanner on those cases before it scans. Prose with no source, such as hook/README.md's "if you installed with `uv tool install`", is not a command and is skipped. The README's test counts move by one, and docs/REPRODUCE.md's expected counts, stale since 1,027, now read what a checkout collects: 1038 offline, 1363 with the corpora, 328 conformance cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
The plugin and the CLI are installed separately, and nothing makes them one release: Claude Code keeps the plugin at the commit its marketplace was cloned at until its version changes, while `uv tool install` takes whatever commit it is given. A skew has shipped already, silently: v0.1.0's recall prints no date column and ranks by relevance, the skill decides which of two lines is current by date, and both say 0.1.0. - `gitmemory --version` prints `gitmemory <__version__>` through argparse's version action (the constant had no reader). Every CLI before it exits 2 with "the following arguments are required: cmd", measured under Python 3.12, 3.13 and 3.14. - The recall skill names the release it is written for (0.1.0, today's pyproject version), runs --version once per session and says when the CLI is older. Its first "When it fails" bullet reads exit 2 with `invalid choice`, or `unrecognized arguments` naming a flag, as an older CLI: say which subcommand or flag is missing and never retry without it, so a later safety flag cannot degrade into its absence. `unrecognized arguments` naming only plain words is a topic left unquoted, not an older CLI: quote it and search again. --version joins the read-only allow-list, and its call joins the echoes: every session that uses the skill now records one, and on a query about versions those records outrank the real hits at the default -k 10. - The skill-commands test accepts `gitmemory --version` with no subcommand and looks for the flag in the top-level help. A new test pins the release the skill names to pyproject and __version__, the --version output, the argparse phrases the skill quotes, each got from the CLI, the unquoted-topic case, and the --version echo. - hook/README.md says how to pin both to one tag, with the install line naming --python 3.13 like every other. CONTRIBUTING.md gets the bump rule, `uv lock` included: uv.lock records the project version, and `uv lock --check` fails after a pyproject-only bump. It also says to move CHANGELOG's [Unreleased] and to tag the bump `v<version>`, which the pin advice names. - The new test moves the README's counts and docs/REPRODUCE.md's expected counts by one, to what a checkout now collects: 1039 offline, 1364 with the corpora, 328 conformance cases. No version bump here; the release commit makes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
A subagent's first turn is user-role text that the parent agent wrote: its dispatch prompt. The default policy counted every user/text block as the user's own words, so a dispatch prompt that was newer than the user's rule, and matched it, led recall ahead of that rule. That is the forgetful-agent failure latest-user exists to prevent, arriving through a subagent. The index already kept the per-turn isSidechain flag in `turns`; neither Hit nor search() could see it. - Hit.is_sidechain, read in search() by a primary-key lookup on turns. No schema bump and no reindex, and search() order is unchanged, so no benchmark number can move. Timed within noise on 120k turns. - latest_user no longer admits a sidechain turn as the user's words. derive._prose still does; changing it would move the secondary-set numbers, and the docstring says so. - recall prints subagent:<role>/<kind> for such a turn, in the same column, so split()[3] is still role/kind and main-session lines are unchanged. The mark is per turn, so old-format sidechain lines inside a main file are labelled too. USAGE.md gives a main_only policy that drops them at read time. - docs/watching.md states the pattern's glob semantics and adds a main-sessions-only recipe, pattern = "*/*.jsonl", with its costs: those turns are never captured, are lost once Claude Code cleans up old transcripts, and drop out of dashboard spend. The brief survives in the parent, and so does the report unless a background agent's or workflow's session ended first; a workflow's journal is left out. The recipe must replace the default table, since a second table on the same root still finds every subagent. A doc test runs the documented block and pins both claims. No exclude key: the existing pattern key already says this. - Three mutation rows, so 606 becomes 609 negative controls. On top of the install and --version commits, the suite is 1,043 tests offline (1,368 with the corpora). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
Setup meant writing TOML by hand: no command wrote config.toml, and with none the watcher logs "watching nothing". `gitmemory init claude-code` (or `pi`) now adds that agent's documented root to the config. Watch roots still have no default. daemon.ROOTS is read only by init, and only because the user named the agent; load_watches reads the file and nothing else. It appends and never changes a byte already there, because config.toml also holds the [remote.*] push opt-ins and a rewrite would revoke one. Same agent and root already watched: exit 0, nothing written. It refuses with exit 1, writing nothing, when the file is a symlink, has another hard link (the rename would leave the other name holding the old bytes), or is not a regular file; is not valid TOML; has a `watch` a [[watch]] table cannot join; already watches the root under another agent (discover gives a shared root's files to the first watch); or holds a table the watcher would skip. That last check runs the watcher's own rules, split out of load_watches as watches_from, so the refusal is the watcher's sentence and cannot drift from it. Usage errors and a config it could not read stay exit 2: an unreadable file is never taken for a missing one and replaced. Since init now prints the watcher's sentence as its reason, the store check gets one per direction: a root inside the store "is inside the store" rather than "contains the store itself", which was wrong there. The write goes through store._write_atomic, so a running watcher reads the old file or the new one. A killed init can leave config.toml.tmp.<pid>.<tid>, a full copy of the file, at the store root, and the watcher's `git add --all` would commit it, so /config.toml.tmp.* is gitignored. Docs: README quickstart and command table, docs/watching.md, docs/USAGE.md, hook/README.md, ARCHITECTURE.md. Skill setup step 2 tells the user to run init, after the older-CLI bullet and the `--python 3.13` install line already there, and init joins the commands the skill must not run. The plugin description adds the configure step beside the pinned install command. The doc test reads the table under watching.md's "## Configure what to watch", which ends before the main-sessions-only recipe's own toml block. Eight mutation rows (609 -> 617); suite 1,043 -> 1,063 offline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
… the watcher The watcher is the only thing that captures, and nothing kept it running: the docs said "no installer" and left the unit to the user. A hand-written unit falls into three traps, each reproduced while designing this. The interpreter's realpath is outside the venv and cannot import gitmemory. `python -m` from a unit's working directory ($HOME) runs a stray ~/gitmemory.py instead of the package. And a user service sees neither the shell's GITMEMORY_HOME nor its PATH, so it watches another store or misses git. `gitmemory service install` (new module service.py) writes the facts only the running process has into one file: sys.executable as given, with `-P -m gitmemory`, an explicit `--home <store>`, and PATH from git's directory (made absolute) plus /usr/bin:/bin. It writes a systemd --user unit on Linux, under $XDG_CONFIG_HOME only when that is absolute and otherwise ~/.config, and a LaunchAgent plist on macOS. It refuses (exit 1, O_EXCL) to overwrite an existing file, in the one-line `refusing: ...` shape `gitmemory init` uses, through the same helper. It exits 2 on another platform, with no git, when it cannot tell which Python is running, and for a store path with a line break, which a unit cannot hold and a plist would turn from \r into \n. It prints the start, check, log, upgrade and remove commands and never runs systemctl, launchctl or loginctl, because enabling it is the user's step, as installing the hook is. The removal is `stop; rm`, not `stop && rm`: `launchctl bootout` of a job written and never started exits 3, and the refusal's way out has to remove the file anyway. Tests run the file's own command line under the manager's conditions (cwd $HOME holding a decoy gitmemory.py, env HOME plus the file's PATH) for both backends, and run the printed removal against stand-in managers that fail. The unit's quoting is read back strictly, as systemd would, so an undoubled %h or $HOME or an unescaped backslash fails the test. Six mutation rows (O_EXCL, -P, realpath, the % and $ doubling, the removal's `;`) are caught by them. Docs: watching.md "Keep it running", USAGE step 2, README table, ARCHITECTURE (whose CLI list now names init and service), the skill's setup steps (install, `gitmemory init`, then `gitmemory service install`) and its do-not-run list, a dated DESIGN.md amendment naming the systemd user unit, CHANGELOG, and the pinned counts, re-measured on top of install, version, subagents and init. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
recall checked nothing about capture, so a stopped watcher was invisible from it: every search answered from the store as it was when the watcher stopped, with an empty stderr. The time since the last capture cannot tell that from an idle machine, because a running watcher on an idle machine captures nothing either. The spool can: every watcher pass drains every complete record, covered or not, so a record an hour old means no pass has run on that store since it arrived. daemon.STALE_RECORD and a read-only daemon.uncollected() count the spool's complete records and the age of the oldest, by drain_spool's own rule (a dot-name is a hook still writing). A directory is not counted either: no pass can unlink one, so it would warn for ever with a watcher running. recall prints one stderr line before the results when that age passes the threshold, naming the store, so a hook and a watcher on different stores show up too. Exit code unchanged, and the subagent label on result lines is untouched. Skipped under --db, whose index names no store, and silent on any store the hook does not ring: capture-only stores, tests and bench. The skill quotes the line and tells the user to start `gitmemory watch` and keep it running with `gitmemory service install`; it runs neither. It does not rule out a reindex, which still finds what the watcher captured before it stopped. docs/watching.md names the other causes: a service that is installed but not started or failing, one whose --home is another store, and a watcher that runs passes less often than hourly. Three tests, seven negative controls (623 -> 630), and the README and REPRODUCE.md counts, measured. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
recall stops each turn at 160 characters and hands back an address, but nothing resolved one: the rest of the turn was segment arithmetic done by hand under raw/, which the recall skill spelled out for the agent. `gitmemory show <agent>/<session>/gNN@<offset> [-C N | --context N]` reads the store, not the index, so it needs no index and a stale or tampered one cannot choose what it prints. It parses the generation the way `index` does, so a turn cut by a segment boundary comes back whole, and an offset inside a turn resolves to that turn with its own start in the header. A subagent's turn is headed subagent:<role>, because its user text is the parent agent's brief, not the user's words. recall and show now share one _date() helper, and recall's line keeps its subagent: label in the role column. The address is input. Its names go through store.identity() before anything is read and are only compared with what sessions() found, so it never becomes a path; `[0-9]` keeps int() from reading Arabic-Indic digits. Errors name the address as it was typed, masked, since a session id can be a token: identity() lowercases, and a lowercased AWS key id is one no detector matches. Exit 2 for a malformed address or not a store, 1 for an address the store does not hold or two manifests claiming it. Only header lines start in column 0: tool names are whitespace-collapsed and text is split on "\n" only, which closes the two header forgeries the prototype reproduced. -C rejects a negative count, which plain type=int turned into empty output and exit 0. Eight mutation rows cover the guards (638 rows). README, USAGE (with a full example address), the recall skill (show first; for a CLI older than show, tell the user as the older-CLI rule says and read raw/ by hand; show in the read-only list and the --home sentence; and a captured show result listed as an echo, since it holds the whole turn dated later than the turn), DESIGN 2.8, ARCHITECTURE and CHANGELOG are updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
…al shapes Through the plugin's skill, recall and show output goes to the model provider, and both printed transcript text as written: an AKIA key, a private key, a session id that is a token. The redaction gate stands at push only, on purpose, because a store on the owner's own disk is not an egress. So this is opt-in masking, not a gate: `--redact` on recall or show, or GITMEMORY_REDACT set to anything but empty or 0, masks both tiers' matches in the printed text and in the address. It refuses nothing, and the default is unchanged. redact.mask() is safe_path's span masking with one widening: private_key_block matches only the BEGIN line, so masking the match printed the base64 key under a masked header. Its span now runs to the END line, or to the end of the text. Only a header no earlier span covers searches for its END, and the END pattern's runs are bounded: a page of headers with no END, or one END line that never closes, made the widening quadratic (a 2 MB tool_result took recall --redact 41 s; it now takes 0.28 s). Spans are joined once rather than with `bytes +=`. safe_path delegates to mask(). The index stores records.safe_text's spelling of a control or invisible character (``, ``, `\x7f`), and each spelling ends in a word character, so a key right after one lost the `\b` its detector needs while the push gate, reading raw bytes, still found it. mask() also scans a copy with each spelling blanked to as many spaces and masks the union. recall masks after the whitespace collapse, which can join a PEM header a newline split, and before the 160-character cut, because a key straddling the cut leaves a prefix no detector matches (graph._label measured both). The masked address drops the marker's space, so the line keeps its columns, and the subagent mark stays in the role's field. show masks each block whole before it splits it into lines, because a private key's body is the lines under its header, and masks the header's address and a tool's name too. It keeps the line breaks recall flattens, so it asks mask() for a third scan, `collapse=True`: the blanked copy with every whitespace run one space, its matches mapped back to the text's own offsets. recall does not ask for it, since its text is flattened already. The flag lives in a _redact_flag helper that both commands take. conftest clears GITMEMORY_REDACT, as the docs tell the owner to make it standing in settings.json. The skill passes --redact to recall and show whenever GITMEMORY_REDACT is set and not 0, so an older CLI fails with an argparse error instead of printing unmasked, and while masking it never reads a turn from raw/, and says a masked address is not one show can look up. SECURITY.md and USAGE.md list what it does not cover. DESIGN.md's "exactly two boundaries" gets a dated amendment, and its stale detector count (nine and three) is corrected to twelve and four. Twelve tests and sixteen mutation rows (recall: cut, collapse, PEM widening, address and its one column, env parsing, the spelled-control scan and its union, the linear widening, the bounded END pattern; show: whole blocks, address, its one field, tool name, the collapsed scan and its offset mapping), each caught by its intended test. README: 1,119 tests, 654 negative controls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
`watch` logged `captured 470 +43025169B commit=...` and `capture` printed `<manifest> +6202B size=11550 g00`, so a person had to count digits to know how much a pass took. Both now say the amount through `daemon.size_text()`: `+392.0 MB (391995999 B)`, or `+N B` under 1000 bytes. The units are decimal, so the rounded figure and the exact count in parentheses can be checked against each other by moving a point, and a script doing byte arithmetic still has the count. `size=` stays plain bytes: it is the end of `[0, size)`, the coordinate `verify` and every `@offset` are stated in, not an amount. The git commit subject keeps `+NB`, because it is the durable record and the history already written says it that way; the watch-line test pins that too. Three mutation rows, each caught by its named test (657 in all): the watch line and the capture line reverted to `+NB`, and the 999.95 guard loosened so 999950 B reads "1000.0 kB". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
A store came to 1.75x what it had captured (684 MB for 392 MB), and no document said why. ARCHITECTURE.md said the store "holds each byte the agent wrote exactly once", and DESIGN.md's first consequence of appending was "Zero duplication", which are true of raw/ only: .git keeps a second, zlib-compressed copy (gitrepo.init says so), and index/ holds the searchable text a third time, uncompressed, beside its FTS5 postings. docs/watching.md gets a "How much disk it takes" section: budget about twice what you capture, what each directory costs, a gzip one-liner that estimates .git for a store holding what is under the Claude Code root now (and what that count leaves out), and the measurements behind the advice (du -sk on synthetic 470-transcript corpora, 2.3x for prose and code, 3.4x for base64 tool output, 1.2-1.5x for the part that cannot be rebuilt). It says index/ is the part to delete for space, because gitmemory index rebuilds it from the store, and that recall and dashboard refuse until it is rebuilt. It also says a rebuild needs about twice index/'s usual space while it runs: index.build writes the new database beside the old one and renames it into place, measured at 1.92x of capture steady and 3.82x just before the rename. So when space is tight, delete index/ first. The one real store is a reported data point, not proof of why real transcripts index smaller. The README points at the section, ARCHITECTURE.md scopes "exactly once" to raw/, and DESIGN.md gets a dated bracketed amendment. The ratios are not pinned in tests: they move with block size, git version, gc timing and corpus. The new doc test pins the advice instead. Deleting index/ is safe only while the store's .gitignore ignores index/ and tracks raw/ and sessions/. It also checks that the README's, ARCHITECTURE.md's and DESIGN.md's anchors resolve to a watching.md heading, reading headings with fenced blocks cut out so a `# comment` in a recipe cannot stand in for one. On this branch the section also points at the main-sessions-only pattern as the way to keep subagent and workflow transcripts out of the store, with its cost, and says that `show` reads the store and keeps working while index/ is gone. The new doc test moves the README count to 1,129 and the corpus arithmetic to 1454 and 1457; docs/REPRODUCE.md's collect lines are re-measured to match. No mutation row is added, so 657 stands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
Claude Code keeps an installed plugin's copy until the plugin's version changes, so the recall skill, gitmemory show, --redact and the other changes under skills/ and hooks/ since 0.1.0 reach nobody who installed the plugin until the version moves. This bumps it in the five places CONTRIBUTING names, in one commit: pyproject.toml, uv.lock, __version__, .claude-plugin/plugin.json and the release the recall skill says it is written for. The tests pin the first four equal. CHANGELOG's [Unreleased] becomes [0.2.0] - 2026-10-18 with a compare link from v0.1.0, and [Unreleased] now starts at v0.2.0. The CHANGELOG and hook/README.md text that said the skill would arrive with the next release now says, in the past tense, why 0.1.0 installs lacked it, and that 0.2.0's bump brings it on update. DESIGN.md 2.8's "As built" said "the CLI only"; it now names the plugin's recall skill too, as a search the agent runs rather than an injection, so the no-injection statement stays true. The plugin.json description is cut from 620 to 449 characters, keeping the install command at --python 3.13, which the install-line test requires. Not tagged and not pushed: tag v0.2.0 once this is on main, since hook/README.md tells users to pin both installs to the tag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
Kimi K3 reviewed each item commit of the round on its own, against main. Every finding was then re-checked against the integrated branch, with a live probe where one could run. Of 53, 21 led to a change, 6 are true and declined, and 26 do not hold; several of those came from seeing one commit without the rest. docs/reviews/install-feedback-round.md records each one with its evidence. The one behavioural fix: two `gitmemory init`s at once read the same config.toml and the later rename dropped the other's table, while both said "added" (46 of 60 concurrent pairs, measured). init now re-reads the file under a lock in .locks/ just before the rename and refuses, exit 1, when it moved; 0 losses in 60 since. Both reads go through one helper, because the re-read alone also refused an unreadable file, and the existing control for that guard had stopped biting. One new negative control (658), and watching.md and the CHANGELOG say what is and is not covered. User-visible wording: the stale-spool line now says "N hook record(s) uncollected, the oldest for <age>", since the age was only the oldest record's (code, skill, USAGE, watching.md and tests together), and the --redact help says empty is off as well as 0. Tests that now hold what they claimed: the install-floor scan reads a command split across code spans; REPRODUCE.md's counts are held to the README's (a new test; with init's, the suite is 1,131); the disk section's delete advice is checked against the store's .gitignore, and CHANGELOG's anchors are checked too; the init temp test asks git about the writer's real temp; the PEM-header linearity test counts searches instead of timing them; a key inside a PEM body is pinned; the _ago 2-hour and 2-day edges are pinned; a daemon test no longer depends on /tmp/x being absent. Docs and skill: show's echo rule matches the header's shape; the skill says to search again with -k 50 when subagent lines crowd the results, and not to list sessions/ for a masked address; USAGE says the service starts the watcher at every login and names linger; "about twice" the capture is bounded for transcripts that compress poorly; the stale suite sizes in mutate_index's docstrings are dated rather than re-pinned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
Kimi K3's final pass, over the fix commit and the skill as it now reads whole, and an Opus gate of five lenses over the whole branch, with two refute-by-default skeptics per finding, raised 33 findings between them. This answers them. The record is the "Final gate" section of docs/reviews/install-feedback-round.md, which also corrects show-7 and moves service-3 to fixed. Four changes of behaviour, each with a test that failed first and a new mutation row: - show --redact no longer stops with exit 2 partway through a turn that holds a lone surrogate. mask() encodes with surrogatepass, as records hashes; surrogateescape round-trips only U+DC80-U+DCFF. - recall --redact masks as show does, with the mask's own collapse, and flattens and cuts after it. str.split() broke a value at a no-break space before the mask saw it, so recall printed what show and the gate flag. A header a line break split is still joined. - show refuses a masked address with a line of its own, still exit 2, not with "as recall prints it" about what recall printed. - The printed systemd removal ends with daemon-reload. Two simplifications: init refuses in the except of its re-parse and drops `have`, and uncollected() loses a clock nothing passed. Docs: the --redact caveat list and the settings.json snippet are kept in USAGE.md only, and SECURITY.md and hook/README.md link them. The list now says the store lowercases a session id, so a credential-shaped one prints unmasked, and that a key split across two blocks is masked to the end of the first. The --redact sample comes from its own transcript. While masking, the skill reads nothing under the store by hand; it puts -- before a topic that starts with -, and states each rule once, with one retry, -k 50. REPRODUCE 5b sets HOME as the suite does, so a generic account collects what the README says. The CHANGELOG, DESIGN.md, watching.md and the docs index follow. A2, the push gate missing a key right after a JSON escape, is on main and left for its own issue. Pins move to 1,134 tests and 662 negative controls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U Signed-off-by: doronp <3586743+doronp@users.noreply.github.com>
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.
Answers the feedback from a fresh install on an Ubuntu 24.04 VM: one commit per item, then a 0.2.0 bump and two review-fix commits. The commits can be split into separate PRs if you prefer.
The items
pipxroute59865e1docs--python 3.13, andpipxmoves to a footnote. A docs test pins every documented install line to the floor inpyproject.toml.beffcf2featgitmemory init {claude-code,pi}appends the standard[[watch]]table and never overwrites, becauseconfig.tomlalso holds push opt-ins. It validates through the watcher's own parser, writes atomically, and does nothing on a second run. Refusals exit 1.b39449afeatgitmemory service installwrites a systemd--userunit or a LaunchAgent, refuses to overwrite one, and prints the enable, check, log, linger and remove lines. It never runssystemctlorlaunchctlitself.5c6a874featrecallwarns on stderr when hook records have waited more than an hour. Time since the last capture can't tell an idle machine from a dead watcher; a waiting record can, because every watcher pass drains the spool.dba5db7featgitmemory show <agent>/<session>/gNN@<offset> [--context N]prints the turn from the store. The address is validated by the store's own identity rule and never becomes a path.656136cfixlatest-usercounted a subagent's dispatch prompt as the user's own words, so it could outrank the user's actual rule. That is fixed. Subagent hits are now labelledsubagent:<role>/<kind>, anddocs/watching.mdgives a main-sessions-onlypatternrecipe with its costs. No newexcludekey.recalloutput skips the redaction gateb9a5f57featrecall --redact/show --redact, orGITMEMORY_REDACT=1, masks the push gate's credential shapes in what is printed. This is masking, not a gate, and the docs say what it does not catch. DESIGN.md:212 gets a dated amendment.+391995999B3bb0afffeat+392.0 MB (391995999 B).size=and the commit subject stay exact.0434189docsdocs/watching.md: why it's above 1×, a measured table, and the one real store at 1.75×.56327cffeat,7a10f22gitmemory --version. The skill names the release it was written for (a test pins it topyproject) and readsinvalid choice/unrecognized argumentsas "the CLI is older".7a10f22bumps to 0.2.0 in lockstep, so installed plugins actually receive this round's skill.Owner decisions: docs-only Python fix, a write-only service command, opt-in redaction, one PR. The rest are defaults;
docs/reviews/install-feedback-round.mdlists them.Review
b7e3521.SKILL.md. Fixes are inf0ebb41, including a realshow --redactcrash on a lone UTF-16 surrogate.mainunder the same setup, so none is from this branch.Every review finding and its disposition is in
docs/reviews/install-feedback-round.md.Tests
ruff, the full suite, and the corpus-on suite are green locally on macOS.sys.platform. It has not run against a real systemd host.Changes in visible behaviour
watchandcaptureprint sizes in a new format. A script parsing+NBneeds updating.recall's role column readssubagent:user/textfor subagent turns.Left open
v0.2.0. The CHANGELOG compare links and the pinning advice inhook/README.mdrefer to it.main, not fixed here: the push gate misses a\b-anchored key that comes right after a JSON\nor\tescape. Nothing is sent today. The unpushed egress branch's decoded-view scanning appears to target this.main: 16 mutation rows don't bite their intended test offline. Some need the corpora, the dashboard rows needserve, and the rest also fail onmain.SECURITY.md's supported-versions table still reads0.1.x.docs/assets/demo.gifstill shows the old+NBformat.__main__.py,redact.pyand the README pins.🤖 Generated with Claude Code
https://claude.ai/code/session_01QPBLmJD2q1PVxdkWx8C16U