Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ TRACEDECAY_DISABLE_GLOBAL_DB = { value = "1", force = false }
# profile; tests that need a private profile still override per-test with
# `set_var`/`Command::env`, and the HOME-fallback tests remove the variable
# in-process. Installed binaries run outside cargo and are unaffected, but the
# profile does apply to `cargo run` — use the installed binary for real
# profile does apply to `cargo run`. Use the installed binary for real
# `daemon install-service` runs.
# Enforced by crates/tracedecay-cli/tests/core_cli_suite/test_profile_isolation_test.rs.
TRACEDECAY_DATA_DIR = { value = "target/test-profile/.tracedecay", force = true, relative = true }
Expand All @@ -45,7 +45,7 @@ TRACEDECAY_DATA_DIR = { value = "target/test-profile/.tracedecay", force = true,
# x86_64-unknown-linux-gnu with its bundled rust-lld, CI symlinks ld -> mold
# (see .github/actions/setup-linux-mold; `-fuse-ld=mold` needs GCC >= 12),
# and driver-only flags such as `-B` would land on the raw linker command
# line — and fail — for anyone who sets `target.<triple>.linker` or
# line, and fail, for anyone who sets `target.<triple>.linker` or
# CARGO_TARGET_<TRIPLE>_LINKER to a linker executable directly. Developers
# who want mold locally opt in through their own linker config or RUSTFLAGS
# (a user-set RUSTFLAGS env var replaces these flags entirely).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# diagnose-summary.sh — turn TraceDecay diagnostics into a mapped-owner summary.
# diagnose-summary.sh. Turn TraceDecay diagnostics into a mapped-owner summary.
#
# Runs the graph-aware diagnostic path this skill prescribes and prints: how many
# diagnostics were recognized, which symbol/file owns each failure, the callers
Expand Down Expand Up @@ -63,7 +63,7 @@ print(f"recognized : {parsed} parsed, {returned} returned ({errs} error, {wa
print(f"mapped/unmapped: {mapped} mapped to a symbol, {unmapped} UNMAPPED")
if d.get("truncated"): print("note : output truncated (raise --max-diagnostics for more)")
if not diags:
print("\nclean — no diagnostics with a resolvable file:line span.")
print("\nclean. No diagnostics with a resolvable file:line span.")

# Group by mapped owner so shared root causes cluster.
from collections import defaultdict
Expand All @@ -88,7 +88,7 @@ if by_owner:
print(f" - {loc} {tag}")

if unmapped_hits:
print("\n## UNMAPPED (parse/file-mapping coverage gap — still real errors)")
print("\n## UNMAPPED (parse/file-mapping coverage gap, still real errors)")
for loc, tag in unmapped_hits[:10]:
print(f" - {loc} {tag}")
print(" -> If these own real code, that is a TraceDecay extractor/mapping gap worth an issue.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# project-analytics.sh — TraceDecay usage & fact-store adoption snapshot.
# project-analytics.sh. TraceDecay usage & fact-store adoption snapshot.
#
# Fills the gaps `tracedecay analytics diagnostics` leaves open: a per-tool MCP
# call breakdown, and fact-store *adoption* (how often facts are seen vs. rated).
Expand Down Expand Up @@ -49,7 +49,7 @@ GLOBAL_DB="$TD_HOME/global.db"
q() { sqlite3 -noheader -separator ' ' "$1" "$2" 2>/dev/null; }

echo "================================================================"
echo " TraceDecay usage & fact-store adoption — $PROJECT_ID"
echo " TraceDecay usage & fact-store adoption, $PROJECT_ID"
echo "================================================================"

# --- 1. MCP tool adoption (per-tool breakdown; the CLI only groups by kind). --
Expand Down Expand Up @@ -92,7 +92,7 @@ if [ "$FB" -gt 0 ]; then
printf ' %-26s %s : 1\n' "seen : feedback ratio:" "$(( SEEN / FB ))"
RATE=$("$PY" -c "print(f'{100*$FB/max($RETR,1):.2f}%')")
printf ' %-26s %s of retrievals\n' "feedback rate:" "$RATE"
echo " signal: feedback loop is ACTIVE but sparse — confirm trust scores are earned, not just seeded."
echo " signal: feedback loop is ACTIVE but sparse. Confirm trust scores are earned, not only seeded."
else
echo " seen : feedback ratio: ${SEEN} : 0"
echo " >> DEAD FEEDBACK LOOP: facts are seen ${SEEN}x but never rated helpful/unhelpful."
Expand All @@ -101,10 +101,10 @@ fi

# --- 3. Feedback ledger (transport-agnostic: CLI + MCP + automation). ---------
echo
echo "## Feedback ledger (memory_v2_feedback_history — all transports)"
echo "## Feedback ledger (memory_v2_feedback_history, all transports)"
LEDGER="$(q "$SERVING_DB" "SELECT action, datetime(occurred_at,'unixepoch'), COALESCE(source,'unknown'), substr(COALESCE(note,''),1,60)
FROM memory_v2_feedback_history ORDER BY occurred_at, event_id;")"
if [ -n "$LEDGER" ]; then printf '%s\n' "$LEDGER" | sed 's/^/ /'; else echo " (none — no fact has ever received feedback)"; fi
if [ -n "$LEDGER" ]; then printf '%s\n' "$LEDGER" | sed 's/^/ /'; else echo " (none, no fact has ever received feedback)"; fi

# --- 4. Read vs write activity (oplog is write-side; retrievals are read-side).
echo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# friction-scan.sh — mine TraceDecay usage logs for friction the self-improving
# friction-scan.sh. Mine TraceDecay usage logs for friction the self-improving
# loop should act on: tool error rates, low-adoption tools, dead feedback loops,
# and the evidence sessions behind them. Maps directly onto this skill's
# "Opportunity Ranking" table.
Expand Down Expand Up @@ -51,7 +51,7 @@ WHERE="event_kind='mcp_tool_call'"
SCOPE=$([ "$ALL" -eq 1 ] && echo "ALL PROJECTS" || echo "$PROJECT_ID")

echo "================================================================"
echo " TraceDecay friction scan — $SCOPE"
echo " TraceDecay friction scan, $SCOPE"
echo "================================================================"
[ -f "$GLOBAL_DB" ] || { echo "(global analytics db not found at $GLOBAL_DB)"; }

Expand All @@ -77,8 +77,8 @@ q "$GLOBAL_DB" "SELECT tool_name, COUNT(*) c, SUM(outcome='error') e

# --- 3. Low-adoption tools: called, but rarely (discovery/trigger gaps). ------
echo
echo "## Least-invoked tools (bottom 12 of those ever called) — candidate discovery gaps"
q "$GLOBAL_DB" "SELECT ' '||tool_name||' — '||COUNT(*)||' call(s)'
echo "## Least-invoked tools (bottom 12 of those ever called), candidate discovery gaps"
q "$GLOBAL_DB" "SELECT ' '||tool_name||', '||COUNT(*)||' call(s)'
FROM analytics_events WHERE $WHERE GROUP BY tool_name ORDER BY COUNT(*) ASC LIMIT 12;"
echo " (a tool the agents know exists but almost never call is a trigger-text or discoverability gap)"

Expand All @@ -101,8 +101,8 @@ fi

# --- 5. Evidence: sessions carrying the most tool errors. --------------------
echo
echo "## Evidence — sessions with the most tool errors (cite these)"
q "$GLOBAL_DB" "SELECT ' '||COALESCE(NULLIF(session_id,''),'(no session)')||' — '||COUNT(*)||' errors, provider='||provider
echo "## Evidence. Sessions with the most tool errors (cite these)"
q "$GLOBAL_DB" "SELECT ' '||COALESCE(NULLIF(session_id,''),'(no session)')||', '||COUNT(*)||' errors, provider='||provider
FROM analytics_events WHERE $WHERE AND outcome='error'
GROUP BY session_id, provider ORDER BY COUNT(*) DESC LIMIT 8;"
echo
6 changes: 3 additions & 3 deletions .claude/skills/using-hotpath/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ A tripped deadline, admission refusal, memory budget, or backoff ceiling is a
measurement arriving through a policy surface. Never raise, remove, or
env-override the limit as the fix. Use the lanes below to decompose where the
time or memory actually goes, then compare against what the operation should
cost for its inputs. Mis-sized work — an N+1 query storm, an unbatched writer,
a serial phase that should use every core, an inlined mega-future — is the
cost for its inputs. Mis-sized work, an N+1 query storm, an unbatched writer,
a serial phase that should use every core, an inlined mega-future, is the
defect; fix it and keep the limit. Change the budget only when the measured
cost is genuinely irreducible, in its own commit, with the measurement
attached. Overrides that keep an investigation moving are scaffolding: label
Expand Down Expand Up @@ -55,7 +55,7 @@ does not need the facility catalog.
- Record failed/cancelled work too; success-only counters hide the waste being diagnosed.
- Use RAII for active/queued/running gauges so cancellation, panic, abort, and shutdown cannot leak them.
- Do not wrap tiny getters or inner-loop nodes without measured need. Enabled probes still have event/drain overhead even when timing is sampled out.
- Keep the observability layers distinct. `tracing` events are the always-compiled operator log surface: they cost callsite checks even unsubscribed, are invisible in tests without a subscriber, and typed error mappings may collapse their messages. Hotpath macros are the compile-to-no-op measurement surface. A warn and a gauge on one path serve different consumers — neither replaces the other, and harvesting first-party logs into metrics couples placement decisions to log-field schemas. `eprintln!` is investigation scaffolding; it never merges.
- Keep the observability layers distinct. `tracing` events are the always-compiled operator log surface: they cost callsite checks even unsubscribed, are invisible in tests without a subscriber, and typed error mappings may collapse their messages. Hotpath macros are the compile-to-no-op measurement surface. A warn and a gauge on one path serve different consumers. Neither replaces the other, and harvesting first-party logs into metrics couples placement decisions to log-field schemas. `eprintln!` is investigation scaffolding; it never merges.
- Treat Hotpath 0.24 as flat aggregation: it has caller attribution for selected resources, but no parent call tree and no exclusive wall-time subtraction.
- For parallel extraction/indexing, report one outer sweep wall span plus per-worker service demand, queue depth, effective worker count, memory reservation, and limiting reason.

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/using-hotpath/references/hotpath-0.24.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ Recommended order:
- Async `#[measure]` bridges allocation attribution per poll. A synchronous `measure_block!` spanning `.await` can migrate threads; wall time remains useful but allocation attribution may be unavailable.
- Axum/HTTP client durations end at response headers. Measure streamed body/download/decode separately.
- Direct rusqlite emits no automatic SQL report. Use TraceDecay's writer/reader/transaction/checkpoint spans and truthful work gauges.
- The `sql` report and `sql_logs` tools are fed only by the crate's third-party front-ends: the `sqlx` feature's `sqlx_tracing_layer()` — a `tracing_subscriber` layer that harvests sqlx's `sqlx::query` completed-query events (sqlx-measured `elapsed`, statement text normalized into parameter-insensitive buckets, attribution to the innermost measured frame via the caller stack) — the `toasty` feature's equivalent layer, and the `diesel` feature's `instrument_diesel_sql`. The layer never times anything itself and holds every other target at `Interest::never`, but a *global* `EnvFilter` runs before per-layer filters and can suppress `sqlx::query` for the whole stack: attach `EnvFilter` per layer. Bridges fit third-party emitters that already pay tracing's cost; first-party code keeps compile-out macros.
- The `sql` report and `sql_logs` tools are fed only by the crate's third-party front-ends: the `sqlx` feature's `sqlx_tracing_layer()`, a `tracing_subscriber` layer that harvests sqlx's `sqlx::query` completed-query events (sqlx-measured `elapsed`, statement text normalized into parameter-insensitive buckets, attribution to the innermost measured frame via the caller stack), the `toasty` feature's equivalent layer, and the `diesel` feature's `instrument_diesel_sql`. The layer never times anything itself and holds every other target at `Interest::never`, but a *global* `EnvFilter` runs before per-layer filters and can suppress `sqlx::query` for the whole stack: attach `EnvFilter` per layer. Bridges fit third-party emitters that already pay tracing's cost; first-party code keeps compile-out macros.
- I/O wrapper timing starts on first poll and completes on Ready. Cancellation while Pending is not detected; do not treat it as a full future-lifecycle replacement.
- Dynamic HTTP paths, SQL identifiers/comments, debug values, and per-instance `iter = true` can leak or explode cardinality. Keep production keys static and bounded.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Hotpath Instrumentation Facilities
# Hotpath instrumentation facilities

- Synchronous function or bounded phase: `#[hotpath::measure]` or `hotpath::measure_block!("static.label", expression)`.
- Bulk instrumentation of a suspect area: `#[hotpath::measure_all]` on an inline `mod` or `impl` block applies `measure` to every function inside; exclude trivial or noisy functions with `#[hotpath::skip]`. It cannot be a file-level inner attribute, and trait-impl methods get timing/allocation but not CPU-sample attribution. Use it to blanket one investigation target, not the codebase; trim it back per the instrumentation rules before merge.
Expand All @@ -12,5 +12,5 @@
- Tokio: register the already-built runtime once with `hotpath::tokio_runtime!(runtime.handle())`.
- Counts/current state: static `hotpath::gauge!` keys; use additive lifecycle guards for shared state and clean them up in `Drop`.
- Debug values: avoid in production unless values are bounded and non-sensitive.
- Direct rusqlite: manual phase/queue/transaction instrumentation; Hotpath 0.24 has no rusqlite adapter. Its `sql` report is fed only by third-party front-ends — `sqlx_tracing_layer()` / `toasty_tracing_layer()` are `tracing_subscriber` layers that harvest those ORMs' completed-query tracing events (emitter-measured elapsed, statements normalized into parameter-insensitive buckets, attributed to the innermost measured frame), and diesel hooks its own instrumentation trait. Use a tracing bridge only for a third-party emitter that already pays tracing's cost; first-party code keeps compile-out macros. Each bridge needs its cargo feature, and a global `EnvFilter` can suppress `sqlx::query` events for the whole stack — attach filters per layer.
- Direct rusqlite: manual phase/queue/transaction instrumentation; Hotpath 0.24 has no rusqlite adapter. Its `sql` report is fed only by third-party front-ends, `sqlx_tracing_layer()` / `toasty_tracing_layer()` are `tracing_subscriber` layers that harvest those ORMs' completed-query tracing events (emitter-measured elapsed, statements normalized into parameter-insensitive buckets, attributed to the innermost measured frame), and diesel hooks its own instrumentation trait. Use a tracing bridge only for a third-party emitter that already pays tracing's cost; first-party code keeps compile-out macros. Each bridge needs its cargo feature, and a global `EnvFilter` can suppress `sqlx::query` events for the whole stack. Attach filters per layer.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# diagnose-summary.sh — turn TraceDecay diagnostics into a mapped-owner summary.
# diagnose-summary.sh. Turn TraceDecay diagnostics into a mapped-owner summary.
#
# Runs the graph-aware diagnostic path this skill prescribes and prints: how many
# diagnostics were recognized, which symbol/file owns each failure, the callers
Expand Down Expand Up @@ -63,7 +63,7 @@ print(f"recognized : {parsed} parsed, {returned} returned ({errs} error, {wa
print(f"mapped/unmapped: {mapped} mapped to a symbol, {unmapped} UNMAPPED")
if d.get("truncated"): print("note : output truncated (raise --max-diagnostics for more)")
if not diags:
print("\nclean — no diagnostics with a resolvable file:line span.")
print("\nclean. No diagnostics with a resolvable file:line span.")

# Group by mapped owner so shared root causes cluster.
from collections import defaultdict
Expand All @@ -88,7 +88,7 @@ if by_owner:
print(f" - {loc} {tag}")

if unmapped_hits:
print("\n## UNMAPPED (parse/file-mapping coverage gap — still real errors)")
print("\n## UNMAPPED (parse/file-mapping coverage gap, still real errors)")
for loc, tag in unmapped_hits[:10]:
print(f" - {loc} {tag}")
print(" -> If these own real code, that is a TraceDecay extractor/mapping gap worth an issue.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# project-analytics.sh — TraceDecay usage & fact-store adoption snapshot.
# project-analytics.sh. TraceDecay usage & fact-store adoption snapshot.
#
# Fills the gaps `tracedecay analytics diagnostics` leaves open: a per-tool MCP
# call breakdown, and fact-store *adoption* (how often facts are seen vs. rated).
Expand Down Expand Up @@ -49,7 +49,7 @@ GLOBAL_DB="$TD_HOME/global.db"
q() { sqlite3 -noheader -separator ' ' "$1" "$2" 2>/dev/null; }

echo "================================================================"
echo " TraceDecay usage & fact-store adoption — $PROJECT_ID"
echo " TraceDecay usage & fact-store adoption, $PROJECT_ID"
echo "================================================================"

# --- 1. MCP tool adoption (per-tool breakdown; the CLI only groups by kind). --
Expand Down Expand Up @@ -92,7 +92,7 @@ if [ "$FB" -gt 0 ]; then
printf ' %-26s %s : 1\n' "seen : feedback ratio:" "$(( SEEN / FB ))"
RATE=$("$PY" -c "print(f'{100*$FB/max($RETR,1):.2f}%')")
printf ' %-26s %s of retrievals\n' "feedback rate:" "$RATE"
echo " signal: feedback loop is ACTIVE but sparse — confirm trust scores are earned, not just seeded."
echo " signal: feedback loop is ACTIVE but sparse. Confirm trust scores are earned, not only seeded."
else
echo " seen : feedback ratio: ${SEEN} : 0"
echo " >> DEAD FEEDBACK LOOP: facts are seen ${SEEN}x but never rated helpful/unhelpful."
Expand All @@ -101,10 +101,10 @@ fi

# --- 3. Feedback ledger (transport-agnostic: CLI + MCP + automation). ---------
echo
echo "## Feedback ledger (memory_v2_feedback_history — all transports)"
echo "## Feedback ledger (memory_v2_feedback_history, all transports)"
LEDGER="$(q "$SERVING_DB" "SELECT action, datetime(occurred_at,'unixepoch'), COALESCE(source,'unknown'), substr(COALESCE(note,''),1,60)
FROM memory_v2_feedback_history ORDER BY occurred_at, event_id;")"
if [ -n "$LEDGER" ]; then printf '%s\n' "$LEDGER" | sed 's/^/ /'; else echo " (none — no fact has ever received feedback)"; fi
if [ -n "$LEDGER" ]; then printf '%s\n' "$LEDGER" | sed 's/^/ /'; else echo " (none, no fact has ever received feedback)"; fi

# --- 4. Read vs write activity (oplog is write-side; retrievals are read-side).
echo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ for scope in project all; do
PATH="$FAKE_BIN:$PATH" SERVING_DB="$SERVING_DB" TD_EXPECT_SCOPE="$scope" "$helper" "${args[@]}"
} 2>&1)"

assert_contains "$output" "TraceDecay usage & fact-store adoption — proj_current"
assert_contains "$output" "TraceDecay usage & fact-store adoption, proj_current"
assert_contains "$output" "serving store: graph.db"
assert_contains "$output" "total mcp_tool_call events: 3"
assert_contains "$output" "facts stored: 2"
Expand Down
Loading
Loading