Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1cf7e13
feat(dashboard): Rspack build foundation, real Tailwind v4, canonical cn
Jun 18, 2026
9680b63
feat(dashboard): migrate LCM to TSX, drop esbuild from build, add Rsb…
Jun 18, 2026
a0aa66c
feat(dashboard): accessibility + minor code-quality fixes
Jun 18, 2026
5b3cfc6
feat(dashboard): working Rsbuild dev server + refresh frontend docs
Jun 18, 2026
b1e7b2c
refactor(dashboard): shared UI primitives, adopted in graph
Jun 18, 2026
64a0025
refactor(dashboard): collapse shell light-theme overrides into semant…
Jun 18, 2026
fee43ef
fix(storage): enforce project path guards in MCP handlers
Jun 18, 2026
dfcdb2e
build(dashboard): consolidate on Rsbuild, remove esbuild, add typecheck
Jun 19, 2026
c09b3e2
fix(dashboard): undefined-ref crashes, shared-primitives adoption, a11y
Jun 19, 2026
47c3a6f
refactor(dashboard): real React imports, stale-dist build guard
Jun 19, 2026
fc166a8
chore(dashboard): drop redundant standalone typecheck script
Jun 19, 2026
a01f26a
test: sync plugin docs and dashboard smoke markers
Jun 19, 2026
b81ce22
test: isolate profile-shard fixtures across platforms
Jun 19, 2026
e0a55b7
test: canonicalize profile fixtures on macOS
Jun 19, 2026
a969a1f
test: canonicalize cleanup project fixture
Jun 19, 2026
77fadad
test: isolate hook branch fixtures in CI
Jun 19, 2026
8ddad42
test: canonicalize migration inventory fixtures
Jun 19, 2026
d49a296
test: canonicalize migration manifest fixtures
Jun 19, 2026
82fd695
test: canonicalize profile storage fixtures
Jun 19, 2026
f8d3061
test: canonicalize storage resolver fixtures
Jun 19, 2026
c5e88f4
ci: avoid restoring test target cache
Jun 19, 2026
ed60ffb
test: align doctor shard fixture paths
Jun 19, 2026
701edac
test: avoid canonical profile root in doctor fixture
Jun 19, 2026
bf3525e
test: avoid verbatim temp paths on Windows
Jun 19, 2026
13b1abc
fix: close global db before wipe cleanup
Jun 19, 2026
152012e
ci: stabilize cross-platform PR checks
Jun 19, 2026
fd69b89
test: normalize profile storage relpaths
Jun 19, 2026
4c96595
test: serialize global registry db tests
Jun 19, 2026
1aff9bb
test: isolate storage resolver home env
Jun 19, 2026
abd3997
fix(dashboard): make dev plugin imports analyzable
Jun 19, 2026
cc35ace
fix: finish dashboard branch followups
Jun 19, 2026
fdb54ca
test: accept crlf skill frontmatter
Jun 19, 2026
b3453f4
test: serialize context database fixtures
Jun 19, 2026
78f6377
docs: satisfy clippy sqlite docs
Jun 19, 2026
2ea92eb
docs: require conventional commits
Jun 19, 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
29 changes: 18 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ on:
permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"

jobs:
test:
Expand All @@ -29,9 +34,9 @@ jobs:
runner: '"windows-latest"'

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: "22"
cache: npm
Expand All @@ -47,7 +52,8 @@ jobs:

- uses: Swatinem/rust-cache@v2
with:
prefix-key: v1-rust-test
prefix-key: v2-rust-test
cache-targets: false

- name: Run tests
run: cargo test --workspace
Expand All @@ -57,9 +63,9 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository || contains(fromJSON('["master","feature/holographic-memory"]'), github.event.pull_request.base.ref) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: "22"
cache: npm
Expand All @@ -83,7 +89,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository || contains(fromJSON('["master","feature/holographic-memory"]'), github.event.pull_request.base.ref) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
Expand All @@ -103,9 +109,9 @@ jobs:
lcm/dist/index.js
lcm/dist/style.css
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: "22"
cache: npm
Expand Down Expand Up @@ -147,7 +153,7 @@ jobs:
run: cargo test --test dashboard_api_test

- name: Cache Playwright browsers
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('dashboard/package-lock.json') }}
Expand Down Expand Up @@ -204,9 +210,9 @@ jobs:
HERMES_UPSTREAM_REPO: https://github.com/NousResearch/hermes-agent.git
HERMES_UPSTREAM_REF: 9dd9ef0ec99a87f078f7272b4323df5440b4b3f9
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: "22"
cache: npm
Expand Down Expand Up @@ -241,6 +247,7 @@ jobs:
- uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: ${{ runner.temp }}/hermes-upstream/uv.lock
cache-suffix: hermes-${{ env.HERMES_UPSTREAM_REF }}

- name: Set up stock Hermes environment
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- When the user asks to remember preferences or decisions, persist concise durable facts using the project memory system when available.
- For large efforts, user wants fleets of concurrent multi-model subagents with strict per-agent file ownership so writers never collide; choose subagent models by task complexity.
- Commit only when explicitly asked, scope commits to the agent's own changes grouped by logical subsystem, and push only when told.
- Use Conventional Commit messages for all commits (for example `fix:`, `feat:`, `docs:`, `test:`, `ci:`, `chore:`); mark breaking changes with `!` or a `BREAKING CHANGE:` footer so release-plz can infer SemVer correctly.
- Reported metrics (token savings, costs) must be honest and audited — net rather than gross math, cross-checked against real transcript/usage data.
- Fix flaky tests instead of skipping them; never skip tests to get CI green.
- Keep one-off migration scripts untracked and delete them once their results are verified per project.
Expand Down
151 changes: 144 additions & 7 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::hash::{Hash, Hasher};
use std::process::{Command, Stdio};
use std::{collections::hash_map::DefaultHasher, fs, path::Path};
use std::{
collections::hash_map::DefaultHasher,
fs,
path::{Path, PathBuf},
};

const DASHBOARD_ASSET_FILES: &[&str] = &[
"dashboard/shell/dist/shell.js",
Expand All @@ -15,6 +19,28 @@ const DASHBOARD_ASSET_FILES: &[&str] = &[
"dashboard/savings/dist/style.css",
];

const DASHBOARD_SOURCE_FILES: &[&str] = &[
"dashboard/build.mjs",
"dashboard/build.shared.mjs",
"dashboard/holographic/build.from-hermes.mjs",
"dashboard/package.json",
"dashboard/package-lock.json",
"dashboard/run-unit-tests.mjs",
"dashboard/smoke.mjs",
"dashboard/vitest.config.mjs",
];

const DASHBOARD_SOURCE_DIRS: &[&str] = &[
"dashboard/dev",
"dashboard/graph/src",
"dashboard/hermes-wrapper/src",
"dashboard/holographic/src",
"dashboard/lcm/src",
"dashboard/lib",
"dashboard/savings/src",
"dashboard/shell/src",
];

/// Locates a working npm executable (`npm.cmd` is the Windows launcher).
fn npm_program() -> Option<&'static str> {
["npm", "npm.cmd"].into_iter().find(|candidate| {
Expand Down Expand Up @@ -64,18 +90,22 @@ fn run_npm(npm: &str, args: &[&str], dir: &Path) -> Result<(), String> {
}

/// Builds the dashboard frontend (`cd dashboard && npm ci/install && npm run
/// build`) when dist assets are missing, so plain `cargo build` / `cargo
/// build`) when dist assets are missing or stale, so plain `cargo build` / `cargo
/// install --path .` work from a fresh checkout. Published crates ship the
/// prebuilt dist files (see `package.include` in Cargo.toml), so this never
/// runs for crates.io builds.
fn auto_build_dashboard_assets(missing: &[&str]) {
/// runs for crates.io builds unless those packaged files are incomplete.
fn auto_build_dashboard_assets(reason: &str, affected: &[&str]) {
let fail_fast = |detail: &str| -> ! {
let affected = if affected.is_empty() {
"dashboard source files changed since the embedded dist assets were built".to_string()
} else {
affected.join("\n ")
};
panic!(
"\n\nmissing dashboard dist assets:\n {}\n\n\
"\n\ndashboard dist assets are {reason}:\n {affected}\n\n\
The dashboard UI is embedded into the binary at compile time\n\
(src/dashboard/assets.rs), so the frontend must be built first:\n\n \
cd dashboard && npm ci && npm run build\n\n{detail}\n",
missing.join("\n ")
);
};

Expand Down Expand Up @@ -108,14 +138,121 @@ fn auto_build_dashboard_assets(missing: &[&str]) {
println!("cargo::warning=dashboard assets: npm build finished; embedding fresh dist files");
}

/// Content hash of the dashboard source inputs (each input's path + bytes),
/// independent of filesystem mtimes. Returns `None` when there are no source
/// inputs - e.g. a published crate that ships only the prebuilt dist - so a
/// stamp is never recorded and a rebuild is never triggered for crates.io.
fn dashboard_source_stamp(source_inputs: &[PathBuf]) -> Option<String> {
if source_inputs.is_empty() {
return None;
}
// Hash in a stable path order so the stamp depends only on file content,
// not on the unspecified `read_dir` traversal order.
let mut paths: Vec<&PathBuf> = source_inputs.iter().collect();
paths.sort();
let mut hasher = DefaultHasher::new();
for path in paths {
// Hashing the path makes adds/removes/renames flip the stamp even when
// the surviving files are byte-identical.
path.to_string_lossy().hash(&mut hasher);
if let Ok(bytes) = fs::read(path) {
bytes.hash(&mut hasher);
}
}
Some(format!("{:016x}", hasher.finish()))
}

/// True when the dashboard source inputs differ from the content stamp recorded
/// by the previous build in this `OUT_DIR` - i.e. the sources genuinely changed
/// rather than just having their mtimes rewritten by a `git checkout`/`pull`.
///
/// A build with no recorded stamp (a fresh checkout, a clean target dir, or a
/// crates.io build that ships only dist) trusts the committed/shipped dist and
/// returns false, so it is never forced to run `npm run build`. The genuine
/// "source edited but dist not rebuilt" case is still caught on every later
/// build because the stamp from this run is persisted afterward.
fn dashboard_sources_changed(current_stamp: Option<&str>) -> bool {
let Some(current) = current_stamp else {
return false;
};
match read_dashboard_source_stamp() {
Some(previous) => previous != current,
None => false,
}
}

/// Location of the persisted source stamp inside cargo's `OUT_DIR`. Keeping it
/// in the build output (never the source tree) keeps `cargo package`
/// verification - which forbids build scripts from editing tracked files -
/// happy.
fn dashboard_source_stamp_path() -> Option<PathBuf> {
let out_dir = std::env::var_os("OUT_DIR")?;
Some(Path::new(&out_dir).join("dashboard-source-stamp"))
}

fn read_dashboard_source_stamp() -> Option<String> {
let contents = fs::read_to_string(dashboard_source_stamp_path()?).ok()?;
let stamp = contents.trim();
(!stamp.is_empty()).then(|| stamp.to_string())
}

fn store_dashboard_source_stamp(stamp: &str) {
// Best-effort: if the stamp can't be written, the next build simply falls
// back to trusting the existing dist, which is still safe.
if let Some(path) = dashboard_source_stamp_path() {
let _ = fs::write(path, stamp);
}
}

fn collect_dashboard_source_inputs() -> Vec<PathBuf> {
let mut inputs = Vec::new();
for relative in DASHBOARD_SOURCE_FILES {
println!("cargo::rerun-if-changed={relative}");
let path = PathBuf::from(relative);
if path.is_file() {
inputs.push(path);
}
}
for relative in DASHBOARD_SOURCE_DIRS {
println!("cargo::rerun-if-changed={relative}");
collect_dashboard_source_dir(Path::new(relative), &mut inputs);
}
inputs
}

fn collect_dashboard_source_dir(dir: &Path, inputs: &mut Vec<PathBuf>) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_dashboard_source_dir(&path, inputs);
} else if path.is_file() {
println!("cargo::rerun-if-changed={}", path.display());
inputs.push(path);
}
}
}

fn emit_dashboard_asset_inputs() -> String {
let source_inputs = collect_dashboard_source_inputs();
let missing: Vec<&str> = DASHBOARD_ASSET_FILES
.iter()
.copied()
.filter(|relative| !Path::new(relative).exists())
.collect();
let source_stamp = dashboard_source_stamp(&source_inputs);
if !missing.is_empty() {
auto_build_dashboard_assets(&missing);
auto_build_dashboard_assets("missing", &missing);
} else if dashboard_sources_changed(source_stamp.as_deref()) {
auto_build_dashboard_assets("stale", &[]);
}
// Record the source content hash we just accepted so the next build can
// distinguish a genuine source edit from a mtime-only churn (git
// checkout/pull). Skipped when no source inputs ship (crates.io).
if let Some(stamp) = source_stamp.as_deref() {
store_dashboard_source_stamp(stamp);
}

let mut hasher = DefaultHasher::new();
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/atomic-code-edits/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: atomic-code-edits
description: Use when editing source with safe anchored primitives: unique string replacement, atomic multi-replace, anchored insert, whole-symbol rewrite, structural ast-grep rewrite, or mechanical edits that should re-index the graph.
description: "Use when editing source with safe anchored primitives: unique string replacement, atomic multi-replace, anchored insert, whole-symbol rewrite, structural ast-grep rewrite, or mechanical edits that should re-index the graph."
---

# Atomic code edits
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/auditing-code-safety/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: auditing-code-safety
description: Use when auditing ship-blocking code risk: panic/unwrap/expect/todo/unimplemented/unsafe sites, FIXME/HACK markers, dead code, unused imports, or high-risk untested symbols.
description: "Use when auditing ship-blocking code risk: panic/unwrap/expect/todo/unimplemented/unsafe sites, FIXME/HACK markers, dead code, unused imports, or high-risk untested symbols."
---

# Auditing code safety
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/exploring-types-and-traits/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: exploring-types-and-traits
description: Use when answering type-level questions: trait implementors, impl blocks, type hierarchies, struct construction sites, field read/write sites, derive-generated methods, or method bodies across implementors.
description: "Use when answering type-level questions: trait implementors, impl blocks, type hierarchies, struct construction sites, field read/write sites, derive-generated methods, or method bodies across implementors."
---

# Exploring types & traits
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/finding-impacted-areas/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: finding-impacted-areas
description: Use when estimating blast radius: what depends on a symbol or file, affected tests, risk of a change, impacted areas for a refactor, or what could break if a target changes.
description: "Use when estimating blast radius: what depends on a symbol or file, affected tests, risk of a change, impacted areas for a refactor, or what could break if a target changes."
---

# Finding impacted areas
Expand Down
15 changes: 8 additions & 7 deletions codex-plugin/skills/project-status/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ Cheap, read-only surface for active project identity, resolved storage, the inde

1. **Active project → `tracedecay_active_project`** (no args): resolved project root, scope prefix, branch identity, and the resolved active project store backing this session. Use this before describing where data lives.
2. **Storage status → `tracedecay_storage_status`** (no args): resolved active project store health, graph DB path, writability, branch fallback warnings, and counts. Use this instead of probing `.tracedecay` or running direct SQLite checks.
3. **Index status → `tracedecay_status`** (no args): node/edge/file counts, DB size, active branch + any branch-fallback warning, tokens saved. Add `tracedecay_distribution` (`path?`, `summary?`) / `tracedecay_files` (`path?`, `pattern?`) for a kind/file breakdown.
4. **Config lookups → `tracedecay_config`** (`key` required, plus `path` for one file **or** `glob` for many): query TOML/JSON by dotted key (e.g. `key: "package.version"` on `Cargo.toml`, or `glob: "crates/*/Cargo.toml"`). Pure filesystem parse — works even before `tracedecay init`.
5. **Outstanding work → `tracedecay_todos`** (`kinds?`, `path?`, `limit?`): TODO/FIXME/XXX/HACK/WIP/NOTE/UNIMPLEMENTED markers with the enclosing symbol.
6. **Server triage → `tracedecay_runtime`** (no args): PID, resident/virtual memory, CPU%, threads, DB/WAL/SHM sizes — use when TraceDecay seems to hog CPU or RAM.
7. **User wants a visual → `tracedecay_dashboard`** (`action`: `start`|`stop`, optional `host`/`port`): starts the local dashboard server in the background and returns its URL (idempotent; `stop` shuts it down). Hand the URL to the user instead of describing charts.
8. **Memory subsystem health (optional) → `tracedecay_memory_status`** (repairs derived vectors/banks; returns fact/entity counts + trust distribution).
3. **Project registry → `tracedecay_project_list` / `tracedecay_project_search` / `tracedecay_project_context`**: list known projects, search by name/root, or load a registered project's resolved context when the user asks about another project or workspace.
4. **Index status → `tracedecay_status`** (no args): node/edge/file counts, DB size, active branch + any branch-fallback warning, tokens saved. Add `tracedecay_distribution` (`path?`, `summary?`) / `tracedecay_files` (`path?`, `pattern?`) for a kind/file breakdown.
5. **Config lookups → `tracedecay_config`** (`key` required, plus `path` for one file **or** `glob` for many): query TOML/JSON by dotted key (e.g. `key: "package.version"` on `Cargo.toml`, or `glob: "crates/*/Cargo.toml"`). Pure filesystem parse — works even before `tracedecay init`.
6. **Outstanding work → `tracedecay_todos`** (`kinds?`, `path?`, `limit?`): TODO/FIXME/XXX/HACK/WIP/NOTE/UNIMPLEMENTED markers with the enclosing symbol.
7. **Server triage → `tracedecay_runtime`** (no args): PID, resident/virtual memory, CPU%, threads, DB/WAL/SHM sizes — use when TraceDecay seems to hog CPU or RAM.
8. **User wants a visual → `tracedecay_dashboard`** (`action`: `start`|`stop`, optional `host`/`port`): starts the local dashboard server in the background and returns its URL (idempotent; `stop` shuts it down). Hand the URL to the user instead of describing charts.
9. **Memory subsystem health (optional) → `tracedecay_memory_status`** (repairs derived vectors/banks; returns fact/entity counts + trust distribution).

## Guardrails

- `tracedecay_active_project`, `tracedecay_storage_status`, `tracedecay_status`, `tracedecay_config`, `tracedecay_todos`, `tracedecay_runtime` are read-only. `tracedecay_dashboard` starts/stops a local server and `tracedecay_memory_status` repairs/normalizes memory state — use them only when the user wants the dashboard or memory counts.
- `tracedecay_active_project`, `tracedecay_storage_status`, `tracedecay_project_list`, `tracedecay_project_search`, `tracedecay_project_context`, `tracedecay_status`, `tracedecay_config`, `tracedecay_todos`, `tracedecay_runtime` are read-only. `tracedecay_dashboard` starts/stops a local server and `tracedecay_memory_status` repairs/normalizes memory state — use them only when the user wants the dashboard or memory counts.
- For deeper structural/quality questions hand off to `tracedecay:architecture-overview` or `tracedecay:code-health-report`; for memory recall, `tracedecay:recalling-project-memory`; for memory curation/update/delete, `tracedecay:curating-project-memory`; for past-session recall, `tracedecay:recalling-session-context`.

## Output
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/recalling-session-context/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: recalling-session-context
description: Use when retrieving what happened in past agent sessions: full-text transcript recall, scoped/time-filtered grep, lossless session replay, summary-DAG drill-down, or compaction recovery.
description: "Use when retrieving what happened in past agent sessions: full-text transcript recall, scoped/time-filtered grep, lossless session replay, summary-DAG drill-down, or compaction recovery."
---

# Recalling session context
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/refactoring-safely/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: refactoring-safely
description: Use when planning or executing mechanical refactors: renames, signature changes, field add/remove/rename, moving helpers, or any edit where missed call sites break the build.
description: "Use when planning or executing mechanical refactors: renames, signature changes, field add/remove/rename, moving helpers, or any edit where missed call sites break the build."
---

# Refactoring safely
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/tracing-functions/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracing-functions
description: Use when tracing call relationships: who calls a function, what it calls, shortest call paths between symbols, references for rename prep, recursion, hubs, or dynamic dispatch.
description: "Use when tracing call relationships: who calls a function, what it calls, shortest call paths between symbols, references for rename prep, recursion, hubs, or dynamic dispatch."
---

# Tracing functions
Expand Down
Loading