Skip to content

fix: wire up knowledge pipeline for SQLite-based system#16

Closed
john-wilmes-luma-health wants to merge 7 commits into
john-wilmes:masterfrom
john-wilmes-luma-health:master
Closed

fix: wire up knowledge pipeline for SQLite-based system#16
john-wilmes-luma-health wants to merge 7 commits into
john-wilmes:masterfrom
john-wilmes-luma-health:master

Conversation

@john-wilmes-luma-health
Copy link
Copy Markdown
Contributor

@john-wilmes-luma-health john-wilmes-luma-health commented Mar 25, 2026

Summary

  • session-end: auto-promote staged_candidates to entries at session end — previously required manual /learn invocation, so staged captures were accumulating without ever being promoted
  • knowledge-consolidate: rewrite to query the SQLite DB instead of the flat entries/ directory, which no longer exists since the system migrated to SQLite

Test plan

  • Verify staged candidates are promoted to entries at session end
  • Verify knowledge-consolidate --dry-run runs without error and finds entries from the DB
  • Verify knowledge-consolidate --apply archives flagged entries via knowledge-db.js archive

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Investigate Step 10 renamed to "Finalize and Auto-Close": auto-invokes close, auto-applies suggested tags, and shows a structured final summary (tags, pattern extraction, PHI sanitization, findings path).
  • Improvements

    • Knowledge consolidation now uses a database for deduplication and archives duplicates in-place.
    • Installer switches many hook artifacts to symlinks for easier updates.
    • MCP integration notes now emphasize running local, PHI-sanitizing wrapper servers and secure token handling.
    • Context-guard defers checkpoint triggers until a few tool calls have occurred.
  • Tests

    • Added unit tests for session-end auto-promote helpers and updated context-guard tests to exercise the new priming behavior.

john-wilmes-luma-health and others added 3 commits March 25, 2026 10:59
After synthesis passes self-assessment, the run subcommand now
automatically proceeds to close (tagging, pattern extraction, PHI
sanitization) without requiring a separate /investigate <id> close
invocation. Tags are applied without confirmation in this auto-close
path; interactive close still prompts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…servers

Point datadog, clickup, and mongodb registry entries to their
PHI-sanitizing custom server wrappers registered in ~/.claude.json,
replacing generic public server notes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- session-end: auto-promote staged_candidates to entries at session end
  (previously required manual /learn invocation)
- knowledge-consolidate: rewrite to query SQLite DB instead of flat
  entries/ directory that no longer exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 777bb57a-9a38-4572-8e92-116fb880dffa

📥 Commits

Reviewing files that changed from the base of the PR and between 76cce6e and 7b13179.

📒 Files selected for processing (4)
  • scripts/knowledge-consolidate.sh
  • templates/hooks/context-guard.js
  • templates/hooks/session-end.js
  • tests/hooks/context-guard.test.js

📝 Walkthrough

Walkthrough

Auto-closes investigations by invoking close inline (skipping interactive tag confirmation), migrates knowledge consolidation to a SQLite export flow, auto-promotes staged knowledge into the DB at session end, switches many hook installs to symlinks, and updates MCP server registry notes to reference local launch wrappers.

Changes

Cohort / File(s) Summary
Investigate Workflow
profiles/combined/skills/investigate/SKILL.md
Step 10 renamed to "Finalize and Auto-Close"; invokes close inline in auto-close mode, skips interactive tag confirmation (writes suggested tags into FINDINGS.md frontmatter), and expands final output with applied-tags, pattern-extraction flag, PHI-sanitized flag, and resolved findings path.
Knowledge Consolidation Script
scripts/knowledge-consolidate.sh
Replaced filesystem scanning with SQLite DB export via knowledge-db.js; builds prompts from DB-exported fields, groups entries by tool via Node JSONL parsing, and archives duplicates by calling knowledge-db.js archive <id> rather than moving files.
Session-End Hook (Auto-Promote)
templates/hooks/session-end.js
Added _generateEntryId, _mapStagedToEntry, and autoPromoteStagedCandidates; transactional promotion from staged_candidates -> entries with duplicate-first-line avoidance, collision-safe ID suffixing, deletion of promoted staged rows, and exported helpers for testing.
Hook Installation Script
install.sh
Added install_symlink() helper; changed many hook installs from copying to symlinking; updated uninstall to handle files or symlinks and improved DRY_RUN/overwrite behavior.
MCP Server Registry Notes
templates/registry/mcp-servers.json
Updated notes for datadog, clickup, and mongodb to instruct using local wrapper launch scripts for PHI-sanitizing/OAuth handling; adjusted fleet-index.config.args path guidance.
Context Guard & Tests
templates/hooks/context-guard.js, tests/hooks/context-guard.test.js
Added MIN_CALLS_BEFORE_BLOCK=5 gating of checkpoint/failsafe/block actions; tests updated with primeSession(...) helper to pre-seed toolCalls for threshold behavior.
Unit Tests (Session-End)
tests/hooks/session-end-promote.test.js
New unit tests for _mapStagedToEntry and _generateEntryId, covering mapping rules, tag composition, deterministic ID format and slug truncation.

Sequence Diagram(s)

sequenceDiagram
  participant Hook as Session Hook (session-end.js)
  participant DB as Knowledge DB (knowledge.db)
  participant Staged as staged_candidates
  participant Entries as entries
  participant Logger as Console/Logger

  Hook->>DB: open connection
  DB->>Staged: query staged_candidates for session
  Staged-->>Hook: list of candidate rows
  Hook->>DB: query entries first-line snapshots
  DB-->>Hook: existing first-lines
  loop each candidate
    Hook->>Hook: map candidate -> entry (_mapStagedToEntry)
    alt first-line duplicate exists
      Hook->>Logger: skip promotion (duplicate)
    else
      Hook->>DB: insert new entry (generate id via _generateEntryId)
      DB-->>Hook: success / inserted id
      Hook->>Staged: delete promoted staged row
      Hook->>Logger: log promotion
    end
  end
  Hook->>DB: close connection
  Hook->>Logger: completion message
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through staged rows, whiskers twitching bright,
Spun snippets into entries, gave duplicates a polite fright.
Auto-close the case with a gentle thump and cheer,
Symlinks hum, DBs whisper—new knowledge draws near.
Little rabbit naps: "Promoted, archived, and clear."

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: wire up knowledge pipeline for SQLite-based system' directly and clearly summarizes the main changes: migrating the knowledge system from filesystem-based to SQLite database, with the key change being auto-promotion of staged candidates and knowledge-consolidate rewritten for SQLite.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
templates/hooks/session-end.js (1)

144-145: Redundant condition check.

Line 144 checks !summary && !snippet, but line 145 immediately returns null if !snippet. The first condition is redundant since !snippet alone would cover the case where both are empty.

Simplify the null checks
 function _mapStagedToEntry(row) {
   const summary = (row.summary || "").trim();
   const snippet = (row.context_snippet || "").trim();
-  if (!summary && !snippet) return null;
   if (!snippet) return null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@templates/hooks/session-end.js` around lines 144 - 145, Remove the redundant
null-check that tests both variables: delete the line "if (!summary && !snippet)
return null;" and keep the single concise early-return "if (!snippet) return
null;" so the code relies on the existing snippet check (variables summary and
snippet) rather than duplicating the condition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/knowledge-consolidate.sh`:
- Around line 127-137: The current nested Node invocation that interpolates the
shell variable tool into a command (used to build ENTRIES_JSON and COUNT) can
break on special characters; instead, stop inlining tool into the command and
pass it safely (e.g., via an environment variable or stdin) to the Node script
that parses GROUPS_JSON. Modify the blocks that set ENTRIES_JSON and COUNT to
read GROUPS_JSON from stdin and get the tool name from process.env.TOOL (or
process.argv) so you can call node once without shell-escaping tool; update
references to GROUPS_JSON, ENTRIES_JSON, COUNT and the tool variable
accordingly.

---

Nitpick comments:
In `@templates/hooks/session-end.js`:
- Around line 144-145: Remove the redundant null-check that tests both
variables: delete the line "if (!summary && !snippet) return null;" and keep the
single concise early-return "if (!snippet) return null;" so the code relies on
the existing snippet check (variables summary and snippet) rather than
duplicating the condition.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c0b30f87-c4f0-43c8-a7b6-602d4e8b0cab

📥 Commits

Reviewing files that changed from the base of the PR and between 89d6a01 and 4628560.

📒 Files selected for processing (4)
  • profiles/combined/skills/investigate/SKILL.md
  • scripts/knowledge-consolidate.sh
  • templates/hooks/session-end.js
  • templates/registry/mcp-servers.json

Comment thread scripts/knowledge-consolidate.sh
…stall

Copying hook files to ~/.claude/hooks/ creates drift: any edit to a
template requires re-running install.sh to take effect at runtime.
Switch all install_file calls for hooks/* to a new install_symlink
helper that uses ln -sf, so the installed path is always the live
template. Also update the uninstall guard to handle symlinks
(-L check alongside -f) so removal works correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@install.sh`:
- Around line 374-405: The install_symlink function can accidentally create a
symlink inside an existing directory because ln -sf on a directory targets the
directory rather than replacing it; before each ln -sf (the one in the overwrite
branch and the one in the normal install branch) add an explicit directory check
for dest (e.g. [ -d "$dest" ]) and treat it as a conflict: prompt the user (or
respect FORCE) to either remove the directory (rm -rf "$dest") then create the
symlink, or skip; update the logic around the overwrite branch (where choice o|O
triggers ln -sf) and the normal branch (where ln -sf is used) to handle
directory-dest safely so install_symlink always replaces or skips a directory
rather than creating a symlink inside it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 13be3f57-c910-4453-8b20-e9467ae9e235

📥 Commits

Reviewing files that changed from the base of the PR and between 4628560 and 934a080.

📒 Files selected for processing (1)
  • install.sh

Comment thread install.sh
Comment on lines +374 to +405
install_symlink() {
local src="$1"
local dest="$2"
local label="$3"

if [ "$DRY_RUN" = true ]; then
if [ -e "$dest" ] || [ -L "$dest" ]; then
echo "[DRY RUN] CONFLICT: $label -> $dest (exists)"
else
echo "[DRY RUN] SYMLINK: $label -> $dest"
fi
return
fi

if ([ -e "$dest" ] || [ -L "$dest" ]) && [ "$FORCE" != true ]; then
echo "EXISTS: $dest"
read -r -p " [s]kip, [o]verwrite? " choice
case $choice in
o|O)
ln -sf "$src" "$dest"
echo " -> Symlinked (overwritten)."
;;
*)
echo " -> Skipped."
;;
esac
else
mkdir -p "$(dirname "$dest")"
ln -sf "$src" "$dest"
echo "INSTALLED: $label -> $dest (symlink)"
fi
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

On GNU coreutils lnand BSD/macOSln, what happens when running ln -sf SOURCE DESTifDESTalready exists as a directory? Does it replaceDESTor create the symlink insideDEST? What is the portable way to avoid silently misinstalling in that case?

💡 Result:

On both GNU coreutils ln and BSD/macOS ln, running ln -sf SOURCE DEST when DEST already exists as a directory creates the symlink named with the basename of SOURCE inside DEST, rather than replacing DEST itself. It does not replace the directory. This occurs because ln detects DEST as an existing directory and treats the command as the 3rd form: ln [options] SOURCE... DIRECTORY, creating the link inside the directory. GNU coreutils man page states: "In the 3rd ... forms, create links to each TARGET in DIRECTORY." And explicitly: "When the destination is an actual directory (not a symlink to one), there is no ambiguity. The link is created in that directory." BSD/macOS/FreeBSD/OpenBSD man pages describe similar behavior: if target is a directory, the link is placed inside it using the basename of source. Examples from sources confirm: mkdir dir; ln -sf file dir results in dir/file -> SOURCE, not replacing dir. The portable way to avoid silently misinstalling (i.e., to force replacing DEST even if it's a directory) is to use ln -sfT SOURCE DEST on GNU systems (where -T/--no-target-directory treats DEST as a normal file always). However, -T is GNU-specific. For full portability across GNU, BSD, and macOS, first check if DEST exists and is a directory, then remove it explicitly: [ -e DEST ] && rm -f DEST # or rm -rf if allowing directory removal ln -sf SOURCE DEST Or more safely: [ -d DEST ] && rmdir DEST 2>/dev/null || rm -f DEST ln -sf SOURCE DEST This ensures DEST is removed if it's a directory or file/symlink, preventing the symlink from being created inside it. POSIX ln -f does not remove directories, as unlink on directories requires privileges and is not standard behavior.

Citations:


🏁 Script executed:

fd install.sh

Repository: john-wilmes/claude-agentic-coding-playbook

Length of output: 96


🏁 Script executed:

if [ -f install.sh ]; then
  echo "=== Lines 374-405 of install.sh ==="
  sed -n '374,405p' install.sh | cat -n
fi

Repository: john-wilmes/claude-agentic-coding-playbook

Length of output: 1079


🏁 Script executed:

# Also check total lines in install.sh
wc -l install.sh

Repository: john-wilmes/claude-agentic-coding-playbook

Length of output: 101


Guard against existing directories before calling ln -sf.

If $dest already exists as a directory, ln -sf will create the symlink inside that directory rather than replacing it. This is consistent behavior on GNU coreutils and BSD/macOS systems. The installer then reports success despite $dest not being the expected hook file path. Add an explicit directory check before attempting symlink creation on both lines 393 and 402:

 install_symlink() {
   local src="$1"
   local dest="$2"
   local label="$3"
 
   if [ "$DRY_RUN" = true ]; then
-    if [ -e "$dest" ] || [ -L "$dest" ]; then
+    if [ -d "$dest" ] && [ ! -L "$dest" ]; then
+      echo "[DRY RUN] CONFLICT: $label -> $dest (directory)"
+    elif [ -e "$dest" ] || [ -L "$dest" ]; then
       echo "[DRY RUN] CONFLICT: $label -> $dest (exists)"
     else
       echo "[DRY RUN] SYMLINK:  $label -> $dest"
     fi
     return
   fi
 
-  if ([ -e "$dest" ] || [ -L "$dest" ]) && [ "$FORCE" != true ]; then
+  if [ -d "$dest" ] && [ ! -L "$dest" ]; then
+    echo "ERROR: $dest is a directory; remove it before installing $label"
+    return 1
+  fi
+
+  if { [ -e "$dest" ] || [ -L "$dest" ]; } && [ "$FORCE" != true ]; then
     echo "EXISTS: $dest"
     read -r -p "  [s]kip, [o]verwrite? " choice
     case $choice in
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
install_symlink() {
local src="$1"
local dest="$2"
local label="$3"
if [ "$DRY_RUN" = true ]; then
if [ -e "$dest" ] || [ -L "$dest" ]; then
echo "[DRY RUN] CONFLICT: $label -> $dest (exists)"
else
echo "[DRY RUN] SYMLINK: $label -> $dest"
fi
return
fi
if ([ -e "$dest" ] || [ -L "$dest" ]) && [ "$FORCE" != true ]; then
echo "EXISTS: $dest"
read -r -p " [s]kip, [o]verwrite? " choice
case $choice in
o|O)
ln -sf "$src" "$dest"
echo " -> Symlinked (overwritten)."
;;
*)
echo " -> Skipped."
;;
esac
else
mkdir -p "$(dirname "$dest")"
ln -sf "$src" "$dest"
echo "INSTALLED: $label -> $dest (symlink)"
fi
}
install_symlink() {
local src="$1"
local dest="$2"
local label="$3"
if [ "$DRY_RUN" = true ]; then
if [ -d "$dest" ] && [ ! -L "$dest" ]; then
echo "[DRY RUN] CONFLICT: $label -> $dest (directory)"
elif [ -e "$dest" ] || [ -L "$dest" ]; then
echo "[DRY RUN] CONFLICT: $label -> $dest (exists)"
else
echo "[DRY RUN] SYMLINK: $label -> $dest"
fi
return
fi
if [ -d "$dest" ] && [ ! -L "$dest" ]; then
echo "ERROR: $dest is a directory; remove it before installing $label"
return 1
fi
if { [ -e "$dest" ] || [ -L "$dest" ]; } && [ "$FORCE" != true ]; then
echo "EXISTS: $dest"
read -r -p " [s]kip, [o]verwrite? " choice
case $choice in
o|O)
ln -sf "$src" "$dest"
echo " -> Symlinked (overwritten)."
;;
*)
echo " -> Skipped."
;;
esac
else
mkdir -p "$(dirname "$dest")"
ln -sf "$src" "$dest"
echo "INSTALLED: $label -> $dest (symlink)"
fi
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@install.sh` around lines 374 - 405, The install_symlink function can
accidentally create a symlink inside an existing directory because ln -sf on a
directory targets the directory rather than replacing it; before each ln -sf
(the one in the overwrite branch and the one in the normal install branch) add
an explicit directory check for dest (e.g. [ -d "$dest" ]) and treat it as a
conflict: prompt the user (or respect FORCE) to either remove the directory (rm
-rf "$dest") then create the symlink, or skip; update the logic around the
overwrite branch (where choice o|O triggers ln -sf) and the normal branch (where
ln -sf is used) to handle directory-dest safely so install_symlink always
replaces or skips a directory rather than creating a symlink inside it.

Copy link
Copy Markdown
Owner

@john-wilmes john-wilmes left a comment

Choose a reason for hiding this comment

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

Review

The core changes (symlink install, SQLite consolidation, auto-promote) are sound and address real gaps. A few issues to address before merge:

Must fix

  1. Drop MCP registry committemplates/registry/mcp-servers.json changes reference machine-specific paths (PROJECTS/core/datadog-sanitizer/server.py, ~/.mcp/datadog-launch.sh, Keychain service names). These are specific to your local setup and don't belong in the shared template. Please revert commit eeecfcfc or make the notes generic.

  2. Shell injection in knowledge-consolidate.sh — The Node -e inline scripts embed bash variables directly into JS strings:

    fs.readFileSync('${JSONL_TMP}', ...)

    If the mktemp path contains a single quote, this breaks. Pass values via process.argv instead of string interpolation. Same issue with the ${tool} variable on the line below.

  3. Silent catch {} in session-end.js — Lines 756 and 777 swallow all errors silently. If the promotion logic fails, there's zero visibility. At minimum log to stderr:

    } catch (e) {
      process.stderr.write(`[session-end] auto-promote error: ${e.message}\n`);
      return 0;
    }

Should fix

  1. No tests for auto-promote_generateEntryId, _mapStagedToEntry, and autoPromoteStagedCandidates are 80+ lines of new logic with no test coverage. The first two are pure functions — easy to unit test.

Looks good

  • install.sh symlink approach with -L check in uninstall — clean
  • knowledge-consolidate.sh migration from flat files to SQLite export — correct fix since entries/ no longer exists
  • SKILL.md auto-close after synthesis — good UX improvement
  • Duplicate prevention via first-line context check in auto-promote — reasonable dedup strategy

- Shell injection in knowledge-consolidate.sh: pass JSONL_TMP and tool
  name via process.argv instead of string-interpolating into node -e JS
- Auto-promote errors now logged to stderr instead of silently swallowed
- install_symlink: remove dest file/symlink before ln -sf to prevent
  symlink being created inside a directory destination
- mcp-servers.json: replace machine-specific paths with generic
  placeholders to make the template usable for all users
- Add unit tests for _mapStagedToEntry and _generateEntryId (exported
  via module.exports guard at bottom of session-end.js)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@john-wilmes-luma-health
Copy link
Copy Markdown
Contributor Author

Addressed all findings in the latest commit: (1) shell injection fixed by passing vars via process.argv, (2) auto-promote errors now logged to stderr, (3) install_symlink removes dest before ln -sf, (4) mcp-servers.json genericized, (5) unit tests added for _mapStagedToEntry and _generateEntryId

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
templates/hooks/session-end.js (1)

144-145: Minor: Redundant null check.

Line 145 (if (!snippet) return null) makes line 144's !snippet portion redundant. The first check only adds value when !summary && snippet is false but both are empty strings after trim—which line 145 already handles.

Consider simplifying:

♻️ Suggested simplification
-  if (!summary && !snippet) return null;
-  if (!snippet) return null;
+  if (!snippet) return null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@templates/hooks/session-end.js` around lines 144 - 145, Trim/normalize both
summary and snippet first, then remove the redundant combined check and keep a
single null-return check for snippet (i.e., delete the if (!summary && !snippet)
branch and retain only if (!snippet) return null), referencing the summary and
snippet variables in the session-end hook so the logic only runs after trimming.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/knowledge-consolidate.sh`:
- Around line 182-199: The loop currently always archives the second parsed id
(variables id1/id2) using node "${KNOWLEDGE_DB_JS}" archive "${id2}", which can
be wrong if the model meant id2 to supersede id1; update the handler to respect
an explicit keep indicator from Claude (e.g., parse an added "KEEP:" or
"ARCHIVE:" token from the ANALYSIS lines) or change the prompt so Claude always
lists the entry-to-archive second, then branch on that token before calling the
archive command (use the existing variables id1/id2 and the DRY_RUN check to
decide which id to pass to node "${KNOWLEDGE_DB_JS}" archive and adjust
ARCHIVED_COUNT/RECOMMENDATION_COUNT logging accordingly).

In `@templates/hooks/session-end.js`:
- Around line 161-209: The autoPromoteStagedCandidates flow can leave the DB in
an inconsistent state because it calls knowledgeDb.insertEntry repeatedly then
does a final DELETE from staged_candidates without a transaction; wrap the
entire promotion process (the loop that maps candidates, uniqueness checks using
db.prepare("SELECT id FROM entries WHERE id = ?"), the inserts via
knowledgeDb.insertEntry, and the final DELETE of promotedIds) in a single
database transaction (BEGIN/COMMIT/ROLLBACK or db.transaction helper) so that
any error aborts and rolls back all inserts and the delete; ensure the
id-generation/duplicate-check (the while loop that calls
db.prepare(...).get(id)) and the update of existingTitles happen inside that
same transaction and that errors trigger a rollback and return 0 as before.

---

Nitpick comments:
In `@templates/hooks/session-end.js`:
- Around line 144-145: Trim/normalize both summary and snippet first, then
remove the redundant combined check and keep a single null-return check for
snippet (i.e., delete the if (!summary && !snippet) branch and retain only if
(!snippet) return null), referencing the summary and snippet variables in the
session-end hook so the logic only runs after trimming.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a797452-c16f-42ab-9ceb-a21df0ec0309

📥 Commits

Reviewing files that changed from the base of the PR and between 934a080 and 76cce6e.

📒 Files selected for processing (5)
  • install.sh
  • scripts/knowledge-consolidate.sh
  • templates/hooks/session-end.js
  • templates/registry/mcp-servers.json
  • tests/hooks/session-end-promote.test.js
✅ Files skipped from review due to trivial changes (1)
  • tests/hooks/session-end-promote.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • install.sh
  • templates/registry/mcp-servers.json

Comment thread scripts/knowledge-consolidate.sh
Comment thread templates/hooks/session-end.js
…eckpoint loop

At claude-loop wakeup, SessionStart injects a large context (memory, CLAUDE.md,
lessons) that can push token usage past 60% before any real work is done.
The first tool call then triggers the BLOCK advisory, /checkpoint runs,
claude-loop restarts, and the cycle repeats indefinitely.

Fix: introduce MIN_CALLS_BEFORE_BLOCK=5 — BLOCK and FAILSAFE thresholds are
suppressed for the first 5 tool calls of a session. WARN thresholds (35%/50%)
still fire immediately so the agent is aware of high baseline usage.

Tests: added primeSession() helper to bypass the grace period in tests that
expect BLOCK/FAILSAFE behavior; all 25 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- session-end: wrap auto-promote in BEGIN/COMMIT transaction for crash safety
- knowledge-consolidate: clarify id1=keep id2=archive in Claude prompt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@john-wilmes
Copy link
Copy Markdown
Owner

Superseded by #17, which includes all commits from this PR plus review fixes and the context-guard grace period fix.

john-wilmes pushed a commit that referenced this pull request Apr 5, 2026
- Remove redundant null check in _mapStagedToEntry (CodeRabbit nitpick)
- Fix install_symlink directory-dest safety: warn and skip in normal branch,
  rm -rf in overwrite branch (CodeRabbit actionable)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

2 participants