From a02d6ad792405af46f65cff38a51d2e4b2cea659 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sun, 7 Jun 2026 01:43:02 +0200 Subject: [PATCH 1/7] ci: route Linux checks to Hetzner runner Use the self-hosted Hetzner runner for Linux CI while keeping macOS and Windows on GitHub-hosted runners, and gate external PR usage behind maintainer approval. --- .github/workflows/ci.yml | 58 ++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f25bdc08e2..457b4e60f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,10 @@ name: CI on: push: - branches: [master] + branches: [master, feature/holographic-memory] pull_request: - branches: [master] + branches: [master, feature/holographic-memory] + types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] permissions: contents: read @@ -13,37 +14,40 @@ env: CARGO_TERM_COLOR: always jobs: + linux-test: + name: Test Linux + if: >- + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository || + contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') + runs-on: [self-hosted, linux, x64, hetzner] + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: v1-rust-test + + - name: Run tests + run: cargo test --workspace + test: name: Test ${{ matrix.name }} - runs-on: ${{ matrix.runner }} + runs-on: ${{ fromJSON(matrix.runner) }} strategy: fail-fast: false matrix: include: - - name: Linux - runner: ubuntu-latest - name: macOS - runner: macos-14 + runner: '"macos-14"' - name: Windows - runner: windows-latest + runner: '"windows-latest"' steps: - uses: actions/checkout@v4 - # The Linux runner ships with ~10 GB free, which our 713 MB cache restore - # plus the cargo target dir (34 tree-sitter parsers compiled in) can - # exceed. See v4.5.1 CI flake: "No space left on device" mid-test. - - name: Free disk space (Linux only) - if: matrix.name == 'Linux' - uses: jlumbroso/free-disk-space@v1.3.1 - with: - tool-cache: false - android: true - dotnet: true - haskell: true - large-packages: false - swap-storage: false - - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 @@ -55,7 +59,11 @@ jobs: clippy: name: Clippy - runs-on: ubuntu-latest + if: >- + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository || + contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') + runs-on: [self-hosted, linux, x64, hetzner] continue-on-error: true steps: - uses: actions/checkout@v4 @@ -67,7 +75,11 @@ jobs: fmt: name: Format - runs-on: ubuntu-latest + if: >- + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository || + contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') + runs-on: [self-hosted, linux, x64, hetzner] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable From 660597cf24f3891b1c51da3e05ce82bd0b8fd338 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sun, 7 Jun 2026 02:18:23 +0200 Subject: [PATCH 2/7] test(mcp): include write tools in annotation check Keep the handler-level annotation regression test aligned with tools that already declare readOnlyHint=false. --- src/mcp/tools/handlers/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mcp/tools/handlers/mod.rs b/src/mcp/tools/handlers/mod.rs index 4eb943ea69..b91b8656f9 100644 --- a/src/mcp/tools/handlers/mod.rs +++ b/src/mcp/tools/handlers/mod.rs @@ -341,7 +341,10 @@ mod tests { "tokensave_str_replace", "tokensave_multi_str_replace", "tokensave_insert_at", + "tokensave_replace_symbol", + "tokensave_insert_at_symbol", "tokensave_ast_grep_rewrite", + "tokensave_run_affected_tests", "tokensave_session_start", "tokensave_session_end", "tokensave_record_decision", From fc90729b3ce1a4cfb6889a194d8eb915d52857e2 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sun, 7 Jun 2026 02:22:48 +0200 Subject: [PATCH 3/7] test(mcp): wait for startup catch-up in watcher test Avoid racing the detached startup catch-up sync before asserting that a new file is stale. --- tests/mcp_handler_test.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/mcp_handler_test.rs b/tests/mcp_handler_test.rs index 8751e1df7b..d061176803 100644 --- a/tests/mcp_handler_test.rs +++ b/tests/mcp_handler_test.rs @@ -4616,6 +4616,12 @@ async fn mcp_server_owns_watcher_and_refreshes_token_map_on_change() { cg.sync().await.unwrap(); let server = tokensave::mcp::McpServer::new(cg, None).await; + assert!( + server + .wait_for_startup_catch_up(std::time::Duration::from_secs(2)) + .await, + "startup catch-up sync should finish before mutating the project" + ); let initial_count = server.file_token_map_snapshot().len(); From 27faafb3d4050dbd8da5a3af0d021458c8537938 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sun, 7 Jun 2026 02:35:10 +0200 Subject: [PATCH 4/7] ci: rerun after runner rebuild Restart CI after refreshing the self-hosted runner image and resources. From a65073e8825c344ed179b901ca589eecd5ac868b Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sun, 7 Jun 2026 02:41:26 +0200 Subject: [PATCH 5/7] chore: add Cursor MCP config Track the local Cursor MCP config needed for this branch's tokensave tooling. --- .cursor/mcp.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .cursor/mcp.json diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 0000000000..fd90a87e23 --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,10 @@ +{ + "mcpServers": { + "tokensave": { + "command": "/home/zack/projects/tokensave/.worktrees/holographic-memory/scripts/tokensave-dev-mcp.sh", + "env": { + "TOKENSAVE_DEV_PROJECT_ROOT": "/home/zack/projects/tokensave/.worktrees/holographic-memory" + } + } + } +} From 4689ebd460bf6fd5d28354d4d82d63a157624a67 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sun, 7 Jun 2026 02:46:02 +0200 Subject: [PATCH 6/7] ci: fail unapproved self-hosted PRs Add an explicit GitHub-hosted gate so external PRs cannot appear green before maintainers approve Hetzner Linux CI. --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 457b4e60f3..328f1dbcd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,19 @@ env: CARGO_TERM_COLOR: always jobs: + self-hosted-runner-approval: + name: Self-hosted runner approval + if: >- + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name != github.repository && + !contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') + runs-on: ubuntu-latest + steps: + - name: Block unapproved external PR + run: | + echo "::error::External PRs must be reviewed and labeled 'run-hetzner-ci' before Linux CI can use the Hetzner self-hosted runner." + exit 1 + linux-test: name: Test Linux if: >- From 2169aa6acf3d38540d04dbc954b0b1905d0fd57c Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sun, 7 Jun 2026 02:52:26 +0200 Subject: [PATCH 7/7] ci: use hosted Linux runners for public repo Keep standard GitHub-hosted Linux CI for the public tokensave fork, where hosted runner minutes are free. --- .github/workflows/ci.yml | 47 ++++------------------------------------ 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 328f1dbcd0..af5b3b4b8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,6 @@ on: branches: [master, feature/holographic-memory] pull_request: branches: [master, feature/holographic-memory] - types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] permissions: contents: read @@ -14,38 +13,6 @@ env: CARGO_TERM_COLOR: always jobs: - self-hosted-runner-approval: - name: Self-hosted runner approval - if: >- - github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name != github.repository && - !contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') - runs-on: ubuntu-latest - steps: - - name: Block unapproved external PR - run: | - echo "::error::External PRs must be reviewed and labeled 'run-hetzner-ci' before Linux CI can use the Hetzner self-hosted runner." - exit 1 - - linux-test: - name: Test Linux - if: >- - github.event_name == 'push' || - github.event.pull_request.head.repo.full_name == github.repository || - contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') - runs-on: [self-hosted, linux, x64, hetzner] - steps: - - uses: actions/checkout@v4 - - - uses: dtolnay/rust-toolchain@stable - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: v1-rust-test - - - name: Run tests - run: cargo test --workspace - test: name: Test ${{ matrix.name }} runs-on: ${{ fromJSON(matrix.runner) }} @@ -53,6 +20,8 @@ jobs: fail-fast: false matrix: include: + - name: Linux + runner: '"ubuntu-latest"' - name: macOS runner: '"macos-14"' - name: Windows @@ -72,11 +41,7 @@ jobs: clippy: name: Clippy - if: >- - github.event_name == 'push' || - github.event.pull_request.head.repo.full_name == github.repository || - contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') - runs-on: [self-hosted, linux, x64, hetzner] + runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v4 @@ -88,11 +53,7 @@ jobs: fmt: name: Format - if: >- - github.event_name == 'push' || - github.event.pull_request.head.repo.full_name == github.repository || - contains(github.event.pull_request.labels.*.name, 'run-hetzner-ci') - runs-on: [self-hosted, linux, x64, hetzner] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable