Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
83e1eca
feat(platform): windows knows whether a pid is alive and when it started
Sunrisepeak Oct 2, 2026
ef638f5
fix(cache): a new clangd generation starts in a swept cache, and the …
Sunrisepeak Oct 2, 2026
4454184
feat(cache): instances describe themselves, and the cache answers on …
Sunrisepeak Oct 2, 2026
82679de
feat(editors): the cache shows in the status bar, and the hub sweeps …
Sunrisepeak Oct 2, 2026
a8ac189
fix(deps): openkal-musl is pinned to 0.19.1 until the vendored openka…
Sunrisepeak Oct 2, 2026
0d26b30
fix(deps): the openkal set is pinned exactly, not by caret
Sunrisepeak Oct 2, 2026
67358e1
fix(ci): mcpp is pinned to 2026.9.30.2, which reads an exact =x.y.z d…
Sunrisepeak Oct 2, 2026
079e418
fix(ci): the devtools binary is found in the root target, where mcpp …
Sunrisepeak Oct 2, 2026
d5d2608
fix(platform): the macos identity comes from ps lstart, not the apple…
Sunrisepeak Oct 2, 2026
c7c32dd
fix(platform): the windows process identity compiles, links and answe…
Sunrisepeak Oct 3, 2026
938cd8e
fix(deps,platform): the pins live in workspace.dependencies, and dllt…
Sunrisepeak Oct 3, 2026
3cf458a
fix(platform): the windows host runs dlltool through cmd's own quotin…
Sunrisepeak Oct 3, 2026
e507924
fix(server,editors): a cache_swept event names itself, and a sweep an…
Sunrisepeak Oct 3, 2026
e3e6b4e
fix(devtools): the clion feature is declared where the member that bu…
Sunrisepeak Oct 3, 2026
ed04a1b
fix(cli,conformance): the classified cache report renders, and the fi…
Sunrisepeak Oct 3, 2026
cec5748
fix(editors): package-lock carries the version set everywhere else al…
Sunrisepeak Oct 3, 2026
3e7163f
fix(platform): a zombie has no identity, so a dead owner's lease is t…
Sunrisepeak Oct 3, 2026
d6dda7f
fix(platform): a sandboxed server names itself by /proc/self, not by …
Sunrisepeak Oct 3, 2026
479181c
cache UI v2 and en/zh: a card in three zones, a hub in four groups th…
Sunrisepeak Oct 3, 2026
cbbfbe3
card v2.1: rows that actually break, and the table without opening th…
Sunrisepeak Oct 3, 2026
e721426
card v2.2: colored bars the sanitizer cannot touch, one grid for the …
Sunrisepeak Oct 3, 2026
d4e4a79
plan: record the v2 revisions the live review drove (breaks, colors, …
Sunrisepeak Oct 3, 2026
e036435
card v2.3: the dot-matrix bar back, as a tiny SVG the hover can actua…
Sunrisepeak Oct 3, 2026
f583882
card v2.4: back to the plain dot-matrix, one title line, layout rules…
Sunrisepeak Oct 3, 2026
c6389cc
card v2.5: the actions stacked one a line, so the table sets the pane…
Sunrisepeak Oct 3, 2026
d150b92
card v2.6: the actions back on one line, with names short enough to f…
Sunrisepeak Oct 3, 2026
0db610b
card v3: the body drawn as one SVG -- real layout for a surface that …
Sunrisepeak Oct 3, 2026
824b822
card v3.1: back to the markdown dot-matrix, with a footer whose two l…
Sunrisepeak Oct 3, 2026
2cb8d9a
card v3.2: footer names sized to the repository line, in both languages
Sunrisepeak Oct 3, 2026
639a7a5
card v3.3: the total row's label says Total -- the budget is the valu…
Sunrisepeak Oct 3, 2026
112f647
review pass: the card forgets a deleted cache, and the issue prompt n…
Sunrisepeak Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,073 changes: 1,073 additions & 0 deletions .agents/docs/2026-10-02-cache-growth-root-fix-plan.md

Large diffs are not rendered by default.

271 changes: 271 additions & 0 deletions .agents/docs/2026-10-03-cache-ui-v2-i18n-plan.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions .agents/docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ before anything is published (`docs/92-release.md`).
| RD11 | `std` is built with the standard library's own configuration macros only; the project's `-D`s never reach it (plan 2026-09-30 G-1) |
| RD12 | Module locks in the cache are the lease holder's: all are cleared before clangd starts, and one another process holds is removed when clangd waits on it (C-4) |
| RD13 | Each unit keeps the BMIs of its two newest commands; the rest are pruned when clangd starts (`mcppls cache --prune` for the others) (C-2) |
| RD18 | Every instance describes itself in the directory it works in (`instance.json`, heartbeat with the lease); a directory whose heartbeat is stale is renamed aside and removed, and one that says nothing waits for a 24-hour grace (C-9) |
| RD19 | The cache under `<cdb>/.cache/clangd` is the lease holder's: the copies of dead generations are swept before the next clangd starts, a per-workspace and a global budget are enforced by removing copies only, and a cache that cannot fit without the published BMIs is reported instead (C-7, C-8) |
| RD20 | The cache shows itself in the editor: one status item with a hover card and a menu, and the sweep behind them answers read-only, sweeps without stopping an engine, and never uploads anything (C-13) |
| RD21 | The editor's words follow the display language (English source, zh-cn bundle; `package.nls*` for the manifest, `l10n/` + a localizer seam for the runtime, key parity unit-tested) — no setting of our own; the server's logs, CLI and prompts stay English. The card is three zones (project, cache table with one bar a class, actions + repository link) and the hub four groups whose entries carry their behavior as data, never an icon to parse (plan 2026-10-03 UI-1..UI-14) |
| RD14 | What nothing recovers from by itself writes a redacted bundle at once and names it in the status; the person reports it, restarts, or turns mcppls off for the workspace (`mcppls.enable`), and nothing is uploaded (K-7) |
| PD1 | Android under Termux (PRoot) is a supported platform: openkal-linux falls back from `execveat` to `execve` and the server detects the sandbox (plan 2026-09-30 D1, X-1..X-5) |
| UD5 | In VS Code, C and C++ open the completion list while you type, alongside inline completions: the extension contributes `editor.quickSuggestions` `{other: "on"}` as their language default (WA-VSCODE-002), since VS Code 1.125's own default waits for inline completions; a person's `[cpp]` / `[c]` value wins, and one set for every language is overridden and told in the log (plan 0.0.8 E-1, E-2) |
Expand All @@ -270,6 +274,7 @@ before anything is published (`docs/92-release.md`).
22.04+, Debian 12+, openEuler 24.03+); elsewhere the status says `engine-incompatible` and only
module-level features remain. A clangd built for a lower floor is the way out, if those systems
turn out to matter (0.0.3 plan §5.2).
- A cache's peak is one engine generation's lifetime × the modules' size: mcppls sweeps what dead generations left when the next one starts and holds the rest under a budget, so a single 24-hour session that never restarts still grows inside its budget (plan 2026-10-03 C-8).
- clangd 23.1 rejects MSVC STL's aligned allocation; the plan turns aligned allocation off for
units using MSVC STL (`msvcStlNeedsNoAlignedAllocation`) until upstream fixes it.
- Every compensation for a clangd defect is a registered workaround (`WA-CLANGD-<n>`,
Expand Down
4 changes: 3 additions & 1 deletion .github/versions.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
#
# These are build inputs, not the product's version: that one lives in mcpp.toml and is checked
# everywhere else by `mcppls-devtools version --check`.
MCPP_VERSION=2026.9.26.1
# 2026.9.30.2: the first mcpp that reads an exact `=x.y.z` dependency pin, which the openkal set
# needs (see the root mcpp.toml: 0.15.4/0.20.1 float past the vendored openkal-linux).
MCPP_VERSION=2026.9.30.2
LLVM_VERSION=22.1.8
XLINGS_VERSION=v2026.8.17.2
# The xmake the xmake conformance fixtures run (installed from xlings; 0.0.8 part 2 X-7).
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
run: |
set -euo pipefail
for exe in mcppls mcppls-conformance mcppls-lspgen mcppls-devtools; do
path=$(find target tools/devtools/target -type f \( -name "$exe" -o -name "$exe.exe" \) -path '*/bin/*' | head -1)
path=$(find target -type f \( -name "$exe" -o -name "$exe.exe" \) -path '*/bin/*' | head -1)
echo "$exe -> $path"
if [ "$exe" = mcppls-devtools ]; then "$path" --help > /dev/null; else "$path" version; fi
done
Expand All @@ -105,7 +105,7 @@ jobs:
if: runner.os == 'Linux' && matrix.profile == 'dev'
run: |
set -euo pipefail
devtools=$(find tools/devtools/target -type f -name mcppls-devtools -path '*/bin/*' | head -1)
devtools=$(find target -type f -name mcppls-devtools -path '*/bin/*' | head -1)
server=$(find target -type f -name mcppls -path '*/bin/*' | head -1)
"$devtools" check all
"$devtools" check binary --server "$server"
Expand Down Expand Up @@ -143,7 +143,7 @@ jobs:
set -euo pipefail
mkdir -p cross
for exe in mcppls mcppls-conformance mcppls-lspgen mcppls-mock-mcpp mcppls-mock-model mcppls-devtools; do
path=$(find target tools/devtools/target -type f -name "$exe${{ matrix.exe }}" -path '*/bin/*' -newer mcpp.toml | head -1)
path=$(find target -type f -name "$exe${{ matrix.exe }}" -path '*/bin/*' -newer mcpp.toml | head -1)
file "$path"
case "${{ matrix.target }}" in
*windows*) file "$path" | grep -q 'PE32+ executable' ;;
Expand Down Expand Up @@ -209,7 +209,7 @@ jobs:
mcpp build -p devtools --profile release
mkdir -p cross
for exe in mcppls mcppls-conformance mcppls-mock-mcpp mcppls-mock-model mcppls-devtools; do
cp "$(find target tools/devtools/target -type f -name "$exe" -path '*/bin/*' -newer mcpp.toml | head -1)" cross/
cp "$(find target -type f -name "$exe" -path '*/bin/*' -newer mcpp.toml | head -1)" cross/
done
- name: The cross-built server
if: matrix.cross != ''
Expand All @@ -225,7 +225,7 @@ jobs:
set -euo pipefail
if [ "$RUNNER_OS" = Linux ] && [ -n '${{ matrix.cross }}' ] && [ -z '${{ matrix.cross-tools }}' ]; then
mcpp build -p devtools --profile release
cp "$(find tools/devtools/target -type f -name mcppls-devtools -path '*/bin/*' -newer mcpp.toml | head -1)" host-devtools
cp "$(find target -type f -name mcppls-devtools -path '*/bin/*' -newer mcpp.toml | head -1)" host-devtools
else
cp cross/mcppls-devtools host-devtools
fi
Expand Down Expand Up @@ -282,21 +282,21 @@ jobs:
part: 2 of 2
os: ubuntu-24.04
extras: true
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults untrusted mcpp-gcc mcpp-gcc@plain mcpp-emit-package-std mcpp-emit-broken mcpp-emit-unavailable mcpp-emit-hang mcpp-emit-needs-download mcpp-emit-provisioned mcpp-emit-watch mcpp-emit-watch@polling mcpp-emit-edits cmake-clang cmake-clang-bdb cmake-fetchcontent-offline watch-polling payload-corrupt clangd-cannot-load generated-module generated-module@vscode generated-module-old-mcpp generated-module-old-mcpp@neovim module-faults@zed failure-at-base failure-at-base@neovim completion-keywords diagnostic-bundle clangd-crash-context mcpp-emit-wait provisional-no-prime inferred-cxx26 reset-cache include-cleaner-modules no-modules tidy-const-views
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults untrusted mcpp-gcc mcpp-gcc@plain mcpp-emit-package-std mcpp-emit-broken mcpp-emit-unavailable mcpp-emit-hang mcpp-emit-needs-download mcpp-emit-provisioned mcpp-emit-watch mcpp-emit-watch@polling mcpp-emit-edits cmake-clang cmake-clang-bdb cmake-fetchcontent-offline watch-polling payload-corrupt clangd-cannot-load generated-module generated-module@vscode generated-module-old-mcpp generated-module-old-mcpp@neovim module-faults@zed failure-at-base failure-at-base@neovim completion-keywords diagnostic-bundle clangd-crash-context mcpp-emit-wait provisional-no-prime inferred-cxx26 reset-cache cache-budget include-cleaner-modules no-modules tidy-const-views
# generated-module{,-old-mcpp,-negotiated}'s mcpp-mock.json bakes in a POSIX driver path
# (${env:HOME}/.mcpp/registry/..., no {exe}); it resolves the same way here as on Linux, so
# these run on macOS but are left off win32-x64 below rather than fixed unverified.
- platform: darwin-arm64
os: macos-14
extras: true
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults untrusted mcpp-llvm mcpp-split mcpp-partition-definition mcpp-all-cppm verify-changes mcpp-watch multi-root failure-at-base clangd-cannot-load failure-at-base@zed generated-module generated-module-old-mcpp generated-module-negotiated typing-import typing-import-spin workaround-canaries completion-keywords diagnostic-bundle typing-autosave module-edit-autosave partial-scan-standins clangd-crash-context mcpp-emit-wait provisional-no-prime inferred-cxx26 reset-cache include-cleaner-modules no-modules tidy-const-views
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults untrusted mcpp-llvm mcpp-split mcpp-partition-definition mcpp-all-cppm verify-changes mcpp-watch multi-root failure-at-base clangd-cannot-load failure-at-base@zed generated-module generated-module-old-mcpp generated-module-negotiated typing-import typing-import-spin workaround-canaries completion-keywords diagnostic-bundle typing-autosave module-edit-autosave partial-scan-standins clangd-crash-context mcpp-emit-wait provisional-no-prime inferred-cxx26 reset-cache cache-budget include-cleaner-modules no-modules tidy-const-views
# No mcpp on the arm64 runner (its tools are the cross-built ones), so the fixtures that
# need no build tool and no compiler of their own: the semantic kit, clangd and the server
# on aarch64, with module faults, a corrupt payload and polling included.
- platform: linux-arm64
os: ubuntu-24.04-arm
cross-tools: true
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults untrusted payload-corrupt clangd-cannot-load failure-at-base watch-polling typing-import typing-import-spin workaround-canaries completion-keywords diagnostic-bundle typing-autosave module-edit-autosave partial-scan-standins clangd-crash-context inferred-cxx26 reset-cache include-cleaner-modules no-modules tidy-const-views
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults untrusted payload-corrupt clangd-cannot-load failure-at-base watch-polling typing-import typing-import-spin workaround-canaries completion-keywords diagnostic-bundle typing-autosave module-edit-autosave partial-scan-standins clangd-crash-context inferred-cxx26 reset-cache cache-budget include-cleaner-modules no-modules tidy-const-views
- platform: win32-x64
part: 1 of 2
os: windows-2022
Expand All @@ -305,7 +305,7 @@ jobs:
part: 2 of 2
os: windows-2022
extras: true
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults mcpp-emit-hang inferred-msvc untrusted mingw cmake-msvc cmake-msvc-bdb cmake-clangxx-msvc cmake-clang-cl mcpp-msvc mcpp-watch failure-at-base clangd-cannot-load completion-keywords diagnostic-bundle mcpp-emit-wait inferred-cxx26 reset-cache include-cleaner-modules no-modules tidy-const-views
fixtures: inferred inferred-bom build-discovery-off engine-none module-faults mcpp-emit-hang inferred-msvc untrusted mingw cmake-msvc cmake-msvc-bdb cmake-clangxx-msvc cmake-clang-cl mcpp-msvc mcpp-watch failure-at-base clangd-cannot-load completion-keywords diagnostic-bundle mcpp-emit-wait inferred-cxx26 reset-cache cache-budget include-cleaner-modules no-modules tidy-const-views
defaults:
run:
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
run: |
set -euo pipefail
mcpp build --release -p devtools
devtools=$(find tools/devtools/target -type f -name mcppls-devtools -path '*/bin/*' | head -1)
devtools=$(find target -type f -name mcppls-devtools -path '*/bin/*' | head -1)
"$devtools" version --check
echo "version=$("$devtools" version --print)" >> "$GITHUB_OUTPUT"
# No rust or java action here on purpose. `[xlings.workspace]` declares what packaging needs
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
run: |
set -euo pipefail
mcpp build -p devtools
devtools=$(find tools/devtools/target -type f -name mcppls-devtools -path '*/bin/*' | head -1)
devtools=$(find target -type f -name mcppls-devtools -path '*/bin/*' | head -1)
"$devtools" version --check
declared=$("$devtools" version --print)
echo "manifests say $declared, releasing ${{ steps.pick.outputs.version }}"
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ mcpp.lock

# Local review reports (issue analyses, not shipped).
.agents/docs/reviews/

# Ad-hoc reviews and the throwaway scripts they came with (issue analyses, not shipped).
.agents/reviews/
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,65 @@ release's notes are that section.
Versions are three-part semantic versions, `MAJOR.MINOR.PATCH`, and every editor plugin carries the
product version unchanged.

## [0.0.10] — 2026-10-03

A workspace's module cache grew without bound when clangd kept crashing: every crash left its
copy-on-read copies behind, the only cleanup was clangd's own three-day garbage collection on
atime, and a real machine reached **64.36 GiB in 26.5 hours** — 97.1% of it copies, and three
orphaned instance directories holding 94.5%. mcppls now owns the whole cache the way it already
owned the module locks: what a dead generation left is swept before the next one starts, the cache
lives under a budget, and what it is doing is visible (plan 2026-10-02, `C-7…C-13`, `X-6`).

### The cache is bounded

- **A new clangd generation starts in a swept cache.** Before clangd starts — at server start and
at every in-session restart — a background sweep removes the versioned copy-on-read copies the
previous generation left, keeping every published BMI, and keeps what it removes older than the
new generation's start so a concurrent start can never lose a copy it is about to write.
- **The cache lives under a budget.** `mcppls.cache.maxBytes` (4 GB a workspace) and
`mcppls.cache.totalBytes` (16 GB over all workspaces): copies and dead instance directories go
first, oldest-used first and only where no instance has the workspace open, and a cache that
cannot fit without its published BMIs is reported, never pruned of them.
- **Instances describe themselves.** Every server writes `instance.json` with a heartbeat into the
directory it works in, and a directory whose heartbeat expired is renamed aside by the lease tick
and removed in the background. A live guest is protected by its own heartbeat even when the
owner's lease is gone — the hole the old `cache --prune` fell into. Leftovers of 0.0.9, which
wrote nothing, wait 24 hours (`mcppls.cache.instanceGrace`) before they are taken.
- **Windows can finally tell a dead owner from a live one** (`process_identity`): a pid another
process has since taken no longer reads as the same server, so a crash-and-restart within the
lease expiry no longer spawns a second instance directory there. Deletions count what they could
not remove, and the count is visible — nothing fails silently.

### The cache is visible

- **One status item, two native surfaces.** The status bar's C++ Modules item carries the cache as
a segment under a character budget (`mcppls.statusBar.maxLength`, default 36): hover for a
read-only card in three zones — the project (state dot, module and unit counts, the real
preparation progress), the cache (one table: a coloured dot-matrix bar a class and a bold
total row against the budget, the bars tiny self-drawn SVGs because hover text cannot carry
colour), and the actions (sweep, open logs & reports, copy the agent prompt) above the
repository link — click for the hub, a QuickPick in four
groups (overview, clean, diagnostics, feedback), every entry with its codicon, one primary action
(**Sweep the Module Cache**, with an eye button for a dry run), a `Details` drill-down into the
largest modules and an `Open a directory…` drill-down into the three places a report names. No
webview, no side bar, no new UI surface.
- **The sweep command.** `mcppls.sweepCache` (extension: `mcppls.sweepWorkspaceCache`) removes what
no engine holds — copies, trash, dead instance directories, and stale command directories only by
explicit request where no engine is live — without stopping an engine and without a rebuild;
`mcppls cache --prune` is the same rules on the CLI, with `--dry-run`, `--older-than`,
`--max-size` and an instances-only report. The classified numbers
(`canonical` / `copies` / `instances` / `trash`) replace the old "each module may be stored twice"
guess, in the CLI, in the status, and behind the new `cxxModules/cache` request.
- **A task book for a local AI agent.** The hub copies the agent prompt — verified facts, the
read-only checks each with what healthy looks like, the output contract (a verdict, the evidence,
what could be done without deleting), and a bug branch that asks the developer first and only
then drafts the issue itself, shows the draft for approval, and never uploads logs or bundles;
`mcppls cache --prompt agent|issue` prints the same text anywhere else.
- **The editor speaks the display language.** English and 简体中文: the manifest through
`package.nls*`, the runtime words through `l10n/` bundles, with key-parity tests holding the two
together; the server's logs, CLI and prompts stay English. A `bundlesDirectory` field joins
`cxxModules/cache`'s `paths`, so a client never guesses where a diagnostic bundle lands.

## [0.0.9] — 2026-10-02

Completion on a project without modules as fast as plain clangd's, and the rest of issue #37. clangd's
Expand Down
Loading