Skip to content

sync: upstream/develop through v3.8.0 — 166 commits (+ ancestry repair) - #401

Merged
jphein merged 602 commits into
mainfrom
sync/upstream-develop-v3.8
Aug 21, 2026
Merged

sync: upstream/develop through v3.8.0 — 166 commits (+ ancestry repair)#401
jphein merged 602 commits into
mainfrom
sync/upstream-develop-v3.8

Conversation

@jphein

@jphein jphein commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Merge with a MERGE COMMIT — not squash

#394 landed as a squash, which severed upstream ancestry: this sync initially wanted to replay all 433 already-resolved commits. The first commit here (910a2a54, tree-identical merge -s ours 8516db7f) records the true synced point, dropping the real conflict set to 15 files. Squashing this PR would throw that away again.

Summary

Sync through v3.8.0 (3e56979f, 166 commits). Upstream: since/before date-window search across search/list/CLI (MemPalace#463 family), openai-compat embedding backend, RFC 002 source-adapter dispatch formalized (MemPalace#2062cmd_mine --source now uses it with typed exits), mempalace-mcpmcp_proxy, chunk_total metadata + interrupted-mine cleanup, read-paired source_mtime, overview-cache invalidation.

Fork-preserved through the 15 conflicts: adaptmem_ft alongside openai-compat (four embedding options), novelty tagging beside chunk_total in both miners, daemon-strict CLI routing (now getattr-tolerant of minimal configs), the delegating CLI search() with upstream's window parse + HNSW fence composed in, tags + the 96f83d7 union guard. Window correctness extended to the fork-only candidate sources upstream couldn't know about: postgres BM25 arm, graph expansion, and the sqlite fallback top-up all honor [since, before); the pre-fusion trim keeps the full in-window pool (upstream's review finding, applied to the fork flow).

Test plan

  • 6099 passed / 0 failed (6189 collected; README/CLAUDE counts updated). Two deselects are known worktree-run artifacts (PYTHONPATH-leak init test; -I-subprocess reload test) — both exercise the installed tree and run green in CI.
  • ruff check + format clean; check-docs.sh 7/7; uv.lock regenerated; tool surface stays 48.
  • Post-merge: hash backfill (merge-commit SHA), deploy to familiar, live verification.

🤖 Generated with Claude Code

igorls and others added 30 commits August 2, 2026 05:25
…edup-convo-miner-drawers

fix: prevent duplicate drawers when re-mining LLM conversations
…ble-drawer-id

fix(search): return round-trippable drawer IDs
fix(convo_miner): stop sweeper drawers being purged (MemPalace#2089)
…exit

fix(mcp): self-exit on stdin EOF/broken pipe so orphaned stdio sessions release locks
…k-contention-retry

fix(daemon): defer jobs refused the palace lock, don't fail them (MemPalace#2014)
fix(mcp): refuse config and ack writes in read-only mode (MemPalace#2103)
fix(entity): defuse entity-candidate ReDoS on long ASCII runs (MemPalace#2065)
Bump package, plugins, lock, and README badge to 3.7.0. Promote the
post-3.6.0 integrity spine into CHANGELOG: single-writer ownership,
HNSW write defaults and preflight, safer repair/re-mine, MCP/daemon
lifecycle hardening, entity ReDoS guard, and hook write-routing.

Also ruff-format two test files that drifted during conflict merges.

Local validation: ruff check/format clean; 3497 passed, 31 skipped.
Address actionable MemPalace#2129 bot feedback and the Windows closet KeyError:

- Reopen immutable sqlite_exact readers only when both WAL sidecars exist
  (partial pair keeps the clean snapshot instead of failing the reconnect).
- Retry get_collection without options when plugin backends reject the kwarg.
- Stamp multi-conversation content_hash only on chunk 0 to avoid O(N²) meta.
- Always include results: [] on search error envelopes so callers never KeyError.
- Clearer hybrid-search assertions in the closet isolation test.
…lish

fix: release 3.7.0 polish — WAL reopen, search errors, hash stamp
from-sqlite ignored --dry-run and performed the real archive+rebuild
(MemPalace#2095, MemPalace#2133). Preview after source validation, skip the destructive
confirm, never take the mine-lock or rename the palace, and fail closed
when SQLite row counts are unreadable instead of inventing zeros.
…-dry-run

fix(repair): honor --dry-run for repair --mode from-sqlite (MemPalace#2133)
…n-mined-state

fix(convos): honor mined state during dry runs
…age-soft-token

fix(embedding): remap unsupported EmbeddingGemma token IDs
…topwords

feat(searcher): wire i18n stop words into BM25 tokenizer (MemPalace#973)
…emPalace#1217)

scan_convos() now prunes any directory named 'subagents' during os.walk.
Claude Code records Explore/Plan/Grep subagent transcripts in
<session-uuid>/subagents/agent-*.jsonl and on a typical workspace these
outweigh main session files ~80:1, dominating mining time and producing
near-zero additional signal (the parent session already summarizes them).

Adds a --include-subagents opt-in flag for users who want full history.
The shared SKIP_DIRS set in palace.py is left untouched, so project
mining (miner.scan_project) still descends into legitimate user-created
subagents/ directories in code projects.
- Case-insensitive directory match (d.lower() == 'subagents') so
  the filter still kicks in if Claude Code or a plugin ever emits
  'Subagents/' on case-preserving filesystems (Windows, macOS APFS).
- Drop defensive getattr in cmd_mine: argparse always defines the
  attribute since --include-subagents is unconditionally registered.
  Direct args.include_subagents access matches every neighbouring
  field and fails loudly if the registration is ever removed.
- Soften CLI help and docstring: drop the 80:1 ratio (reporter-
  specific) and the in-code (MemPalace#1217) reference. Add explicit
  default=False on the argparse flag for symmetry with --extract.
- Add 2 negative tests: 'mysubagents'/'subagentsbackup' must still
  be mined (regression guard against substring-match), and
  'Subagents/' must be skipped (case-insensitive coverage).
igorls and others added 23 commits August 19, 2026 21:30
perf(mcp): make a proxied stdio session stop loading the storage stack
…us-columns

fix(sqlite_exact): index wing/room/hall for structured access
On the i7-8750H chroma palace, list_drawers(limit=20) took 36s and
find_tunnels died at 30s because both paged col.get() and cold-loaded
the vector index. chroma.sqlite3 already has embedding_metadata.

- sqlite_list_id_metadata: ids + metadata + chroma:document without HNSW
- sqlite_room_wing_hall_counts shared with graph_stats
- build_graph uses that grouped read when no collection is injected
- find_tunnels/traverse no longer open the collection first

MCP tests assert the client paging path is not used.
…o sql

Follow-up on the same palace. The metadata scan joined every
embedding_metadata row, and chroma:document lives in that table — so
list_drawers pulled the palace's entire verbatim text into memory to
render 20 previews, then filtered wing/room in Python after scanning
the whole collection.

Measured on the 1.7 GB / 165k-drawer chroma palace,
list_drawers(wing=..., limit=20):

  before  2.31 s, 1148 MB peak RSS
  after   0.01 s,   86 MB peak RSS

Unfiltered goes 1.61 s / 1162 MB -> 1.57 s / 894 MB.

- exclude chroma:document from the scan; hydrate the displayed page via
  sqlite_documents_for_ids (two indexed steps — embedding_id is only
  indexed under UNIQUE(segment_id, embedding_id), so a single join on it
  scans embedding_metadata: 5.7 s for one page)
- push the wing/room equality into SQL as a join per key
- decode cells through _metadata_cell_value with column probing, so
  bool_value is not silently dropped on newer chroma schemas
- grouped counts carry MAX(date), restoring find_tunnels' "recent",
  which the sqlite path had blanked (both backends)
- resolve the graph's sqlite reader from the configured backend instead
  of sniffing the palace dir, so a two-backend directory still raises
  BackendMismatchError instead of being silently picked
- find_tunnels/traverse fall back to the collection when sqlite cannot
  serve, so a missing palace reports "Chroma database missing" again
  rather than [] and "Room not found"

Tests: sql tripwires for the scan (no documents, filter pushed down,
id-scoped preview read), recent-from-sqlite, missing-palace diagnostics,
and backend-gating for the graph reader.
…tadata-reads

fix(chroma): read list_drawers and tunnels from sqlite, skip HNSW
… rule

Coordination stalls are usually listening failures, not protocol failures:
a task sits open because the agent it was addressed to was never watching,
and the requester cannot tell "working on it" from "nobody is home".

The load-bearing correction is the cursor. Events are ordered by append
order (ORDER BY rowid), not wall clock, so a peer's event is appended
whenever it syncs and can already be older than a timestamp high-water
mark. Resuming with since_created_at therefore drops late-arriving
cross-replica events permanently. Measured one such inversion in a single
50-event window: a windows-origin event created 09:10:48Z ingested after a
mac-origin event created 09:13:21Z. list_events' docstring already said
since_event_id is "the precise cursor ... regardless of timestamp ties" —
that just never reached an agent.

- coordination-protocol.md: new "Monitoring the stream" section — the
  cursor rule, four modes (inbox sweep / long-poll / SSE / declared-idle),
  the announce-your-watch convention, and declaring when you are NOT
  watching. Two new hard rules.
- coordination-protocol.md: system-prompt snippet updated, since that block
  is copied verbatim into every agent's instructions.
- agent-logstream.md: matching "Monitoring" concepts section.
- mcp_server.py: event_list and event_wait descriptions now state the
  cursor rule at the point of use, and both since_created_at params are
  marked "NOT a resume cursor".

The announce-your-watch shape is generalized from windows:grok:mempalace's
issue-354-live-layer announcement, which named its filter, its cursor, and
the work not to duplicate.

Docs and tool descriptions only; no behavior change.
…an be woken by

`logstream wait` is a primitive, not a watcher. It caps at MAX_WAIT_TIMEOUT_MS
and reports a timeout, so every caller writes the same re-arm loop and each
one has to remember to carry the cursor forward. Agents mostly did not, so
coordinated tasks stalled on nobody listening rather than on the work.

Two filters a watcher needs cannot be expressed by list_events, whose SQL is
single-valued and positive-only:

  * "wake me for task.request OR patch.ready"
  * "everything addressed to me EXCEPT my own events"

The second is not a nicety. to_agent=<me> deliberately also matches '*'
broadcasts, and an agent's own broadcasts are broadcasts — so a hand-rolled
watcher wakes itself every time it posts a status. Found by running one:
mac-claude woke on its own fleet announcement within seconds.

- logstream.watch_events(): owns the re-arm loop and the cursor. Yields
  ([], cursor) on idle polls so callers can time out, heartbeat, or
  checkpoint without a second clock. The cursor advances past every event
  examined, not only matches, so a restart never re-judges what it rejected.
- event_matches_watch() / normalize_watch_values() / pushdown_watch_filters():
  single-valued filters push down to SQL to keep the query selective;
  multi-valued and negative parts are re-checked client-side. Pushdown is an
  optimization, never a correctness dependency.
- read/write_watch_cursor(): atomic temp+rename. A corrupt state file costs a
  replay; refusing to start costs every event after it, so reads degrade to
  None rather than raising.
- CLI `logstream watch`: --agent (shorthand for --to-agent X
  --exclude-from-agent X), repeatable filters meaning "or", --state-file,
  --follow, --idle-exit-ms. Exit 0 on a match, 2 on idle — the same
  convention `wait` uses, so a harness can background the process and treat
  its exit as "you have mail".

Docs updated to lead with it: coordination-protocol.md (including the
system-prompt snippet every agent copies), the agent-logstream concepts page,
and the CLI reference.

20 tests: self-exclusion at both the matcher and CLI level, multi-valued ORs,
cursor advance-past-rejected, resume-without-replay, --since-event-id
overriding the state file, and the exit-code contract.
…iew cursor docs

Codex P2s on the watch PR, confirmed on Windows:

- Cap each wait_events timeout to remaining --idle-exit-ms so idle 400ms
  does not wait out a 300s long-poll (measured 2687ms -> 572ms).
- Persist the watch cursor after successful stdout on a match; unmatched
  advances still checkpoint immediately. A broken pipe no longer skips
  the event on resume.
- preview=true docs told clients to re-fetch via since_event_id of the
  truncated event, but that cursor is strictly after it. Repeat the
  original filters with preview=false instead.
… the log

windows:grok:mempalace flagged the first-run replay on PR MemPalace#2315. Measured on
the real shared brain: a fresh `logstream watch --agent mac-claude` with no
cursor woke holding 41 events, the oldest 49 days old. Nothing in the payload
marks them stale, so an agent reads July's task.requests as new work — the
opposite of the problem this command exists to solve.

`latest_event_id()` already documents the right behaviour for the SSE
live-tail: capture the tip at connect time "so they receive only post-connect
events". The CLI watcher not doing the same made one product with two
first-run semantics.

- default: a watch with no --since-event-id and no stored cursor starts at
  the tip. Backlog belongs to the inbox sweep (`logstream list`), which can
  page it deliberately; a watcher is for what arrives from now on.
- never silent: it prints what it skipped and how to get it, on stderr, in
  --json mode too, so the note cannot corrupt a parsed payload.
- --from-start opts back into the replay.

Six existing tests turned out to depend on the replay, which is the evidence
this mattered. Their intent is filtering, not first-run behaviour, so the
shared args helper opts them into --from-start and three new tests cover the
tip default explicitly — including that an explicit --since-event-id or a
state-file cursor still wins over it.

Note for review: test_agent_shorthand_does_not_wake_on_your_own_broadcast
asserts exit 2, which the tip default would satisfy for the wrong reason.
It opts into --from-start so it keeps testing the exclusion.

4395 passed, 31 skipped.
Seven inline P2s on 8663ecd. Two were stale — Codex anchored them to lines
0d3f216 had already fixed. The other five were real, and all five are mine.

(a) read_watch_cursor: a state file holding valid JSON that is not an object
    (null, [], a bare string) made .get() raise AttributeError, which the
    except clause did not catch — so the watcher refused to start, the exact
    opposite of the recovery contract in its own docstring.

(b) KeyboardInterrupt returned normally, exiting 0. Exit 0 is the documented
    "a match was printed" signal, so SIGINT told a supervisor it had mail
    that never arrived. Now 130.

(c) --poll-timeout-ms 0 with no idle deadline made the callable return zero
    forever, so watch_events' expired-deadline branch yielded without ever
    polling and burned a core. Rejected at the CLI, so that branch can now
    only mean a deadline actually elapsed. I saw this shape while reviewing
    0d3f216 and wrongly filed it as unreachable in-tree.

(d) A regression I introduced in 8663ecd. read_watch_cursor returns None for
    both "no state file yet" and "state file present but unreadable"; the
    tip-default treated the second like the first, skipping everything since
    the last good checkpoint and then re-checkpointing at the tip, making the
    loss permanent. A failed read now replays and says so, which is what
    read_watch_cursor promises: "a corrupt state file costs a replay".

(e) --follow --json printed repeated indented documents, which json.load and
    jq reject as trailing data — a machine-readable flag that was not, on the
    mode built for daemons. Follow now emits NDJSON, one compact record per
    line; single-shot keeps the one pretty document.

Five regression tests, each verified to fail against 8663ecd. The
--poll-timeout-ms one initially passed pre-fix for the wrong reason — an idle
timeout also exits nonzero — so it now pins the validation error itself
rather than "some nonzero exit".

4400 passed, 31 skipped.
…behind

Third Codex round, one P2 on 57d8d24, and a third variant of the same family
as the last two: "no cursor" is not one fact.

Sequence: a stateful watch starts against an empty log, so latest_event_id()
is None and write_watch_cursor ignored the falsey cursor, leaving no file. The
watcher idles out. An event arrives while it is stopped. The next launch sees
no state file, concludes first run, jumps to that event as the new tip, and
skips it — then checkpoints past it, making the loss permanent.

The root cause both this and the previous round shared is that a missing
cursor was being read as a single condition when it is four:

  absent   no state file — a genuine first run, may start at the tip
  ok       a usable cursor, resume strictly after it
  empty    file exists recording cursor: null — started against an empty log,
           has not seen an event yet. NOT a first run.
  corrupt  unreadable, truncated, or valid JSON that is not an object

Only `absent` may start at the tip. `empty` and `corrupt` replay. read_watch_state
returns (cursor, condition) and the CLI branches on the condition;
read_watch_cursor stays as a wrapper for callers that do not care.

write_watch_cursor now persists a null cursor rather than skipping the write,
and the starting position is recorded at startup instead of waiting for the
first idle yield, so a kill in between cannot reproduce the same hole.

Three regression tests, each verified to fail against 57d8d24, including the
full arrives-while-stopped sequence end to end.

4403 passed, 31 skipped. CI green on 57d8d24 across all nine checks.
…one must not fail quietly

Fourth Codex round, two P2s on b35bfc1. Same family as rounds two and three
— both are ways a *present* checkpoint gets mistaken for a first run — so
both are checked against the invariant rather than patched individually: a
restart may cost a duplicate, never a missed delegation.

read_watch_state no longer preflights with os.path.exists(). That call
answers False both for "no such file" and for "cannot traverse the parent
directory", so a checkpoint that exists but is momentarily unreachable was
classified absent, started at the tip, and skipped every event since the
stored cursor. Opening directly lets FileNotFoundError mean absent and every
other OSError — permissions, a directory in the way, I/O error — mean
corrupt, which replays. It also closes the exists()/open() race.

write_watch_cursor gains required=. Best effort is the right default because
a lost checkpoint costs a replay while a crashed watcher costs every event
after it. That trade-off inverts for the first checkpoint of a fresh watch:
if it never lands there is no state file, the next launch calls itself a
first run, and it starts at the tip — so a swallowed failure there
guarantees the exact skip the checkpoint existed to prevent. The CLI now
refuses to start rather than continuing into that.

Three regression tests, each verified to fail against b35bfc1. The
unreachable-file case is exercised twice, via a directory standing where a
file is expected (OSError on every platform) and via a simulated
PermissionError, so it holds on the Windows runner too.

4406 passed, 31 skipped.
…ced it

Fifth Codex round, one new P2 on dd9b123, and the sixth instance of one bug:
a starting cursor that never reaches disk, so the next launch calls itself a
first run and jumps to the tip.

This time --since-event-id with no state file skipped the immediate
checkpoint entirely, because that write lived inside the "resolve the tip"
branch and an explicit cursor never enters it. Nothing was persisted until
watch_events yielded, which by default is up to five minutes later; an
interrupt inside that window leaves no file at all.

Rather than add a third call site I hoisted it: one place resolves the
starting position, one place records it. The position can arrive three ways
— an explicit --since-event-id, the tip, or None from an empty log or
--from-start — and all three now take the same required write whenever no
state file exists yet. That removes the shape the last four rounds kept
finding new instances of, instead of removing one more instance.

Two test-quality notes, both the same lesson:

The first version of this test passed against the unfixed code. The helper
uses a 60ms poll timeout, so watch_events yielded immediately and the loop's
own checkpoint wrote the file — the startup window the bug lives in never
opened. It now stubs watch_events to interrupt before yielding, which is what
actually pins the startup write, and covers all three entry paths.

The hoist also broke test_match_does_not_checkpoint_if_output_fails, which
asserted that no state file existed after a failed match write. That was
pinning the symptom: a startup checkpoint now legitimately exists. The
invariant is that its cursor must still be the pre-match position so the
undelivered event replays, so it asserts the cursor value instead.

4407 passed, 31 skipped.
…tive idle as forever

Sixth Codex round, two P2s on b5c4af4.

A typo'd or stale --since-event-id was persisted by the required startup
write before anything checked it. list_events raises on an unknown anchor and
that ValueError was uncaught, so the run died with a traceback while the bad
id sat in the state file — and every later run without the flag reloaded it
and died the same way until someone deleted the file by hand. The cursor is
now verified before it is written anywhere.

That check distinguishes where the cursor came from, because the right answer
differs. An explicit --since-event-id that does not resolve is user error:
refuse, and leave nothing behind to reload. A *stored* cursor whose event has
gone — log rebuilt, replica reset — is corrupt state, and refusing would
strand the watcher exactly as an unreadable file would, so it replays with a
notice instead. Same invariant as the rest of this branch: a duplicate,
never a missed delegation.

--idle-exit-ms now rejects negatives. Only 0 documents "wait forever", but a
negative took that same branch silently, so a value arriving from config or
from timeout arithmetic would leave a harness waiting on a watcher it
believed would time out.

Three regression tests. The negative-idle one cannot simply be run against
the old code to prove it: the pre-fix behaviour is an infinite wait, so the
tripwire check hangs rather than fails, and confirming it needed an external
timeout. Worth knowing before someone "fixes" that test by shortening it.

4410 passed, 31 skipped.
… polling

Seventh Codex round, two P2s on af9b7e2, and the same shape twice: input that
was only rejected once the loop had already started.

Filter validation depended on arity. list_events sanitizes what it is given,
so a single-valued watch filter was checked for free by being pushed down,
while a repeated one was not pushed down and got compared raw. `--type
Task.Request` alone was an error; `--type Task.Request --type patch.ready`
was accepted and then matched nothing, so the watcher waited forever for an
event type that cannot exist. sanitize_watch_spec now runs every value
through the same sanitizers list_events uses, whatever the arity, and
normalizes them so a padded routing value still matches.

--limit 0 was accepted by argparse and raised inside the first poll, where
the only handler was for KeyboardInterrupt: a traceback, and under --json no
error document at all. Validated up front.

Both are argument checks, so they belong together and ahead of everything
else: _watch_spec now validates and builds the whole spec before any polling,
cursor resolution, or checkpoint write. That ordering is the point — round
six's bug was a bad cursor being persisted before it was validated, and
keeping validation in one place ahead of side effects is what stops the next
one.

Added a ValueError backstop around the loop as well. Every known bad input is
rejected earlier now, but an escape would otherwise surface as a traceback,
and under --json as nothing a machine consumer can parse.

Four regression tests, each verified to fail against af9b7e2.

4414 passed, 31 skipped.
…oring

feat(logstream): background watcher agents can be woken by, plus the monitoring protocol
…ted-tereshkova-566240

fix(embedding): never let CoreML return NaN vectors for embeddinggemma
…borate-content

fix(repair): check the FTS5 content table before rebuilding from it (MemPalace#2278)
Version bumped across all seven sources and CHANGELOG [Unreleased] promoted
to [3.8.0].

Note for whoever cuts the next one: `integrations/openclaw/SKILL.md` carries
a `version:` in its frontmatter and is the seventh source. version-guard
checks five, a test enforces the README badge, and nothing at all enforces
that one — it has been bumped by hand in every release commit back to 3.3.0.
docs/RELEASING.md still says "all five".

uv.lock carries only the mempalace version line. Regenerating it locally also
resolved onnxruntime-gpu for Python 3.10 from 1.24.3 down to 1.20.2 and
dropped the 1.24.3 entry — an unrelated dependency change that has no place
riding along in a release bump, so the lock was restored and the single line
edited instead. Verified with --frozen.
PR #394 squash-merged the v3.7.0 sync, so main carries that upstream
content but not its ancestry — git's merge base stayed at 2ec4bae and
every subsequent sync would replay all 433 already-resolved commits.

This is a no-op tree-wise (-s ours): it only records 8516db7 as a
parent so merge-base moves forward to the true synced point. Verified
before recording: the v3.7 content IS present in main (kg_supersede,
_aligned_query_ids, upstream repair flow, 48-tool surface).

Future syncs must land as MERGE COMMITS, not squash, to keep this
correct.
…flict files

Preceded by an ancestry-repair commit (merge -s ours 8516db7): #394
landed as a squash, so the merge base was still pre-v3.7 and git wanted
to replay all 433 already-resolved commits. With ancestry recorded,
only the 166 genuinely new commits replayed. THIS PR MUST LAND AS A
MERGE COMMIT (not squash) to keep that true for the next sync.

Upstream: since/before date-window search across search/list/CLI
(MemPalace#463), openai-compat embedding backend, RFC 002 adapter dispatch
formalized (MemPalace#2062) — cmd_mine --source now uses it with typed exits,
mempalace-mcp entry point → mcp_proxy, chunk_total metadata +
interrupted-mine cleanup (MemPalace#2183/MemPalace#2122), read-paired source_mtime (#22),
overview-cache invalidation, v3.8.0.

Fork-preserved: adaptmem_ft alongside openai-compat (four embedding
options), novelty tagging beside chunk_total in both miners,
daemon-strict routing (getattr-tolerant for minimal test configs),
delegating CLI search() with upstream's window parse + HNSW fence
composed in, room-taxonomy warnings beside upstream's cache
invalidation, tags + the 96f83d7 union guard. Window correctness
extended to fork-only candidate sources upstream couldn't know:
postgres BM25 arm, graph expansion, sqlite fallback top-up all honor
[since, before); the pre-fusion trim keeps the full in-window pool.

Tests: 6189 collected; suite green (6099 passed, 82 skipped, 123
deselected; two deselects are known worktree-run artifacts: the
PYTHONPATH-leak init test and the -I-subprocess reload test — both
exercise the installed tree, green in CI). ruff check+format clean;
check-docs 7/7; uv.lock regenerated; 48-tool surface unchanged.

Fixes #400.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 21, 2026 02:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 160 files, which is 60 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c9643ec-7b70-4771-9142-79f7a8ba7085

📥 Commits

Reviewing files that changed from the base of the PR and between e292f42 and 3c1987c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (160)
  • .agents/plugins/marketplace.json
  • .antigravity-plugin/skills/mempalace-recall/SKILL.md
  • .claude-plugin/marketplace.json
  • .claude-plugin/plugin.json
  • .claude-plugin/skills/mempalace-recall/SKILL.md
  • .codex-plugin/plugin.json
  • .github/CODEOWNERS
  • .github/workflows/ci.yml
  • .github/workflows/docker-publish.yml
  • .github/workflows/publish.yml
  • .github/workflows/version-guard.yml
  • .gitignore
  • .mcp.json
  • CHANGELOG.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • FORK_CHANGELOG.md
  • README.md
  • benchmarks/convomem_bench.py
  • benchmarks/locomo_bench.py
  • benchmarks/longmemeval_bench.py
  • benchmarks/membench_bench.py
  • deploy/mempalace-server.service
  • deploy/server.env.example
  • docs/RELEASING.md
  • docs/fork-changes.yaml
  • docs/hook-write-routing.md
  • docs/rfcs/001-storage-backend-plugin-spec.md
  • examples/mx3_public_shim_embeddings_rerank.md
  • examples/mx3_public_shim_embeddings_rerank.py
  • integrations/openclaw/SKILL.md
  • integrations/shared/coordination-protocol.md
  • integrations/shared/recall-protocol.md
  • lychee.toml
  • mempalace/backends/base.py
  • mempalace/backends/chroma.py
  • mempalace/backends/pgvector.py
  • mempalace/backends/sqlite_exact.py
  • mempalace/backups.py
  • mempalace/cli.py
  • mempalace/config.py
  • mempalace/convo_miner.py
  • mempalace/date_window.py
  • mempalace/dedup.py
  • mempalace/dialect.py
  • mempalace/embedding.py
  • mempalace/encoding_repair.py
  • mempalace/entity_detector.py
  • mempalace/exporter.py
  • mempalace/format_miner.py
  • mempalace/hook_shell.py
  • mempalace/hooks_cli.py
  • mempalace/ids.py
  • mempalace/integrations/__init__.py
  • mempalace/integrations/hermes/__init__.py
  • mempalace/layers.py
  • mempalace/llm_refine.py
  • mempalace/logstream.py
  • mempalace/mcp_proxy.py
  • mempalace/mcp_server.py
  • mempalace/migrate.py
  • mempalace/miner.py
  • mempalace/normalize.py
  • mempalace/onboarding.py
  • mempalace/palace.py
  • mempalace/palace_graph.py
  • mempalace/project_scanner.py
  • mempalace/repair.py
  • mempalace/room_detector_local.py
  • mempalace/searcher.py
  • mempalace/server_registry.py
  • mempalace/service.py
  • mempalace/split_mega_files.py
  • mempalace/sweeper.py
  • mempalace/version.py
  • pyproject.toml
  • scripts/mempalace_repair_encoding.py
  • skills/mempalace-recall/SKILL.md
  • tests/benchmarks/test_performance_budgets.py
  • tests/conftest.py
  • tests/test_adaptmem_ft.py
  • tests/test_backend_conformance.py
  • tests/test_backends.py
  • tests/test_backups.py
  • tests/test_cli.py
  • tests/test_cli_logstream.py
  • tests/test_cli_source.py
  • tests/test_cli_source_adapters.py
  • tests/test_codex_plugin_manifest.py
  • tests/test_convo_miner.py
  • tests/test_convo_miner_unit.py
  • tests/test_daemon.py
  • tests/test_date_window.py
  • tests/test_embedding.py
  • tests/test_embedding_api.py
  • tests/test_embeddinggemma.py
  • tests/test_encoding_hardening.py
  • tests/test_encoding_repair.py
  • tests/test_general_extractor.py
  • tests/test_hermes_integration.py
  • tests/test_hybrid_search.py
  • tests/test_knowledge_graph_metadata.py
  • tests/test_logstream.py
  • tests/test_logstream_sync.py
  • tests/test_mcp_mine.py
  • tests/test_mcp_proxy.py
  • tests/test_mcp_server.py
  • tests/test_migrate.py
  • tests/test_miner.py
  • tests/test_non_regular_file_guards.py
  • tests/test_onboarding.py
  • tests/test_palace_graph.py
  • tests/test_palace_locks.py
  • tests/test_pgvector_backend.py
  • tests/test_rebuild_index_quiescence.py
  • tests/test_repair.py
  • tests/test_repair_encoding_cli.py
  • tests/test_searcher.py
  • tests/test_service_palace_env.py
  • tests/test_split_mega_files.py
  • tests/test_sqlite_exact_backend.py
  • tests/test_sweeper.py
  • website/.vitepress/api-sidebar.json
  • website/.vitepress/config.mts
  • website/concepts/agent-logstream.md
  • website/concepts/knowledge-graph.md
  • website/guide/configuration.md
  • website/guide/getting-started.md
  • website/guide/mcp-integration.md
  • website/guide/remote-server.md
  • website/guide/termux.md
  • website/public/llms-full.txt
  • website/reference/api-reference.md
  • website/reference/cli.md
  • website/reference/contributing.md
  • website/reference/mcp-tools.md
  • website/reference/python-api.md
  • website/reference/python-api/backends/base.md
  • website/reference/python-api/backends/chroma.md
  • website/reference/python-api/backends/pgvector.md
  • website/reference/python-api/backends/sqlite_exact.md
  • website/reference/python-api/backups.md
  • website/reference/python-api/cli.md
  • website/reference/python-api/config.md
  • website/reference/python-api/convo_miner.md
  • website/reference/python-api/date_window.md
  • website/reference/python-api/embedding.md
  • website/reference/python-api/hook_shell.md
  • website/reference/python-api/ids.md
  • website/reference/python-api/index.md
  • website/reference/python-api/integrations/hermes.md
  • website/reference/python-api/logstream.md
  • website/reference/python-api/mcp_proxy.md
  • website/reference/python-api/mcp_server.md
  • website/reference/python-api/palace.md
  • website/reference/python-api/palace_graph.md
  • website/reference/python-api/repair.md
  • website/reference/python-api/searcher.md
  • website/reference/python-api/server_registry.md
  • website/reference/python-api/sweeper.md

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… of tags)

v3.6/v3.7 hit this 2026-08-09; v3.7.1/v3.8.0 again today. The compare
family carries no lint value once the pattern is structural.
@jphein
jphein merged commit b3527b1 into main Aug 21, 2026
15 of 16 checks passed
@jphein
jphein deleted the sync/upstream-develop-v3.8 branch August 21, 2026 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.