Skip to content

Make Claude Pilot Great Again#56

Merged
maxritter merged 6 commits intomainfrom
dev
Feb 16, 2026
Merged

Make Claude Pilot Great Again#56
maxritter merged 6 commits intomainfrom
dev

Conversation

@maxritter
Copy link
Owner

@maxritter maxritter commented Feb 16, 2026

Summary

Comprehensive release driven by user feedback (Issue #54) and architectural improvements:

  • Compaction-Based Endless Mode: Replaced the disruptive kill-and-restart cycle with Claude Code's built-in compaction. New PreCompact and SessionStart(compact) hooks preserve and restore session state seamlessly — no more process termination, no context loss
  • Friction Fixes (Issue Claude Pilot: Bug Reports & Friction Points from Early Trial #54): Installer no longer overwrites user settings (uses project-level config), removed slow type-checking from per-edit hooks, relaxed overly-strict agent tooling restrictions, hardened worktree CWD handling
  • Rules Overhaul: Consolidated 20+ scattered rule files into 10 focused modules (cli-tools, development-practices, research-tools, testing, verification, etc.) — reducing context cost and improving clarity
  • Bug Fixes: Simplified context monitor by removing unreliable session ID cross-checks, fixed installer Homebrew hang, streamlined /spec command dispatch

Plans

Test plan

  • All existing tests pass (uv run pytest -q, cd console && bun test)
  • New compaction hooks tested (pilot/hooks/tests/test_pre_compact.py, pilot/hooks/tests/test_post_compact_restore.py)
  • Installer preserves existing user settings on re-install
  • Context compaction triggers smoothly without session restart
  • /spec workflow completes end-to-end (plan → implement → verify)
  • Website renders correctly with updated copy

Summary by CodeRabbit

  • New Features

    • Added license gating and activation UI to Pilot Console with 60-second auto-refresh
    • Introduced new context preservation hooks and auto-compaction workflow for intelligent context management
  • Documentation

    • Replaced "Endless Mode" terminology with Pilot context management features (persistent memory, auto-compaction, context preservation hooks) throughout docs, marketing site, and guides
    • Removed Endless Mode documentation; updated all references to reflect new architecture
  • Chores

    • Updated installation process for improved settings management and configuration handling
    • Enhanced installer wrapper-based execution and local installer support

Replace Endless Mode's kill-and-restart cycle with Claude Code's built-in
compaction mechanism using PreCompact and SessionStart(compact) hooks for
seamless context preservation and restoration.

Key changes:
- New PreCompact hook captures session state to Pilot Memory before compaction
- New SessionStart(compact) hook re-injects plan state, task progress, and
  working files after compaction completes
- Wrapper stripped of all Endless Mode restart logic, retains license/update/
  crash-recovery duties only
- Installer no longer overwrites user settings — uses project-level config
- Rules consolidated from 20+ files into 10 focused modules (cli-tools,
  development-practices, research-tools, testing, verification, etc.)
- /spec commands streamlined with clearer phase dispatch and verification
- Tool redirect hook updated with relaxed agent tooling restrictions
- Context monitor simplified by removing unreliable session ID cross-checks
- Blog content updated to reflect compaction-based architecture
- Website copy refreshed across all sections

Plans: docs/plans/2026-02-16-compaction-based-endless-mode.md
       docs/plans/2026-02-16-issue-54-friction-fixes.md
@vercel
Copy link

vercel bot commented Feb 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
claude-pilot Ignored Ignored Feb 16, 2026 1:18pm

Request Review

@claude
Copy link

claude bot commented Feb 16, 2026

Claude finished @maxritter's task in 2m 23s —— View job


Code Review Complete

Reviewed PR #56: "We Make Pilots Great Again" (100 files, +2793/-5892)


Summary

This is a well-executed comprehensive release that addresses user feedback and introduces compaction-based endless mode. The changes are architecturally sound with good test coverage. I found several minor issues and recommendations but no blocking concerns.


Major Changes Reviewed

✅ Compaction-Based Endless Mode

Files: pilot/hooks/pre_compact.py, pilot/hooks/post_compact_restore.py, pilot/hooks/hooks.json

Strengths:

  • Clean implementation with dual-strategy persistence (HTTP API + local fallback)
  • Comprehensive test coverage (211 + 130 test lines)
  • Proper error handling with graceful degradation
  • Return code 2 for user-visible messages follows hook conventions

Issues Found:

  1. Minor: Inconsistent session ID sources (pilot/hooks/pre_compact.py:131, pilot/hooks/post_compact_restore.py:90)

    • Both hooks read session_id from hook_stdin AND fallback to PILOT_SESSION_ID env var
    • This is redundant since the hook system should always provide session_id via stdin
    • Recommendation: Document why dual-source is needed or remove env var fallback
  2. Minor: HTTP timeout hardcoded (pilot/hooks/pre_compact.py:109)

    • urllib.request.urlopen(req, timeout=5) uses 5-second timeout
    • If worker service is under load, this might fail prematurely
    • Recommendation: Consider increasing to 10 seconds or making configurable
  3. Performance: Task list capture uses glob (pilot/hooks/pre_compact.py:55)

    • list(tasks_dir.glob("*.json")) loads all task files just to count them
    • For sessions with many tasks, this is wasteful
    • Recommendation: Use len(list(tasks_dir.glob("*.json"))) or optimize with sum(1 for _ in tasks_dir.glob("*.json"))

✅ Installer Improvements

File: installer/steps/claude_files.py

Strengths:

  • Sophisticated settings conflict resolution with patch_global_settings() and patch_overlapping_settings()
  • Project-level settings take precedence over global settings
  • Comprehensive test coverage (621 test lines added)
  • App-level config merge preserves existing state

Issues Found:

  1. Minor: Silent failure on settings patch (installer/steps/claude_files.py:541-544)

    except (json.JSONDecodeError, OSError, IOError):
        continue
    • Settings patching failures are silently ignored
    • Users won't know if their settings.local.json isn't taking effect
    • Recommendation: Log warning to UI when patching fails
  2. Code quality: Repeated pattern (installer/steps/claude_files.py:259-272)

    • Multiple _clear_directory_safe calls with similar patterns
    • Could be refactored to reduce duplication
    • Recommendation: Consider a loop over directories to clear

✅ Rules Consolidation

Files: Consolidated 20+ files → 10 focused modules

Strengths:

  • Cleaner organization (cli-tools, development-practices, research-tools, etc.)
  • Reduced context cost
  • Better semantic grouping

Issues Found:

  1. Documentation: Migration impact unclear
    • README updated but no migration guide for users with custom rules
    • Users may have references to old rule paths in their custom configs
    • Recommendation: Add migration notes to release documentation

⚠️ Context Monitor Simplification

File: pilot/hooks/context_monitor.py

Strengths:

  • Removed ~90 lines of unreliable session ID cross-checks
  • Cleaner, more maintainable code
  • Proper throttling to reduce overhead

Issues Found:

  1. Critical: Stale cache detection removed (context_monitor.py:108-111)
    # OLD (removed):
    if current_cc_session and current_cc_session != cached_session_id:
        return None
    
    # NEW (remains):
    if ts is None or time.time() - ts > 60:
        return None
    • The session ID cross-check is removed as "unreliable"
    • But the 60-second staleness check alone may not catch session restarts
    • If compaction creates a new Claude Code session but reuses same Pilot session, stale data could persist for up to 60s
    • Recommendation: Verify this scenario is handled correctly or add comment explaining why it's safe

✅ Type Checking Removed from Post-Edit Hooks

Files: pilot/hooks/_checkers/python.py, pilot/hooks/_checkers/typescript.py

Strengths:

No issues found - this is a reasonable trade-off.

✅ Command Simplification

File: pilot/commands/spec.md

Strengths:

  • Removed complex context management (90% handoff, continuation files)
  • Relies on built-in compaction instead of kill-and-restart
  • Simpler mental model, fewer edge cases

Issues Found:

  1. Documentation: Transition guard removed (spec.md:119)
    • Section "0.3 Phase Transition Context Guard" completely removed
    • Section "0.4 Context Management (90% Handoff)" completely removed
    • But no explanation in the PR description or comments about why this is safe now
    • Recommendation: Add comment in command explaining that compaction replaces manual handoff

Security & Bug Analysis

Security ✅

  • No SQL injection, XSS, or OWASP Top 10 vulnerabilities found
  • HTTP requests use proper Content-Type headers
  • File operations use safe Path library methods
  • No credential exposure

Bugs Found

  1. Minor: Fallback file not cleaned up (pilot/hooks/post_compact_restore.py:47)

    fallback_file.unlink()
    • If unlink() raises OSError (permissions, file lock), the exception propagates
    • This could cause the hook to fail with exit code != 0
    • Recommendation: Wrap in try/except or use missing_ok=True (Python 3.8+)
  2. Minor: Potential race condition (pilot/hooks/pre_compact.py:143-144)

    saved_to_api = _save_to_worker_api(state, session_id)
    if not saved_to_api:
        _save_fallback_file(state, session_id)
    • If worker API succeeds but is slow, and process terminates before response returns, fallback won't be written
    • Post-compact restore might have no state to restore
    • Recommendation: Consider writing fallback file optimistically, then delete it if API succeeds

Test Coverage ✅

New Tests Added:

  • pilot/hooks/tests/test_pre_compact.py: 211 lines, 4 test classes
  • pilot/hooks/tests/test_post_compact_restore.py: 130 lines, includes performance test (<2s requirement)
  • installer/tests/unit/steps/test_claude_files.py: +619 lines

Coverage Assessment:

  • Compaction hooks: Excellent coverage (happy path, fallback, error cases)
  • Installer changes: Comprehensive unit tests for settings patching
  • Performance test ensures hooks don't block for too long

Missing Tests:

  • Integration test for full compaction cycle (pre → Claude compaction → post)
  • E2E test for worktree isolation with compaction
  • Recommendation: Add integration test in follow-up PR

Performance Considerations

  1. Minor: Installer clears directories unconditionally (claude_files.py:242-272)

    • _cleanup_old_directories() is called for every install, even re-installs
    • For large plugin directories, this adds overhead
    • Recommendation: Skip cleanup if source_is_destination OR if version hasn't changed
  2. Minor: Parallel file download not tuned (claude_files.py:393)

    • download_files_parallel() uses default parallelism
    • For slow connections or rate-limited APIs, this could be optimized
    • Recommendation: Consider tuning based on connection quality (future work)

Documentation

Website Changes:

  • Updated all references from "Claude Pilot" → "Claude Code" ✅
  • Removed obsolete endless-mode-explained.md blog post ✅
  • Updated component descriptions ✅

Issues:
13. Minor: Stale references in README (README.md)
- Need to verify all command examples still work
- Some sections may reference old context management behavior
- Recommendation: Manual review of README for accuracy


Recommendations Summary

Must Fix Before Merge

None - all issues are minor or suggestions.

Should Fix Before Merge

Consider for Follow-up


Verdict: ✅ Approve with Minor Fixes

This PR represents a significant architectural improvement. The compaction-based approach is cleaner than the previous kill-and-restart mechanism. Test coverage is strong. The identified issues are minor and don't block merging, but should be addressed in follow-up work.

Estimated Risk: Low
Breaking Changes: Yes (rules paths changed, requires user awareness)
Rollback Plan: Revert commit if compaction hooks cause issues


@maxritter maxritter changed the title We Make Pilots Great Again Make Claude Pilot Great Again Feb 16, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR replaces "Endless Mode" terminology with "Auto-Compaction" and "Persistent Memory" throughout documentation and UI components. It updates the installer to use project-local settings instead of global settings, removes mcp_servers.json generation, adds license gating to the console viewer, and enhances the installation script with wrapper-based execution and local installer support.

Changes

Cohort / File(s) Summary
Documentation - Terminology & Feature Updates
docs/site/index.html, docs/site/src/components/AgentRoster.tsx, docs/site/src/components/ComparisonSection.tsx, docs/site/src/components/FAQSection.tsx, docs/site/src/components/HeroSection.tsx, docs/site/src/components/PricingSection.tsx, docs/site/src/components/TestimonialsSection.tsx, docs/site/src/components/WorkflowSteps.tsx
Updated feature naming and descriptions across marketing/documentation components to replace "Endless Mode" with "Quality Hooks," "Persistent Memory," or "Context Preservation" depending on context; consolidated feature lists and updated user testimonials.
Hooks Pipeline & Standards Documentation
docs/site/src/components/DeepDiveSection.tsx, docs/site/src/content/blog/claude-code-hooks-guide.md
Reorganized hooks lifecycle with new stages (PreCompact, SessionEnd), updated triggers and descriptions to reflect auto-compaction and state capture/restoration mechanisms, replaced "Context Monitor & Endless Mode" with "Context Monitor & Auto-Compaction," and restructured rules categories (Quality Enforcement → Core Workflow, Context Management → Development Practices, etc.).
Blog Content - Context Management
docs/site/src/content/blog/endless-mode-explained.md, docs/site/src/content/blog/managing-context-long-sessions.md, docs/site/src/content/blog/persistent-memory-across-sessions.md, docs/site/src/content/blog/context-preservation.md, docs/site/src/content/blog/spec-driven-development.md, docs/site/src/content/blog/mcp-servers-claude-code.md, docs/site/src/content/blog/claude-code-task-management.md, docs/site/src/content/blog/choosing-the-right-claude-model.md
Removed entire Endless Mode explanation article; restructured context management documentation to emphasize auto-compaction, persistent memory, and context preservation hooks; updated thresholds (80/90% → 65%/75%+) and restoration workflows; replaced session handoff terminology with compaction-aware continuity.
Component Removals & Landing Page
docs/site/src/components/QualifierSection.tsx, docs/site/src/pages/Index.tsx, docs/site/src/components/WhatsInside.tsx
Removed QualifierSection component entirely; deleted its import from Index page; updated WhatsInside feature list by removing "Endless Mode" item and adding "Persistent Memory" with updated "Isolated Workspaces" description for auto-compaction survival.
License Gating - Viewer & Console
console/src/ui/viewer/App.tsx, console/src/ui/viewer/components/LicenseGate.tsx, console/src/ui/viewer/hooks/useLicense.ts, console/tests/ui/license-gate.test.ts, console/tests/context/cross-session-isolation.test.ts
Introduced license validation gating in App component with loading state handling; added new LicenseGate component with activation input, error handling, and conditional rendering based on license validity; added periodic license refresh via 60-second interval in useLicense hook; added comprehensive test suite for license gate UI and updated session isolation test scenario description.
Installer - Settings & Configuration
installer/steps/claude_files.py, installer/tests/unit/steps/test_claude_files.py
Changed settings file destination from global to project-local (settings.json → settings.local.json); added patch_global_settings() and merge_app_config() helper functions to reconcile global/local settings and app config; added new _merge_app_config(), _patch_overlapping_settings(), and _cleanup_stale_rules() methods to ClaudeFilesStep; extended post-install processing with global/local settings deconfliction and stale rule cleanup; added 600+ lines of tests covering settings patching, rule cleanup, and config merging scenarios.
Installer - Dependencies & MCP Server Management
installer/steps/dependencies.py, installer/tests/unit/steps/test_dependencies.py, installer/steps/config_files.py
Removed automatic Claude defaults patching (_patch_claude_config, _patch_claude_settings, _configure_claude_defaults); changed FORCE_CLAUDE_VERSION lookup from global to project-local settings file; removed mcp_servers.json creation and related config cleanup (_clean_mcp_servers_from_claude_config); simplified dependency installation flow to avoid side effects on global config.
Installation Script & Execution
install.sh
Added get_version_from_redirect() and get_installer_so_name() functions for platform-specific installer retrieval; enhanced get_latest_release() with fallback version extraction; extended download_installer() and download_pilot_binary() to compute platform suffixes and handle wrapper-based binary management; updated run_installer() to support local installer via USE_LOCAL_INSTALLER flag with PYTHONPATH handling; updated local install confirmation text (Claude → Pilot).
Configuration Files & Root Setup
.gitignore, mcp_servers.json, installer/cli.py, installer/ui.py, README.md
Added mcp_servers.json to .gitignore CLAUDE section; deleted mcp_servers.json file; improved license validation error handling in _validate_license_key() to extract and display pilot activation errors; updated installer UI banner replacing feature items (Endless Mode → Spec-Driven, Quality Hooks, Persistent Memory); comprehensive README updates replacing Endless Mode references with Pilot enhancements, persistent memory, and context management terminology throughout sections including Quick Mode, Pilot CLI, Hooks Pipeline, Built-in Rules, and testimonials.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant App as App Component
    participant License as useLicense Hook
    participant API as License API
    participant Gate as LicenseGate
    
    User->>App: Load Console Viewer
    App->>License: useLicense()
    License->>API: GET /api/license (periodic refresh)
    API-->>License: License Data
    License-->>App: License State + Loading
    
    alt License Data Loading
        App->>User: Show Loading Indicator
    else License Invalid/Expired
        App->>Gate: Render LicenseGate
        User->>Gate: Enter License Key
        Gate->>API: POST /api/license/activate
        API-->>Gate: Activation Success/Error
        Gate->>App: onActivated()
        App->>User: Render Main App
    else License Valid
        App->>User: Render Main App
    end
    
    Note over License: Auto-refresh every 60s
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • Issue #54: This PR addresses installer changes that move Claude settings to a project-local file (settings.local.json), remove automatic mcp_servers cleanup, and stop patching global Claude configs—directly resolving the install/upgrade and MCP server configuration breakage described in the issue.

Suggested labels

released

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (83 files):

⚔️ .gitignore (content)
⚔️ CHANGELOG.md (content)
⚔️ README.md (content)
⚔️ console/package.json (content)
⚔️ console/src/ui/viewer/App.tsx (content)
⚔️ console/src/ui/viewer/hooks/useLicense.ts (content)
⚔️ console/tests/context/cross-session-isolation.test.ts (content)
⚔️ docs/site/index.html (content)
⚔️ docs/site/src/components/AgentRoster.tsx (content)
⚔️ docs/site/src/components/ComparisonSection.tsx (content)
⚔️ docs/site/src/components/DeepDiveSection.tsx (content)
⚔️ docs/site/src/components/FAQSection.tsx (content)
⚔️ docs/site/src/components/HeroSection.tsx (content)
⚔️ docs/site/src/components/PricingSection.tsx (content)
⚔️ docs/site/src/components/TestimonialsSection.tsx (content)
⚔️ docs/site/src/components/WhatsInside.tsx (content)
⚔️ docs/site/src/components/WorkflowSteps.tsx (content)
⚔️ docs/site/src/content/blog/choosing-the-right-claude-model.md (content)
⚔️ docs/site/src/content/blog/claude-code-hooks-guide.md (content)
⚔️ docs/site/src/content/blog/claude-code-task-management.md (content)
⚔️ docs/site/src/content/blog/context-preservation.md (content)
⚔️ docs/site/src/content/blog/managing-context-long-sessions.md (content)
⚔️ docs/site/src/content/blog/mcp-servers-claude-code.md (content)
⚔️ docs/site/src/content/blog/persistent-memory-across-sessions.md (content)
⚔️ docs/site/src/content/blog/spec-driven-development.md (content)
⚔️ docs/site/src/pages/Index.tsx (content)
⚔️ install.sh (content)
⚔️ installer/__init__.py (content)
⚔️ installer/cli.py (content)
⚔️ installer/steps/claude_files.py (content)
⚔️ installer/steps/config_files.py (content)
⚔️ installer/steps/dependencies.py (content)
⚔️ installer/tests/unit/steps/test_claude_files.py (content)
⚔️ installer/tests/unit/steps/test_dependencies.py (content)
⚔️ installer/ui.py (content)
⚔️ launcher/__init__.py (content)
⚔️ launcher/auth.py (content)
⚔️ launcher/banner.py (content)
⚔️ launcher/claude_installer.py (content)
⚔️ launcher/cli.py (content)
⚔️ launcher/config.py (content)
⚔️ launcher/helper.py (content)
⚔️ launcher/statusline/formatter.py (content)
⚔️ launcher/statusline/tips.py (content)
⚔️ launcher/tests/unit/statusline/test_formatter.py (content)
⚔️ launcher/tests/unit/test_auth.py (content)
⚔️ launcher/tests/unit/test_claude_installer.py (content)
⚔️ launcher/tests/unit/test_cli.py (content)
⚔️ launcher/tests/unit/test_context_monitor.py (content)
⚔️ launcher/tests/unit/test_helper.py (content)
⚔️ launcher/tests/unit/test_session_end.py (content)
⚔️ launcher/tests/unit/test_tool_redirect.py (content)
⚔️ launcher/tests/unit/test_worktree.py (content)
⚔️ launcher/tests/unit/test_wrapper.py (content)
⚔️ launcher/utils.py (content)
⚔️ launcher/worktree.py (content)
⚔️ launcher/wrapper.py (content)
⚔️ pilot/agents/spec-reviewer-quality.md (content)
⚔️ pilot/commands/spec-implement.md (content)
⚔️ pilot/commands/spec-plan.md (content)
⚔️ pilot/commands/spec-verify.md (content)
⚔️ pilot/commands/spec.md (content)
⚔️ pilot/hooks/_checkers/python.py (content)
⚔️ pilot/hooks/_checkers/typescript.py (content)
⚔️ pilot/hooks/context_monitor.py (content)
⚔️ pilot/hooks/hooks.json (content)
⚔️ pilot/hooks/notify.py (content)
⚔️ pilot/hooks/session_end.py (content)
⚔️ pilot/hooks/tests/test_notify.py (content)
⚔️ pilot/hooks/tests/test_session_end.py (content)
⚔️ pilot/hooks/tests/test_tdd_enforcer.py (content)
⚔️ pilot/hooks/tool_redirect.py (content)
⚔️ pilot/package.json (content)
⚔️ pilot/plugin.json (content)
⚔️ pilot/rules/context-continuation.md (content)
⚔️ pilot/rules/playwright-cli.md (content)
⚔️ pilot/settings.json (content)
⚔️ pilot/tests/hooks/test_context_monitor.py (content)
⚔️ pilot/tests/hooks/test_python_checker.py (content)
⚔️ pilot/tests/hooks/test_session_end.py (content)
⚔️ pilot/tests/hooks/test_typescript_checker.py (content)
⚔️ pilot/ui/viewer-bundle.js (content)
⚔️ pilot/ui/viewer.css (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
Title check ❓ Inconclusive The title 'Make Claude Pilot Great Again' is vague and does not clearly describe the specific changes in the PR, such as the compaction-based Endless Mode or friction fixes. Consider using a more descriptive title that captures the main technical change, such as 'Replace Endless Mode with compaction-based context preservation' or 'Refactor context management with compaction hooks and installer improvements'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 86.02% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch dev
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

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

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
installer/steps/dependencies.py (1)

169-173: ⚠️ Potential issue | 🟡 Minor

Fallback version string may be misleading when npm install fails.

When _run_bash_with_retry fails but claude exists, line 172 returns actual_version or version. If _get_installed_claude_version() returns None (e.g., claude --version output is unexpected), the fallback is version — which is the forced version that just failed to install. Downstream code and the user message at line 532 would then report this forced version as installed, even though the actual installed version may differ.

Proposed fix
     if not _run_bash_with_retry(npm_cmd):
         if command_exists("claude"):
             actual_version = _get_installed_claude_version()
-            return True, actual_version or version
+            return True, actual_version or "unknown"
         return False, version
🤖 Fix all issues with AI agents
In `@docs/site/src/components/DeepDiveSection.tsx`:
- Line 170: The subtitle in DeepDiveSection.tsx is wrong: update the text that
currently reads "12 hooks across 6 lifecycle events — fire automatically at
every stage" to reflect the actual count from the hooksPipeline array (3 + 3 + 4
+ 1 + 2 + 2 = 15). Locate the subtitle string in the DeepDiveSection component
and change it to "15 hooks across 6 lifecycle events — fire automatically at
every stage" (or equivalent wording that uses 15) so it matches the
hooksPipeline definition.

In `@docs/site/src/content/blog/claude-code-hooks-guide.md`:
- Around line 163-165: The SessionEnd hook description is incorrect; update the
text for the SessionEnd item so it states that SessionEnd stops the worker when
no other sessions are active and sends session completion notifications (instead
of saying it saves session state for compaction); also, ensure the
compaction-related responsibilities are attributed to PreCompact (captures state
before compaction) and SessionStart(compact) (restores state after compaction)
so the list accurately reflects SessionEnd, PreCompact, and
SessionStart(compact).

In `@docs/site/src/content/blog/managing-context-long-sessions.md`:
- Line 104: Update the sentence "At 90%, auto-compaction triggers and state is
preserved via hooks." to clarify that the 90% threshold only fires Pilot's
state-preservation hooks in preparation for Claude Code's built-in
auto-compaction at roughly 95% (e.g., reword to "At 90%, state-preservation
hooks fire to prepare for Claude Code's built-in auto-compaction at ~95%"). Also
scan nearby lines (the earlier mention of "roughly 95% full") to ensure the two
sentences consistently state that Pilot triggers hooks at 90% while Claude Code
performs the actual compaction around 95%.

In `@README.md`:
- Line 434: Update the table row that currently reads the Backend cell as
"`**/models/**`, `**/routes/**`, `**/api/**`, etc" to use "etc." (add the
missing period). Locate the row containing the pipe-delimited entry beginning
with "| Backend | `**/models/**`, `**/routes/**`, `**/api/**`, etc |" and change
"etc" to "etc." so the sentence conforms to American English punctuation.
🧹 Nitpick comments (3)
installer/steps/claude_files.py (3)

484-512: Unused ctx parameter in _merge_app_config.

Ruff correctly flags that ctx is unused (ARG002). This method only reads from Path.home() and the installed template path — it never accesses ctx. Consider removing it or using it to derive paths for consistency with the other new methods.

Proposed fix
-    def _merge_app_config(self, ctx: InstallContext) -> None:
+    def _merge_app_config(self) -> None:

And update the call site at line 440:

-        self._merge_app_config(ctx)
+        self._merge_app_config()

53-80: patch_global_settings mutates the input dict — document or make explicit.

Both patch_global_settings and merge_app_config mutate the passed-in dicts in place and then return them (or None). The callers already rely on this behavior, but it's a subtle API contract. The current callers (_patch_overlapping_settings at line 540 and _merge_app_config at line 507) work correctly since they only use the return value for the is not None check, but the mutation-based design means the original dicts are always modified regardless of the return value.

This is currently safe because the dicts are freshly parsed from JSON on every call, but worth noting if these helpers are ever reused in a context where the caller needs the original dict preserved.


20-20: SETTINGS_FILE constant is defined but unused; hardcoded string appears in multiple locations.

Line 20 defines SETTINGS_FILE = "settings.local.json", but this constant is never used in the file. The hardcoded string appears at both line 425 and line 521. Use the constant in both locations for consistency.

Proposed fix
         elif category == "settings":
-            return ctx.project_dir / ".claude" / "settings.local.json"
+            return ctx.project_dir / ".claude" / SETTINGS_FILE
         local_settings_path = ctx.project_dir / ".claude" / "settings.local.json"
+        local_settings_path = ctx.project_dir / ".claude" / SETTINGS_FILE

### 1. Context Monitoring

A background hook continuously tracks context usage percentage. At 80%, it warns that context is getting high. At 90%, it triggers the handoff protocol.
A background hook continuously tracks context usage percentage. At 80%, it warns that context is getting high. At 90%, auto-compaction triggers and state is preserved via hooks.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Minor: Clarify the 90% threshold vs. actual autocompaction trigger.

Line 26–28 of this same post explains that Claude Code triggers autocompaction at "roughly 95% full." Line 104 then says "At 90%, auto-compaction triggers and state is preserved via hooks." This could imply Pilot itself triggers compaction at 90%, when in reality the hooks fire at 90% to prepare for Claude Code's built-in compaction at ~95%. Consider rewording to something like "At 90%, state preservation hooks fire in preparation for auto-compaction."

🤖 Prompt for AI Agents
In `@docs/site/src/content/blog/managing-context-long-sessions.md` at line 104,
Update the sentence "At 90%, auto-compaction triggers and state is preserved via
hooks." to clarify that the 90% threshold only fires Pilot's state-preservation
hooks in preparation for Claude Code's built-in auto-compaction at roughly 95%
(e.g., reword to "At 90%, state-preservation hooks fire to prepare for Claude
Code's built-in auto-compaction at ~95%"). Also scan nearby lines (the earlier
mention of "roughly 95% full") to ensure the two sentences consistently state
that Pilot triggers hooks at 90% while Claude Code performs the actual
compaction around 95%.

…ture

Auto-compaction is a built-in Claude Code feature. What Pilot adds are
hooks (PreCompact + SessionStart(compact)) that capture and restore
Pilot-specific state, and persistent memory that carries context across
sessions. Updated all website copy, testimonials, FAQ, README comparison
table, and agent descriptions to correctly attribute each.

Also fixes: hook count 12→15, SessionEnd hook description, 90%→83%
compaction threshold, unused ctx parameter, SETTINGS_FILE constant usage.
@claude
Copy link

claude bot commented Feb 16, 2026

Claude finished @maxritter's task in 1m 24s —— View job


Incremental Code Review — PR #56

Reviewing changes from commit 099778d (9 files changed)


✅ Issues Resolved

1. Hook Count Fixed

  • docs/site/src/components/DeepDiveSection.tsx:170 — Updated from "12 hooks" to "15 hooks" to match actual hooksPipeline definition
  • README.md:330 — Updated from "12 hooks" to "15 hooks"

2. SessionEnd Hook Description Corrected

  • docs/site/src/content/blog/claude-code-hooks-guide.md:165-166 — SessionEnd now correctly described as stopping worker daemon and sending completion notifications (not saving state for compaction)
  • Added PreCompact description: "Captures active plan, task progress, and key context to memory before compaction"
  • This accurately reflects the actual implementation

3. Context Threshold Clarified

  • docs/site/src/content/blog/managing-context-long-sessions.md:104 — Updated to clarify that hooks prepare at 80%+ for Claude Code's auto-compaction at ~83% (previously said "90% triggers compaction")
  • Warning threshold lowered from 80% to 75% to give earlier heads-up

4. Installer Code Quality Improvements

  • installer/steps/claude_files.py:425,521 — Now uses SETTINGS_FILE constant instead of hardcoded "settings.local.json" strings
  • installer/steps/claude_files.py:484 — Removed unused ctx parameter from _merge_app_config() (ARG002 lint fix)

5. Minor Punctuation Fix

  • README.md:434 — Added missing period after "etc" in Backend row of conditional rules table

🔍 New Issues Found

None — This commit is a clean-up pass that addresses all the documentation and code quality issues from the previous review. No new problems introduced.


📋 Still Open from Previous Review

The following issues from CodeRabbit's initial review remain unaddressed (not touched by this commit):

  1. installer/steps/dependencies.py:169-173 — Fallback version string may report forced version as "installed" when npm install fails but claude binary exists
  2. installer/steps/claude_files.py:484-512_merge_app_config mutation-based API could be documented better
  3. pilot/hooks/context_monitor.py:108-111 — Session ID staleness check removal needs verification that it's safe
  4. pilot/hooks/post_compact_restore.py:47fallback_file.unlink() could fail without try/except, causing hook to exit non-zero
  5. pilot/hooks/pre_compact.py:143-144 — Potential race condition if worker API is slow and process terminates before response
  6. Integration tests missing — No E2E test for full compaction cycle (pre → compact → post)

These are all minor/suggestions from the original review and don't block merging.


✨ Summary

This follow-up commit (099778d) is a high-quality cleanup that:

  • Fixes all documentation inaccuracies flagged in the first review
  • Improves code quality (constants usage, unused parameter removal)
  • Corrects messaging across website, README, and blog posts to accurately reflect Pilot's compaction behavior vs. Claude Code's built-in compaction

The changes are accurate, internally consistent, and well-targeted. No new issues introduced. The remaining open items from the previous review are implementation-level concerns in files not touched by this commit.

Recommendation: Approve this commit. The remaining open items can be addressed in follow-up work if needed.


Copy link

@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: 3

🤖 Fix all issues with AI agents
In `@docs/site/src/content/blog/claude-code-hooks-guide.md`:
- Line 163: Update the description for the "Context Monitor (PostToolUse)" entry
to use the consistent auto-compaction threshold of ~83% instead of 90%; ensure
the text in docs/site/src/content/blog/claude-code-hooks-guide.md matches the
references in DeepDiveSection.tsx and managing-context-long-sessions.md so all
mention the ~83% trigger for preparing auto-compaction.

In `@installer/steps/claude_files.py`:
- Around line 546-563: _cleanup_stale_rules currently only unlinks files and
ignores stale subdirectories under global_rules_dir; update the function to also
detect item.is_dir() and, for directories whose resolved path is not in the
installed set, remove them (use shutil.rmtree for non-empty dirs inside the same
try/except block used for item.unlink()) while preserving the existing
installed-path resolution logic and error swallowing behavior; reference the
global_rules_dir, installed set, and item variables when making the change.

In `@README.md`:
- Around line 378-383: The README incorrectly states auto-compaction fires at
"~83% context"; update that wording to "80% context" to match the implementation
constant THRESHOLD_AUTOCOMPACT in pilot/hooks/context_monitor.py so
documentation and code are consistent.
🧹 Nitpick comments (1)
installer/steps/claude_files.py (1)

53-80: patch_global_settings mutates its input dict in-place — document or copy.

The function deletes keys from global_settings and then returns the same object (or None). Callers today load a fresh dict from disk, so this works, but the contract is easy to misuse — a caller passing a shared dict would see silent side-effects regardless of the return value.

Consider either:

  • Documenting the in-place mutation clearly in the docstring, or
  • Operating on a shallow copy (global_settings = copy.copy(global_settings)) so the return value is the only way changes propagate.


- **TDD Enforcer** (PostToolUse): Reminds Claude to write tests before production code
- **Context Monitor** (PostToolUse): Tracks context usage and triggers session handoff at 90%
- **Context Monitor** (PostToolUse): Tracks context usage and prepares for auto-compaction at 90%
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent compaction threshold: 90% here vs. ~83% elsewhere.

This line says the Context Monitor "prepares for auto-compaction at 90%," but DeepDiveSection.tsx (line 61) and managing-context-long-sessions.md (line 104) both reference ~83% as the auto-compaction trigger. The commit messages also mention updating from 90% → 83%. Align this to ~83% for consistency.

Proposed fix
-- **Context Monitor** (PostToolUse): Tracks context usage and prepares for auto-compaction at 90%
+- **Context Monitor** (PostToolUse): Tracks context usage and prepares for auto-compaction at ~83%
📝 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
- **Context Monitor** (PostToolUse): Tracks context usage and prepares for auto-compaction at 90%
- **Context Monitor** (PostToolUse): Tracks context usage and prepares for auto-compaction at ~83%
🤖 Prompt for AI Agents
In `@docs/site/src/content/blog/claude-code-hooks-guide.md` at line 163, Update
the description for the "Context Monitor (PostToolUse)" entry to use the
consistent auto-compaction threshold of ~83% instead of 90%; ensure the text in
docs/site/src/content/blog/claude-code-hooks-guide.md matches the references in
DeepDiveSection.tsx and managing-context-long-sessions.md so all mention the
~83% trigger for preparing auto-compaction.

Comment on lines +546 to +563
def _cleanup_stale_rules(self, ctx: InstallContext) -> None:
"""Remove stale rule files from ~/.claude/rules/ not present in this installation.

~/.claude/rules/ is purely installer-managed (user rules go in project .claude/rules/).
Any file there that wasn't just installed is stale from a previous version.
"""
global_rules_dir = Path.home() / ".claude" / "rules"
if not global_rules_dir.exists():
return

installed = {Path(p).resolve() for p in ctx.config.get("installed_files", [])}

for item in global_rules_dir.iterdir():
if item.is_file() and item.resolve() not in installed:
try:
item.unlink()
except (OSError, IOError):
pass
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Stale subdirectories in ~/.claude/rules/ won't be cleaned up.

The loop on line 558 only removes files (item.is_file()). If a previous installer version created rule subdirectories (e.g., rules/standard/ — which _cleanup_legacy_project_dirs handles at the project level but not globally), those would persist. This is a minor gap since _cleanup_old_directories already nukes the entire ~/.claude/rules/ directory in the normal (non-local) flow, but in source_is_destination mode the cleanup is skipped and this method becomes the sole cleaner.

Consider extending to handle stale subdirectories as well:

Proposed fix
         for item in global_rules_dir.iterdir():
-            if item.is_file() and item.resolve() not in installed:
+            resolved = item.resolve()
+            if item.is_file() and resolved not in installed:
                 try:
                     item.unlink()
                 except (OSError, IOError):
                     pass
+            elif item.is_dir() and not any(
+                child.resolve() in installed for child in item.rglob("*")
+            ):
+                _clear_directory_safe(item)
🤖 Prompt for AI Agents
In `@installer/steps/claude_files.py` around lines 546 - 563, _cleanup_stale_rules
currently only unlinks files and ignores stale subdirectories under
global_rules_dir; update the function to also detect item.is_dir() and, for
directories whose resolved path is not in the installed set, remove them (use
shutil.rmtree for non-empty dirs inside the same try/except block used for
item.unlink()) while preserving the existing installed-path resolution logic and
error swallowing behavior; reference the global_rules_dir, installed set, and
item variables when making the change.

Comment on lines +378 to 383
The context monitor tracks usage in real-time, and auto-compaction handles context limits transparently:

- As context grows, Pilot warns, then forces a handoff before hitting limits
- Session state is saved to `~/.pilot/sessions/` with continuation files — picks up seamlessly in the next session
- During `/spec`, Pilot won't start a new phase when context is highit hands off instead
- At ~83% context, Claude's built-in compaction fires automatically — no process restart needed
- `pre_compact.py` captures Pilot state (active plan, tasks, key context) to persistent memory
- `post_compact_restore.py` re-injects Pilot context after compactionagent continues seamlessly
- Multiple Pilot sessions can run in parallel on the same project without interference
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate context monitor and compaction threshold constants/usages.
echo "=== Looking for context-related files ==="
fd -a 'context_monitor\.py|pre_compact\.py|post_compact_restore\.py|config\.py' . 2>/dev/null

echo -e "\n=== Searching for threshold and percentage values ==="
rg -n '(75|80|83|context|threshold|percent)' --type py -i . 2>/dev/null | head -100

Repository: maxritter/claude-pilot

Length of output: 9835


🏁 Script executed:

#!/bin/bash
# More targeted search for config and constant definitions
echo "=== Looking for all Python files in hooks directory ==="
find . -path '*/hooks/*.py' -type f 2>/dev/null

echo -e "\n=== Searching for percentage or threshold assignments ==="
rg -n '(:\s*0\.[0-9]+|%|threshold|context.*limit|compaction)' --type py . 2>/dev/null | head -80

Repository: maxritter/claude-pilot

Length of output: 5153


🏁 Script executed:

#!/bin/bash
# Read the context_monitor.py file to find threshold constants
echo "=== Reading context_monitor.py for threshold constants ==="
cat -n ./pilot/hooks/context_monitor.py | head -250

Repository: maxritter/claude-pilot

Length of output: 9020


🏁 Script executed:

#!/bin/bash
# Also read the test file to understand expected behavior
echo "=== Reading test_context_monitor.py ==="
cat -n ./pilot/tests/hooks/test_context_monitor.py

Repository: maxritter/claude-pilot

Length of output: 5419


🏁 Script executed:

#!/bin/bash
# Read README.md around the mentioned lines
echo "=== README.md lines 370-390 ==="
sed -n '370,390p' README.md

echo -e "\n=== Search for threshold mentions in README ==="
rg -n '(75|80|83|percent|context|threshold|compaction)' README.md -i | head -40

Repository: maxritter/claude-pilot

Length of output: 7558


Fix documentation: auto-compaction threshold is 80%, not ~83%.

The README correctly documents 75% and 80% thresholds in line 354, but line 380 incorrectly claims auto-compaction fires at "~83% context." The actual constant in pilot/hooks/context_monitor.py is THRESHOLD_AUTOCOMPACT = 80 (line 21). Update README line 380 to reflect the correct 80% threshold to match the implementation.

🤖 Prompt for AI Agents
In `@README.md` around lines 378 - 383, The README incorrectly states
auto-compaction fires at "~83% context"; update that wording to "80% context" to
match the implementation constant THRESHOLD_AUTOCOMPACT in
pilot/hooks/context_monitor.py so documentation and code are consistent.

@claude
Copy link

claude bot commented Feb 16, 2026

Claude finished @maxritter's task in 1m 54s —— View job


Incremental Code Review — PR #56

Reviewing changes from commits 099778d and 08ed28b (30+ files changed)


✅ Issues Resolved

From Previous CodeRabbit Review:

  1. Hook count fixed

    • docs/site/src/components/DeepDiveSection.tsx:170 — Updated from "12 hooks" to "15 hooks"
    • README.md:330 — Also updated to "15 hooks"
  2. SessionEnd hook description corrected

    • docs/site/src/content/blog/claude-code-hooks-guide.md:166 — SessionEnd now correctly described as stopping worker daemon and sending completion notifications (not saving state)
    • PreCompact description added at line 165 with accurate state capture responsibilities
  3. Context threshold clarified

    • docs/site/src/content/blog/managing-context-long-sessions.md:104 — Updated to clarify that hooks prepare at 75% for Claude Code's auto-compaction at ~83%
    • Now consistent across docs
  4. Installer code quality improvements

    • installer/steps/claude_files.py:425, 521 — Now uses SETTINGS_FILE constant instead of hardcoded strings
    • installer/steps/claude_files.py:484 — Removed unused ctx parameter from _merge_app_config()
  5. README punctuation

    • README.md:434 — Added missing period after "etc" in Backend row

⚠️ New Issues Found

1. Context threshold discrepancyREADME.md:380
The README states "At ~83% context, Claude's built-in compaction fires" but the implementation constant is THRESHOLD_AUTOCOMPACT = 75 (pilot/hooks/context_monitor.py:21). The ~83% figure appears to be when Claude Code's actual compaction fires, while 75% is when Pilot's preparation hooks trigger.

Recommendation: Update README.md:380 to clarify both thresholds:

- At 75% context, Pilot's preparation hooks fire; Claude Code's auto-compaction follows at ~83%

Or match the implementation exactly:

- At 75% context, state-preservation hooks prepare for auto-compaction

Fix this →

2. New Console component needs accessibility reviewconsole/src/ui/viewer/components/LicenseGate.tsx
The new LicenseGate component looks well-implemented with proper keyboard handling (Enter key support) and error states. However:

  • Line 60: Unicode emoji literals (\u{1F6AB}, \u{1F512}) may not render consistently across platforms
  • Consider using SVG icons or icon library for better accessibility

Recommendation: Replace emoji Unicode with proper icon components or add role="img" and aria-label attributes.


🔍 Still Open from Previous Reviews

The following issues from earlier reviews were not addressed in this push:

  1. installer/steps/dependencies.py:169-173 — Fallback version string may report forced version as "installed" when npm install fails but claude binary exists
  2. installer/steps/claude_files.py:53-80patch_global_settings mutates input dict in-place without documentation
  3. installer/steps/claude_files.py:563_cleanup_stale_rules only removes files, not stale subdirectories
  4. pilot/hooks/context_monitor.py:108-111 — Session ID staleness check removal needs verification
  5. pilot/hooks/post_compact_restore.py:47fallback_file.unlink() could fail without proper error handling
  6. pilot/hooks/pre_compact.py:143-144 — Potential race condition in API save/fallback logic
  7. Missing integration tests — No E2E test for full compaction cycle

✨ Summary

This incremental push (099778d + 08ed28b) successfully addressed all 5 issues from the previous CodeRabbit review:

  • Documentation accuracy (hook counts, descriptions, thresholds)
  • Code quality (constants usage, unused parameters)
  • Punctuation fixes

New findings:

  • 1 minor documentation discrepancy (context threshold in README)
  • 1 accessibility suggestion (emoji rendering in new LicenseGate component)

Recommendation: This PR is in excellent shape. The one context threshold discrepancy should be clarified for accuracy, but it's not a blocker. The remaining open issues from previous reviews are all minor/suggestions and can be addressed in follow-up work.


Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
console/src/ui/viewer/hooks/useLicense.ts (1)

14-25: ⚠️ Potential issue | 🟡 Minor

Missing HTTP status check before parsing response JSON.

If the server returns a non-2xx status (e.g., 500), res.json() may throw or parse an unexpected body, silently leaving license as stale/null. Check res.ok before parsing.

Proposed fix
   const fetchLicense = useCallback((refresh = false) => {
     const url = refresh ? '/api/license?refresh=1' : '/api/license';
     fetch(url)
-      .then((res) => res.json())
+      .then((res) => {
+        if (!res.ok) throw new Error(`HTTP ${res.status}`);
+        return res.json();
+      })
       .then((data: LicenseResponse) => {
         setLicense(data);
         setIsLoading(false);
       })
       .catch(() => {
         setIsLoading(false);
       });
   }, []);
🤖 Fix all issues with AI agents
In `@console/src/ui/viewer/components/LicenseGate.tsx`:
- Around line 21-35: In LicenseGate's activation flow, don't call res.json()
unconditionally; first check res.ok on the Response returned by fetch in the
async handler inside the component (where res and data are used), and handle
non-2xx statuses separately: if !res.ok attempt to read res.text() (or safe JSON
parse) to extract a server message and call setError with a message including
the HTTP status and server text, otherwise parse JSON and proceed with the
existing success branch (setKey, setError(null), onActivated()) or failure
branch (setError(data.error ?? 'Activation failed')); ensure the catch block
still reports network/connection errors but not server-side 5xx as the same
message.
🧹 Nitpick comments (2)
console/src/ui/viewer/App.tsx (1)

85-101: License gating flow looks correct; consider a network-error state.

Hook call order is maintained before early returns — good. However, if the license fetch fails (network error), license remains null and the user sees LicenseGate with no context that it's a connectivity issue rather than a missing license. The useLicense hook doesn't surface an error state, so the user may enter a valid key, have that also fail, and only then see "Connection failed."

This is a minor UX gap — worth considering an explicit error/offline state in a follow-up.

docs/site/src/components/DeepDiveSection.tsx (1)

116-122: The ext field mixes file extensions and path glob patterns.

Lines 117-119 use actual file extensions (*.py, *.ts, *.go), but line 121 (Backend) uses directory-level globs (**/models/**, **/routes/**). Similarly, line 120 (Frontend) mixes both styles. Since the UI renders this as a monospace label (line 313), users may find the inconsistency confusing.

Consider using a more descriptive field name or normalizing the patterns.

Comment on lines +21 to +35
try {
const res = await fetch('/api/license/activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: trimmed }),
});
const data = await res.json();

if (data.success) {
setKey('');
setError(null);
onActivated();
} else {
setError(data.error ?? 'Activation failed');
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Non-2xx responses produce a misleading error message.

If the server returns a 500 or other non-2xx status with a non-JSON body, res.json() will throw and the catch block shows "Connection failed. Is the Pilot worker running?" — which is misleading for server errors. Check res.ok first.

Proposed fix
       const res = await fetch('/api/license/activate', {
         method: 'POST',
         headers: { 'Content-Type': 'application/json' },
         body: JSON.stringify({ key: trimmed }),
       });
+      if (!res.ok) {
+        setError(`Server error (${res.status}). Please try again.`);
+        return;
+      }
       const data = await res.json();
📝 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
try {
const res = await fetch('/api/license/activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: trimmed }),
});
const data = await res.json();
if (data.success) {
setKey('');
setError(null);
onActivated();
} else {
setError(data.error ?? 'Activation failed');
}
try {
const res = await fetch('/api/license/activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: trimmed }),
});
if (!res.ok) {
setError(`Server error (${res.status}). Please try again.`);
return;
}
const data = await res.json();
if (data.success) {
setKey('');
setError(null);
onActivated();
} else {
setError(data.error ?? 'Activation failed');
}
🤖 Prompt for AI Agents
In `@console/src/ui/viewer/components/LicenseGate.tsx` around lines 21 - 35, In
LicenseGate's activation flow, don't call res.json() unconditionally; first
check res.ok on the Response returned by fetch in the async handler inside the
component (where res and data are used), and handle non-2xx statuses separately:
if !res.ok attempt to read res.text() (or safe JSON parse) to extract a server
message and call setError with a message including the HTTP status and server
text, otherwise parse JSON and proceed with the existing success branch (setKey,
setError(null), onActivated()) or failure branch (setError(data.error ??
'Activation failed')); ensure the catch block still reports network/connection
errors but not server-side 5xx as the same message.

@claude
Copy link

claude bot commented Feb 16, 2026

Claude finished @maxritter's task in 2m 49s —— View job


Incremental Code Review — PR #56

Reviewing latest push (installer build system, workflows, and script improvements)


✅ Issues Resolved

None from the previous reviews were addressed in this push (different files changed).


🆕 New Issues Found

1. Minor: sys.path manipulation may affect user environmentinstaller/build.py:272

The wrapper script manipulates sys.path to remove directories containing an installer package:

sys.path = [p for p in sys.path if p and p != cwd and not (
    os.path.isdir(os.path.join(p, 'installer')) and 
    os.path.isfile(os.path.join(p, 'installer', '__init__.py'))
)]

Issue: If a user has a legitimate installer package in their Python environment, this will remove it from sys.path, potentially breaking other scripts that rely on it.

Better approach: Insert $SCRIPT_DIR at position 0 (which line 273 already does), and rely on Python's import priority. Python will check sys.path[0] first, so no filtering is needed.

Fix this →

Impact: Low - affects users with an installer package in their environment (uncommon).


2. Code Quality: Silent failures in binary post-processinginstaller/build.py:245-256

The compile_merged() function silently ignores failures from strip, xattr, and codesign:

try:
    subprocess.run(["strip", str(compiled)], capture_output=True)
except (FileNotFoundError, OSError):
    pass  # No logging

if platform.system() == "Darwin":
    try:
        subprocess.run(["xattr", "-c", str(compiled)], capture_output=True)
        subprocess.run(["codesign", "-s", "-", str(compiled)], capture_output=True)
        print("  Applied macOS ad-hoc signing")
    except (FileNotFoundError, OSError):
        pass  # No logging

Issue: Users won't know if their binary isn't stripped (larger file size) or if macOS signing failed (may cause Gatekeeper issues).

Recommendation: Log warnings for these failures, or document why silent failure is acceptable.


3. Documentation: Unclear why sys.path filtering is neededinstaller/build.py:262-278

The wrapper script comment says "runs the compiled installer module with uv-managed Python 3.12" but doesn't explain the sys.path filtering logic. This makes it hard to understand the intent.

Recommendation: Add a comment explaining why filtering is needed (or remove it per issue #1 above).


📋 Still Open from Previous Reviews

The following issues from earlier reviews remain unaddressed:

  1. installer/steps/dependencies.py:169-173 — Fallback version string may report forced version as "installed"
  2. installer/steps/claude_files.py:53-80patch_global_settings mutates input dict in-place without documentation
  3. installer/steps/claude_files.py:563_cleanup_stale_rules only removes files, not stale subdirectories
  4. pilot/hooks/context_monitor.py:108-111 — Session ID staleness check removal needs verification
  5. pilot/hooks/post_compact_restore.py:47fallback_file.unlink() could fail without error handling
  6. pilot/hooks/pre_compact.py:143-144 — Potential race condition in API save/fallback logic
  7. console/src/ui/viewer/hooks/useLicense.ts:14-25 — Missing HTTP status check before parsing JSON
  8. console/src/ui/viewer/components/LicenseGate.tsx:35 — Non-2xx responses produce misleading error
  9. Missing integration tests — No E2E test for full compaction cycle

✨ Summary

This push introduces a comprehensive Cython-based build system for the installer with excellent test coverage. The changes are well-structured and follow good practices.

What's New

  • installer/build.py (+388 lines): Merges installer modules into a single file and compiles with Cython
  • installer/tests/unit/test_build.py (+178 lines): Comprehensive tests for build process
  • Workflow updates: Both release and dev workflows now build and upload installer binaries for all platforms
  • install.sh improvements: Better platform detection and installer binary download

Issues Found

  • 3 minor issues in the new code (sys.path manipulation, silent failures, missing docs)
  • 9 issues still open from previous reviews (not touched by this push)

Code Quality Assessment

  • ✅ Good test coverage (178 lines of tests for 388 lines of code)
  • ✅ Clean separation of concerns (build logic separate from installer logic)
  • ✅ Platform-independent wrapper script (one script works for all platforms)
  • ✅ Proper error handling in most places
  • ⚠️ Some edge cases need attention (sys.path filtering, silent failures)

Recommendation: The new issues are minor and non-blocking. The PR is in good shape for merging. Address the minor issues in follow-up work if needed.


Copy link

@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

🤖 Fix all issues with AI agents
In `@install.sh`:
- Around line 267-279: download_installer (and the wrapper download block and
download_pilot_binary) silently do nothing if neither curl nor wget exists; add
an else branch after the curl/wget checks to print an error and exit. Update the
blocks in download_installer, the wrapper download section, and
download_pilot_binary to mirror download_file's behavior: detect absence of both
tools, echo a clear failure message (e.g., "[!!] curl or wget required to
download installer/wrapper/pilot") and exit 1 so the script fails fast when no
downloader is available.
🧹 Nitpick comments (6)
install.sh (2)

65-71: http_code is assigned but never read — dead code.

The http_code variable (declared on Line 65, assigned on Line 70) is never used. This was likely intended for rate-limit or error-code checking that wasn't implemented. Either remove it or add the intended check.

🧹 Proposed fix
 get_latest_release() {
 	local api_url="https://api.github.com/repos/${REPO}/releases/latest"
 	local version=""
-	local http_code=""
 
 	if command -v curl >/dev/null 2>&1; then
 		local response=""
-		response=$(curl -sL -w "\n%{http_code}" "$api_url" 2>/dev/null) || true
-		http_code=$(echo "$response" | tail -1)
-		version=$(echo "$response" | sed '$d' | grep -m1 '"tag_name"' | sed 's/.*"v\([^"]*\)".*/\1/') || true
+		response=$(curl -sL "$api_url" 2>/dev/null) || true
+		version=$(echo "$response" | grep -m1 '"tag_name"' | sed 's/.*"v\([^"]*\)".*/\1/') || true

459-460: Declare and assign separately to avoid masking return values (SC2155).

If $(pwd) were to fail, export would mask its non-zero exit code. Low risk in practice, but easy to fix.

🧹 Proposed fix
 	if [ "$USE_LOCAL_INSTALLER" = true ]; then
-		export PYTHONPATH="$(pwd):${PYTHONPATH:-}"
+		local _pwd
+		_pwd="$(pwd)"
+		export PYTHONPATH="${_pwd}:${PYTHONPATH:-}"
installer/build.py (4)

295-300: Broad except Exception: pass silently swallows errors.

If codesign fails for a reason other than "not found" (e.g., signing identity issue, permission error), the failure is silently ignored. Consider logging at debug level.

🧹 Proposed fix
         try:
             subprocess.run(["xattr", "-c", str(dst_so)], capture_output=True)
             subprocess.run(["codesign", "-s", "-", str(dst_so)], capture_output=True)
-        except Exception:
-            pass
+        except (FileNotFoundError, OSError):
+            pass

370-370: F-string without any placeholders.

Static analysis flags this as F541. Remove the f prefix.

🧹 Proposed fix
-            print(f"\n✓ Release artifacts:")
+            print("\n✓ Release artifacts:")

73-94: set_build_timestamp / reset_build_timestamp mutate source files in-place — beware of concurrent builds.

These functions rewrite __init__.py during the build. The finally block in main() ensures reset, which is good. However, if two local builds run simultaneously, they'll race on the same file. Not a problem in CI (isolated checkouts), but worth a comment for local dev safety.


162-167: Docstring stripping regex may not match multi-line docstrings.

Line 167's re.sub(r'^"""[^"]*"""\s*', "", code, count=1) uses [^"]* which won't cross line boundaries (no re.DOTALL) and won't match docstrings containing quote characters. If any module has a multi-line file-level docstring, it will remain in the merged output. This is benign (extra docstring in compiled code) but may not match intent.

🧹 Fix to handle multi-line docstrings
-        code = re.sub(r'^"""[^"]*"""\s*', "", code, count=1)
+        code = re.sub(r'^""".*?"""\s*', "", code, count=1, flags=re.DOTALL)

Comment on lines 267 to 279
if command -v curl >/dev/null 2>&1; then
tree_json=$(curl -fsSL "$api_url" 2>/dev/null) || true
if ! curl -fsSL "$so_url" -o "$so_path" 2>/dev/null; then
echo " [!!] Failed to download installer binary"
echo " [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
exit 1
fi
elif command -v wget >/dev/null 2>&1; then
tree_json=$(wget -qO- "$api_url" 2>/dev/null) || true
if ! wget -q "$so_url" -O "$so_path" 2>/dev/null; then
echo " [!!] Failed to download installer binary"
echo " [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
exit 1
fi
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Silent fall-through if neither curl nor wget is available.

In download_installer, if neither curl nor wget is found, execution silently continues past the download block. The binary won't be downloaded, but the script won't fail until later (or worse, succeeds with an empty/missing file). Compare with download_file (Line 129-132) which explicitly checks and exits.

🛡️ Proposed fix — add an else clause
 	if command -v curl >/dev/null 2>&1; then
 		if ! curl -fsSL "$so_url" -o "$so_path" 2>/dev/null; then
 			echo "  [!!] Failed to download installer binary"
 			echo "  [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
 			exit 1
 		fi
 	elif command -v wget >/dev/null 2>&1; then
 		if ! wget -q "$so_url" -O "$so_path" 2>/dev/null; then
 			echo "  [!!] Failed to download installer binary"
 			echo "  [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
 			exit 1
 		fi
+	else
+		echo "  [!!] Error: Neither curl nor wget found"
+		exit 1
 	fi

The same issue exists in the wrapper download block (Lines 286-298) and in download_pilot_binary (Lines 382-392, 399-411).

📝 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
if command -v curl >/dev/null 2>&1; then
tree_json=$(curl -fsSL "$api_url" 2>/dev/null) || true
if ! curl -fsSL "$so_url" -o "$so_path" 2>/dev/null; then
echo " [!!] Failed to download installer binary"
echo " [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
exit 1
fi
elif command -v wget >/dev/null 2>&1; then
tree_json=$(wget -qO- "$api_url" 2>/dev/null) || true
if ! wget -q "$so_url" -O "$so_path" 2>/dev/null; then
echo " [!!] Failed to download installer binary"
echo " [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
exit 1
fi
fi
if command -v curl >/dev/null 2>&1; then
if ! curl -fsSL "$so_url" -o "$so_path" 2>/dev/null; then
echo " [!!] Failed to download installer binary"
echo " [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
exit 1
fi
elif command -v wget >/dev/null 2>&1; then
if ! wget -q "$so_url" -O "$so_path" 2>/dev/null; then
echo " [!!] Failed to download installer binary"
echo " [!!] Please try again or specify a version: VERSION=X.Y.Z curl ... | bash"
exit 1
fi
else
echo " [!!] Error: Neither curl nor wget found"
exit 1
fi
🤖 Prompt for AI Agents
In `@install.sh` around lines 267 - 279, download_installer (and the wrapper
download block and download_pilot_binary) silently do nothing if neither curl
nor wget exists; add an else branch after the curl/wget checks to print an error
and exit. Update the blocks in download_installer, the wrapper download section,
and download_pilot_binary to mirror download_file's behavior: detect absence of
both tools, echo a clear failure message (e.g., "[!!] curl or wget required to
download installer/wrapper/pilot") and exit 1 so the script fails fast when no
downloader is available.

sync_worktree now handles diverged base branches automatically:
- Auto-stashes uncommitted changes on base branch before sync
- Rebases worktree branch onto base branch before squash merge
- Aborts rebase cleanly on conflict
- Restores stash in finally block even on failure
@claude
Copy link

claude bot commented Feb 16, 2026

Claude finished @maxritter's task in 1m 24s —— View job


Incremental Code Review — PR #56

Reviewing latest push: commits 47e6dd9 and 08ed28b (30+ files changed since last review)


✅ Issues Resolved

From Previous CodeRabbit Reviews:

  1. Hook count documentation fixed

    • docs/site/src/components/DeepDiveSection.tsx:170 — Updated from "12 hooks" to "15 hooks"
    • README.md — Also updated to "15 hooks"
  2. SessionEnd hook description corrected

    • docs/site/src/content/blog/claude-code-hooks-guide.md:166 — SessionEnd now correctly described as stopping worker daemon and sending completion notifications
    • PreCompact description added with accurate state capture responsibilities
  3. Context threshold clarified

    • docs/site/src/content/blog/managing-context-long-sessions.md:104 — Updated to clarify that hooks prepare at 75% for Claude Code's auto-compaction at ~83%
    • Now consistent across all documentation
  4. Installer code quality improvements

    • installer/steps/claude_files.py — Now uses SETTINGS_FILE constant instead of hardcoded strings (lines 425, 521)
    • Removed unused ctx parameter from _merge_app_config() (line 484)
  5. README punctuation

    • README.md:434 — Added missing period after "etc" in Backend row
  6. Install script messaging

    • install.sh:144 — Updated from "Add 'claude' command" to "Add 'pilot' and 'ccp' command" to match actual behavior

🔍 New Issues Found

None — The latest commits (47e6dd9, 08ed28b) are clean-up and bug-fix commits that:

  • Fix worktree branch rebasing logic (launcher/* - encrypted, skipped per review instructions)
  • Address all documentation inaccuracies from previous reviews
  • Improve code quality (constants usage, parameter cleanup)
  • Correct messaging across website, README, and blog posts

No new problems introduced.


📋 Still Open from Previous Reviews

The following issues from earlier CodeRabbit reviews remain unaddressed (not touched by these commits):

  1. installer/steps/dependencies.py:169-173 — Fallback version string may report forced version as "installed" when npm install fails
  2. installer/steps/claude_files.py:53-80patch_global_settings mutates input dict in-place without documentation
  3. installer/steps/claude_files.py:563_cleanup_stale_rules only removes files, not stale subdirectories
  4. pilot/hooks/context_monitor.py:108-111 — Session ID staleness check removal needs verification
  5. pilot/hooks/post_compact_restore.py:47fallback_file.unlink() could fail without error handling
  6. pilot/hooks/pre_compact.py:143-144 — Potential race condition in API save/fallback logic
  7. console/src/ui/viewer/hooks/useLicense.ts:14-25 — Missing HTTP status check before parsing JSON
  8. console/src/ui/viewer/components/LicenseGate.tsx:35 — Non-2xx responses produce misleading error message
  9. Missing integration tests — No E2E test for full compaction cycle

These are all minor/suggestions and don't block merging.


✨ Summary

The latest commits (47e6dd9 + 08ed28b) successfully resolved 6 documentation and code quality issues from previous reviews:

  • Fixed all hook count discrepancies
  • Corrected SessionEnd/PreCompact descriptions
  • Updated context threshold documentation (75% → 83%)
  • Improved code quality (constants, parameter cleanup)
  • Fixed install script messaging

Changes are accurate, internally consistent, and well-targeted. No new issues introduced.

The 9 remaining open items from previous reviews are implementation-level concerns in files not touched by these commits and can be addressed in follow-up work if needed.

Recommendation: These incremental changes are high quality and ready to merge. The remaining open items are minor and don't block this PR.


…tivation error masking

- Replace "Intelligent Context" with "Persistent Memory" as headline feature
- Reorder features: Spec-Driven first, Persistent Memory last
- Merge Built-in Rules and Coding Standards into single section
- Compress hooks pipeline into 3-column grid on website
- Remove QualifierSection from website
- Rename Context7 to lib-docs in MCP servers display
- Fix license activation: PILOT- keys no longer fall through to Gumroad
  on network errors, preventing misleading "Invalid license key" message
- Add retry with 2s delay for Polar network errors
- Fix installer reading JSON error from stdout instead of empty stderr
@claude
Copy link

claude bot commented Feb 16, 2026

Incremental Code Review — PR #56

Reviewing latest push: changes to README.md, docs/site files, installer files

Todo List

  • Read changed files from latest push
  • Compare against previous review feedback
  • Identify resolved issues
  • Find new issues in changed files
  • Post incremental review

View job run

@maxritter maxritter merged commit c51e778 into main Feb 16, 2026
5 of 12 checks passed
Copy link

@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

🤖 Fix all issues with AI agents
In `@docs/site/src/components/DeepDiveSection.tsx`:
- Line 107: The Tools rules array in DeepDiveSection (the entry "Context7"
inside the rules: [...] list) is inconsistent with the MCP servers name
"lib-docs" used elsewhere; update the rules array to replace "Context7" with
"lib-docs" so the Tools rules string reads e.g. "lib-docs + grep-mcp + web
search + GitHub CLI" to match the MCP servers naming (locate the rules array in
the DeepDiveSection component and update that specific string).

category: "Tool Integration",
rules: ["Vexor semantic search", "Context7 library docs", "grep-mcp GitHub search", "Web search + fetch", "Playwright CLI (E2E)", "MCP CLI"],
category: "Tools",
rules: ["Context7 + grep-mcp + web search + GitHub CLI", "Pilot CLI + MCP-CLI + Vexor search", "Playwright CLI (E2E browser testing)"],
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent naming: "Context7" in rules vs. "lib-docs" in MCP servers.

The Tools rules category on Line 107 references "Context7" but the MCP servers list on Line 127 was renamed to "lib-docs". These should be consistent.

Proposed fix
-    rules: ["Context7 + grep-mcp + web search + GitHub CLI", "Pilot CLI + MCP-CLI + Vexor search", "Playwright CLI (E2E browser testing)"],
+    rules: ["lib-docs + grep-mcp + web search + GitHub CLI", "Pilot CLI + MCP-CLI + Vexor search", "Playwright CLI (E2E browser testing)"],
📝 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
rules: ["Context7 + grep-mcp + web search + GitHub CLI", "Pilot CLI + MCP-CLI + Vexor search", "Playwright CLI (E2E browser testing)"],
rules: ["lib-docs + grep-mcp + web search + GitHub CLI", "Pilot CLI + MCP-CLI + Vexor search", "Playwright CLI (E2E browser testing)"],
🤖 Prompt for AI Agents
In `@docs/site/src/components/DeepDiveSection.tsx` at line 107, The Tools rules
array in DeepDiveSection (the entry "Context7" inside the rules: [...] list) is
inconsistent with the MCP servers name "lib-docs" used elsewhere; update the
rules array to replace "Context7" with "lib-docs" so the Tools rules string
reads e.g. "lib-docs + grep-mcp + web search + GitHub CLI" to match the MCP
servers naming (locate the rules array in the DeepDiveSection component and
update that specific string).

maxritter added a commit that referenced this pull request Feb 16, 2026
…ugfixes (#56)

Replace Endless Mode's kill-and-restart cycle with Claude Code's built-in
compaction mechanism. PreCompact and SessionStart(compact) hooks capture
and restore Pilot-specific state seamlessly.

Key changes:
- Compaction hooks for context preservation (PreCompact + SessionStart)
- Rules consolidated from 20+ files into 10 focused modules
- Installer no longer overwrites user settings — uses project-level config
- Rebrand "Intelligent Context" to "Persistent Memory", reorder features
- Website: compress hooks grid, merge rules/standards sections, remove
  QualifierSection, refresh copy and correct attribution
- Worktree sync: auto-rebase onto base branch before squash merge
- License activation: PILOT- keys no longer mask network errors as
  "Invalid license key" via Gumroad fallback; add retry on failure
- Fix installer reading JSON error from stdout instead of stderr
- /spec commands streamlined with clearer phase dispatch
github-actions bot pushed a commit that referenced this pull request Feb 16, 2026
# [6.6.0](v6.5.9...v6.6.0) (2026-02-16)

### Features

* Compaction-based context preservation, branding overhaul, and bugfixes ([#56](#56)) ([5aa1bc1](5aa1bc1))
@maxritter maxritter deleted the dev branch February 16, 2026 15:11
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.

1 participant