Releases: Datata1/mycelium
Release list
v5.0.1
Changelog
All notable changes to this project are documented here. Format follows
Keep a Changelog; the project adheres
to Semantic Versioning.
[v4.0.0] — 2026-05-18
v4.0 — Adoption fixed-point + bug triage
Tickets under tickets/v4-*.md. v4 focuses on adoption quality:
making the existing tools work reliably on real codebases. The three
themes are (1) adoption-health observability (B1–B3), (2) bug triage
from the first TS field test (T1–T7), and (3) the get_references
class-symbol fix. No new languages, no new symbol kinds — Rust
and route literals are deferred to v5. No new architectural pillars.
See LIMITATIONS.md for what stays out of scope.
Fixed
-
get_references(ClassName)now includes static and instance method
calls. The TS resolver resolvesCsEnv.from()to the method symbol
(env.CsEnv.from), not the class symbol (env.CsEnv). Previously
symbolsByTargetonly matched the target's own qualified name, so
get_references("CsEnv")missed allCsEnv.from()/CsEnv.empty()
call sites. Fix: after collecting direct matches, a second pass
expands each qualified name to its children viaLIKE q+".%", then
deduplicates. Regression test inget_refs_test.gowith fixture
testdata/fixtures/get-refs/(reproduces the codesphere monorepo-4
finding where 3/3get_referencescalls returned null on a class
used only through static-method calls). -
F1 follow-up batch (T3-T7): five smaller field-test fixes.
- T3 —
search_lexicalergonomics. Three changes: empty results
return[]LexicalHit{}notnil(JSON consumers see[]instead
ofnull, distinguishable from "tool errored"); regex compile
errors gain an actionable hint ("Go regexp syntax: |, ., [], (?i)
all supported"); the path-eliminate-all error message suggests
omitting the filter; the daemon logs a one-line stderr hint when
path_containsnarrowed the candidate set but no hits surfaced
(debuggable signal inmyco daemonoutput). - T4 — adaptive corpus for
bench-counterfactual. New
bench.BuildAdaptiveCorpus(client)probes the daemon
(list_files→ pick the heaviest indexed file bysymbol_count
→get_file_outline→ pick the first non-trivial symbol) to
construct a corpus targeting REAL symbols + files in any
indexed repo. New--adaptiveflag opts in. When--repois
passed without--adaptiveand every row fails, the bench
prints a concrete hint: "the default corpus is mycelium-tuned;
re-run with --adaptive". Closes the F1/T4 silent-DRIFT case. - T5 — counterfactual gates on success.
Summary.OutputBytesOK
new field tracks bytes from successful calls only. Aggregator
changes counterfactual basis fromOutputBytesto
OutputBytesOKso afind_symbolthat returnednull(4-14
bytes for the failed envelope) doesn't earn savings credit.
F1's session showed-0.1% savingsbecause failed myco calls
diluted the math; post-fix the model is honest — failed calls
contribute zero to the without-myco estimate, so savings goes
appropriately negative when myco failed and the agent paid the
fallback anyway. Backward-compat shim: whenOutputBytesOK == 0
ANDOK == Count, falls back toOutputBytesso v3.4-shape
fixtures continue to work. - T6 — top-level
myco findandmyco searchaliases. Users
(and agents) typedmyco find symbol WorkspacePlanand
myco search "WorkspacePlan|plans"during F1 — both errored
because the actual subcommands are buried undermyco query.
Two new top-level cobra commands delegate to the existing
runQueryFind/runQueryLexicalso reflexive command names
just work.myco findalso aliases asfind-symbol;
myco searchaliases asgrep. The originalmyco query find
/myco query grepkeep working unchanged. - T7 — strip IDE wrapper tags from session "Task" field. A
session that begins with an IDE-injected<ide_opened_file>...
or<system-reminder>...block used to surface that tag-soup
as the session's task description inmyco session export.
NowparseTranscriptReaderskips messages whose body is
exclusively a wrapper tag (<ide_opened_file>,<ide_selection>,
<system-reminder>,<command-name>,<local-command-stdout>)
and falls through to the next user message. Mixed-content
messages (wrapper + prose) keep their content. Nine-case unit
test pins the heuristic.
All five land in one bundle because each is small and they
collectively complete the F1 v4 P0+P1+P2 follow-ups. T3 ergonomics- T4 adaptive corpus + T5 counterfactual honesty are the load-
bearing fixes; T6 + T7 are usability polish.
- T3 —
-
Daemon fd-leak on large repos (F1/T2 EMFILE). Codesphere
monorepo-4(3079 files, 49 packages) hittoo many open files
on macOS during a real session because fsnotify consumes one fd
per watched directory and the macOS defaultRLIMIT_NOFILEsoft
limit is 256. Three-layer fix:-
Layer 3 (the real fix): bump
RLIMIT_NOFILEto hard at
daemon startup. Newinternal/daemon/RaiseFileDescriptorLimit()
is called inrunDaemonbefore any fd-consuming code runs.
macOS goes 256 → ~10240 (40× headroom); Linux goes 1024 →
~1048576 (1024× headroom). Verified live: daemon now logs
[daemon] RLIMIT_NOFILE raised to 1048576 (hard cap 1048576)
on startup. macOS quirk handled: when Setrlimit to hard fails
(kernelkern.maxfilesperproccap), we step down through
{10240, 4096, 2048, 1024} and accept whichever the kernel
allows. Failure is non-fatal (warning logged, daemon continues
at original limit). Windows is a no-op (per-process handle
limit is 16M+, not a real constraint). Build-tagged
rlimit_unix.go+rlimit_other.gofor portability. -
Layer 1 (diagnostic):
daemon_fd_headroomdoctor check.
Newinternal/doctor/fd_headroom_linux.goreads the daemon's
PID file (newly written byrunDaemon) and probes
/proc/<pid>/fd+/proc/<pid>/limitsto surface fd-headroom
pressure before EMFILE hits. WARN at 60% utilisation, FAIL at
90% (Thresholds.FDHeadroomWarn/FDHeadroomFail). Skips
gracefully on missing pid file (daemon not running), stale pid
(daemon died without cleanup), or unreadable/proc
(sandboxed environments). Linux-only;fd_headroom_other.go
is a no-op stub on macOS / Windows because counting another
process's fds requireslsof(sub-process, brittle) or
platform-specific syscalls — and on macOS the layer 3 setrlimit
bump preempts the EMFILE case anyway, so the check is less
load-bearing there. Three unit tests cover the no-pid-file,
stale-pid, and real-process paths. -
Layer 2 (already shipped): Watchman backend. Inventory
during T2 work showedinternal/watch/watchman/is fully
implemented (1477 LOC, opt-in viawatcher.backend: watchman
in.mycelium.ymlsince v1.7). Users with monorepos beyond
even Layer 3's headroom set the config flag and watchman takes
over. The existinginotify_headroomdoctor check already
points at this path; no changes needed.
Daemon also writes
.mycelium/daemon.pidon startup (best-effort,
cleaned up on shutdown via defer) so the doctor check has
something to probe. Not exposed as a public API — implementation
detail of T2 layer 1.Closes the F1/T2 blocker. Phase 2 field tests can now run on
monorepo-scale repos without hitting the macOS 256-fd cap; Linux
users get a doctor warning before crossing 60% utilisation. -
-
TypeScript
.d.tsindexing — the F1/T1 adoption blocker. The
default include glob wassrc/**/*.{ts,tsx}which narrowed TS
coverage to files insidesrc/. Codespheremonorepo-4's field
test surfaced this:find_symbol{name: "WorkspacePlan"}returned
null because the type is defined in
packages/payment-service/common/lib/Product.d.ts—
outsidesrc/, never walked. Default include broadened to
**/*.{ts,tsx,d.ts,mts,cts}so all common TS source locations
(package roots,lib/, ambient declarations, test config files)
are picked up; compiled outputs continue to be excluded by
**/dist/**/**/build/**. Wizard inherits viaconfig.Default()
— no separate template change needed.Also fixed the
moduleNamequalifier for.d.tsfiles: previously
Product.d.tsproduced symbols qualified asProduct.d.WorkspacePlan
(the.dsuffix wasn't stripped), now strips.d.ts/.d.mts/
.d.ctsas a unit so qualified names match the source file
(Product.d.ts→Product.WorkspacePlan). Pure rename — symbol
identity / refs continue to resolve.New fixture
testdata/fixtures/sample/src/types.d.tsmirrors the
monorepo-4 shape:interface WorkspacePlan,type WorkspacePlanMap,
type PlanSelector,enum PlanTier,declare module. New
integration testfind_symbol_in_d_ts_v4_T1_fixasserts each
shape returns fromfind_symbol. Existingstatsandlist_files
expected file count bumped from 3 → 4 to include the new fixture.
task checkgreen;myco bench-counterfactualcontinues to pass
(unrelated surface). Closes the F1/T1 blocker; Phase 2 Python
field test (planned) will replicate the fixture for.pyistub
files.
Added
- B3 — multi-repo bench-counterfactual harness. The bench-counterfactual
corpus + runner moved out ofcmd/myco/main.gointo a new
internal/bench/package:Case,Corpus,Rowtypes,
MyceliumDefaultCorpus()returning the v3.4-calibrated mycelium-self
corpus,Run(client, repoRoot, corpus, language, driftThreshold)
the orchestrator,PrintTable(rows, threshold, corpusName, language)
the renderer. The CLI command shrinks to a thin flag-parsing
or...
v5.0.0
Changelog
All notable changes to this project are documented here. Format follows
Keep a Changelog; the project adheres
to Semantic Versioning.
[v4.0.0] — 2026-05-18
v4.0 — Adoption fixed-point + bug triage
Tickets under tickets/v4-*.md. v4 focuses on adoption quality:
making the existing tools work reliably on real codebases. The three
themes are (1) adoption-health observability (B1–B3), (2) bug triage
from the first TS field test (T1–T7), and (3) the get_references
class-symbol fix. No new languages, no new symbol kinds — Rust
and route literals are deferred to v5. No new architectural pillars.
See LIMITATIONS.md for what stays out of scope.
Fixed
-
get_references(ClassName)now includes static and instance method
calls. The TS resolver resolvesCsEnv.from()to the method symbol
(env.CsEnv.from), not the class symbol (env.CsEnv). Previously
symbolsByTargetonly matched the target's own qualified name, so
get_references("CsEnv")missed allCsEnv.from()/CsEnv.empty()
call sites. Fix: after collecting direct matches, a second pass
expands each qualified name to its children viaLIKE q+".%", then
deduplicates. Regression test inget_refs_test.gowith fixture
testdata/fixtures/get-refs/(reproduces the codesphere monorepo-4
finding where 3/3get_referencescalls returned null on a class
used only through static-method calls). -
F1 follow-up batch (T3-T7): five smaller field-test fixes.
- T3 —
search_lexicalergonomics. Three changes: empty results
return[]LexicalHit{}notnil(JSON consumers see[]instead
ofnull, distinguishable from "tool errored"); regex compile
errors gain an actionable hint ("Go regexp syntax: |, ., [], (?i)
all supported"); the path-eliminate-all error message suggests
omitting the filter; the daemon logs a one-line stderr hint when
path_containsnarrowed the candidate set but no hits surfaced
(debuggable signal inmyco daemonoutput). - T4 — adaptive corpus for
bench-counterfactual. New
bench.BuildAdaptiveCorpus(client)probes the daemon
(list_files→ pick the heaviest indexed file bysymbol_count
→get_file_outline→ pick the first non-trivial symbol) to
construct a corpus targeting REAL symbols + files in any
indexed repo. New--adaptiveflag opts in. When--repois
passed without--adaptiveand every row fails, the bench
prints a concrete hint: "the default corpus is mycelium-tuned;
re-run with --adaptive". Closes the F1/T4 silent-DRIFT case. - T5 — counterfactual gates on success.
Summary.OutputBytesOK
new field tracks bytes from successful calls only. Aggregator
changes counterfactual basis fromOutputBytesto
OutputBytesOKso afind_symbolthat returnednull(4-14
bytes for the failed envelope) doesn't earn savings credit.
F1's session showed-0.1% savingsbecause failed myco calls
diluted the math; post-fix the model is honest — failed calls
contribute zero to the without-myco estimate, so savings goes
appropriately negative when myco failed and the agent paid the
fallback anyway. Backward-compat shim: whenOutputBytesOK == 0
ANDOK == Count, falls back toOutputBytesso v3.4-shape
fixtures continue to work. - T6 — top-level
myco findandmyco searchaliases. Users
(and agents) typedmyco find symbol WorkspacePlanand
myco search "WorkspacePlan|plans"during F1 — both errored
because the actual subcommands are buried undermyco query.
Two new top-level cobra commands delegate to the existing
runQueryFind/runQueryLexicalso reflexive command names
just work.myco findalso aliases asfind-symbol;
myco searchaliases asgrep. The originalmyco query find
/myco query grepkeep working unchanged. - T7 — strip IDE wrapper tags from session "Task" field. A
session that begins with an IDE-injected<ide_opened_file>...
or<system-reminder>...block used to surface that tag-soup
as the session's task description inmyco session export.
NowparseTranscriptReaderskips messages whose body is
exclusively a wrapper tag (<ide_opened_file>,<ide_selection>,
<system-reminder>,<command-name>,<local-command-stdout>)
and falls through to the next user message. Mixed-content
messages (wrapper + prose) keep their content. Nine-case unit
test pins the heuristic.
All five land in one bundle because each is small and they
collectively complete the F1 v4 P0+P1+P2 follow-ups. T3 ergonomics- T4 adaptive corpus + T5 counterfactual honesty are the load-
bearing fixes; T6 + T7 are usability polish.
- T3 —
-
Daemon fd-leak on large repos (F1/T2 EMFILE). Codesphere
monorepo-4(3079 files, 49 packages) hittoo many open files
on macOS during a real session because fsnotify consumes one fd
per watched directory and the macOS defaultRLIMIT_NOFILEsoft
limit is 256. Three-layer fix:-
Layer 3 (the real fix): bump
RLIMIT_NOFILEto hard at
daemon startup. Newinternal/daemon/RaiseFileDescriptorLimit()
is called inrunDaemonbefore any fd-consuming code runs.
macOS goes 256 → ~10240 (40× headroom); Linux goes 1024 →
~1048576 (1024× headroom). Verified live: daemon now logs
[daemon] RLIMIT_NOFILE raised to 1048576 (hard cap 1048576)
on startup. macOS quirk handled: when Setrlimit to hard fails
(kernelkern.maxfilesperproccap), we step down through
{10240, 4096, 2048, 1024} and accept whichever the kernel
allows. Failure is non-fatal (warning logged, daemon continues
at original limit). Windows is a no-op (per-process handle
limit is 16M+, not a real constraint). Build-tagged
rlimit_unix.go+rlimit_other.gofor portability. -
Layer 1 (diagnostic):
daemon_fd_headroomdoctor check.
Newinternal/doctor/fd_headroom_linux.goreads the daemon's
PID file (newly written byrunDaemon) and probes
/proc/<pid>/fd+/proc/<pid>/limitsto surface fd-headroom
pressure before EMFILE hits. WARN at 60% utilisation, FAIL at
90% (Thresholds.FDHeadroomWarn/FDHeadroomFail). Skips
gracefully on missing pid file (daemon not running), stale pid
(daemon died without cleanup), or unreadable/proc
(sandboxed environments). Linux-only;fd_headroom_other.go
is a no-op stub on macOS / Windows because counting another
process's fds requireslsof(sub-process, brittle) or
platform-specific syscalls — and on macOS the layer 3 setrlimit
bump preempts the EMFILE case anyway, so the check is less
load-bearing there. Three unit tests cover the no-pid-file,
stale-pid, and real-process paths. -
Layer 2 (already shipped): Watchman backend. Inventory
during T2 work showedinternal/watch/watchman/is fully
implemented (1477 LOC, opt-in viawatcher.backend: watchman
in.mycelium.ymlsince v1.7). Users with monorepos beyond
even Layer 3's headroom set the config flag and watchman takes
over. The existinginotify_headroomdoctor check already
points at this path; no changes needed.
Daemon also writes
.mycelium/daemon.pidon startup (best-effort,
cleaned up on shutdown via defer) so the doctor check has
something to probe. Not exposed as a public API — implementation
detail of T2 layer 1.Closes the F1/T2 blocker. Phase 2 field tests can now run on
monorepo-scale repos without hitting the macOS 256-fd cap; Linux
users get a doctor warning before crossing 60% utilisation. -
-
TypeScript
.d.tsindexing — the F1/T1 adoption blocker. The
default include glob wassrc/**/*.{ts,tsx}which narrowed TS
coverage to files insidesrc/. Codespheremonorepo-4's field
test surfaced this:find_symbol{name: "WorkspacePlan"}returned
null because the type is defined in
packages/payment-service/common/lib/Product.d.ts—
outsidesrc/, never walked. Default include broadened to
**/*.{ts,tsx,d.ts,mts,cts}so all common TS source locations
(package roots,lib/, ambient declarations, test config files)
are picked up; compiled outputs continue to be excluded by
**/dist/**/**/build/**. Wizard inherits viaconfig.Default()
— no separate template change needed.Also fixed the
moduleNamequalifier for.d.tsfiles: previously
Product.d.tsproduced symbols qualified asProduct.d.WorkspacePlan
(the.dsuffix wasn't stripped), now strips.d.ts/.d.mts/
.d.ctsas a unit so qualified names match the source file
(Product.d.ts→Product.WorkspacePlan). Pure rename — symbol
identity / refs continue to resolve.New fixture
testdata/fixtures/sample/src/types.d.tsmirrors the
monorepo-4 shape:interface WorkspacePlan,type WorkspacePlanMap,
type PlanSelector,enum PlanTier,declare module. New
integration testfind_symbol_in_d_ts_v4_T1_fixasserts each
shape returns fromfind_symbol. Existingstatsandlist_files
expected file count bumped from 3 → 4 to include the new fixture.
task checkgreen;myco bench-counterfactualcontinues to pass
(unrelated surface). Closes the F1/T1 blocker; Phase 2 Python
field test (planned) will replicate the fixture for.pyistub
files.
Added
- B3 — multi-repo bench-counterfactual harness. The bench-counterfactual
corpus + runner moved out ofcmd/myco/main.gointo a new
internal/bench/package:Case,Corpus,Rowtypes,
MyceliumDefaultCorpus()returning the v3.4-calibrated mycelium-self
corpus,Run(client, repoRoot, corpus, language, driftThreshold)
the orchestrator,PrintTable(rows, threshold, corpusName, language)
the renderer. The CLI command shrinks to a thin flag-parsing
or...
v3.0.0-rc6
Changelog
All notable changes to this project are documented here. Format follows
Keep a Changelog; the project adheres
to Semantic Versioning.
[Unreleased]
Added
- v3.0-rc polish + docs. Canonicalises the
docs/layout (the
old rootRESEARCH.mdmoves todocs/research.mdand gains a
design-decision crosswalk plus a "read but not acted on" section),
rewrites the README around the v3 agent-native story (skills tree
and focused reads as the headline; structural MCP tools demoted
to "for programmatic use") while leaving the project header /
badges untouched, shipsdocs/adoption.mdas a guide to verifying
agent uptake via the v2.2 telemetry log, and adds a navigation
integration test (navigation_integration_test.go) that
mechanisesdocs/navigation-example.mdso the
INDEX.md → SKILL.md → read_focusedpath is enforced in CI.
Release tarballs now bundle the matching sqlite-vec shared library
next to the binary, andindex.OpenWithExtensionauto-discovers
it whenindex.vector.extension_pathis left empty in
.mycelium.yml— semantic search at scale is now zero-config on
release builds. - Incremental skills regeneration (Pillar H, v2.5 in the v3 plan).
The v2.3 skills tree gets a hash gate: every rendered file (per-
package SKILL.md, per-aspect INDEX.md, root INDEX.md) is hashed
before write; ifskill_files.skill_hashmatches, the WriteFile and
store update are both skipped. New migration0006_skills.sql
introduces theskill_filestable; newinternal/indexhelpers
(SkillFileHash,UpsertSkillFile,DeleteSkillFile,
PruneSkillFiles,ListSkillFiles) satisfy a smallskills.Store
interface so the renderer stays storage-agnostic.Compilegrows
Options.Store,Options.Stats,Options.DryRun; passing a Store
enables hash-gated writes, and Stats reportsRendered / Written / Skipped / Pruned. The wall-clockgenerated:frontmatter line is
stripped from the hash input so two renders of the same structural
content produce the same hash regardless of when they ran — without
this the gate would fire on every daemon batch and defeat the
whole milestone. - Daemon-driven incremental regen. New
Daemon.SkillsRegen func(ctx, packages []string) errorfield plus a debounced batcher in
the watcher event loop: everypath.Dir(relPath)from
Pipeline.HandleChangeis collected into a dedup set, and after
SkillsDebounce(default 200ms) of channel idle the batch is
flushed to a worker goroutine that calls SkillsRegen exactly once.
A second worker serialises regen calls so two bursts can't race on
the same.mycelium/skills/tree.cmd/myco daemonwires
SkillsRegen toskills.RegenerateAffectedonly when
.mycelium/skills/already exists, so users who never opted into
the skills feature aren't surprised by a regenerated tree.
RegenerateAffectedfor v2.5 is a thin wrapper overCompilewith
the Store set: the per-render cost is ~100ms on the self-index and
the hash gate makes the actual write cost zero on a clean tree, so
fully exploiting per-package short-circuiting was deferred — the
packages slice is captured for telemetry and reserved for a future
optimisation hook. skills_coveragedoctor metric. NewStats.SkillsPackagesIndexed
(distinct directories holding indexed files) plus a filesystem walk
ininternal/doctorthat counts presentSKILL.mdfiles under
.mycelium/skills/packages/. Coverage = on-disk / indexed; pass at
≥ 0.95, warn below, fail below 0.5. Skipped when the skills dir
doesn't exist (opt-in feature, not a regression). Walking the
filesystem rather than readingskill_filescatches the case where
the DB row outlives the file on disk.myco skills compile --statusand--incrementalflags.
--statusruns the renderer in DryRun mode against the live
skill_fileshashes and reportsrendered / unchanged / would changewithout touching disk or the DB.--incrementalis the
hash-gated equivalent ofcompile: it writes only the files whose
rendered bytes differ and prints the same per-call counters the
daemon logs.
Measured
-
v2.5 hash gate on the self-index (Tiger Lake, 105 files / 30
packages / 35 rendered files). Cold compile: 35 rendered, 35
written, ~100ms. Warm compile (no source changes): 35 rendered, 0
written, 35 skipped, ~70ms. Single-symbol change (added one
top-levelfunc): 35 rendered, 6 written, 29 skipped — the changed
package + root INDEX.md + four aspect indices. Pure-formatting
source change (added a blank line to a comment): 35 rendered, 0
written, 35 skipped — the index hash didn't move, so neither did
the SKILL.md hash. -
Focused reads (Pillar I, v2.4 in the v3 plan). New
internal/focuspackage implements the deterministic lexical filter
promised by the v3 roadmap: tokenize a focus string (lowercase,
stopword-strip), then score candidates against name (3.0 exact / 2.0
substring), qualified name (2.0 substring), docstring (1.0
substring), and ref targets (0.5 substring). Pure Go, no neural
model — we adopt the SWE-Pruner pattern but explicitly not the
mechanism, so the single-static-binary distribution story holds.
Wired into three existing reader methods as an optionalfocus
param:FindSymboldrops non-matchers and re-ranks survivors by
score;GetFileOutlinekeeps top-level items whose subtree
contains any match;GetNeighborhoodprunes nodes outside the
focus and surfaces afocus filter pruned N node(s)note. Empty
focus is byte-identical to prior behaviour — verified by the
pre-existing integration suite. -
read_focusedMCP tool /myco readCLI. New top-level read
primitive that returns one indexed file with non-focus-matching
symbols collapsed to one-line markers in the file's native
comment style (// signature ... // collapsed (lines N-M)for
Go/TS/JS,# ...for Python). Empty focus returns the file in
full, so the tool also functions as a daemon-mediatedcatwhen
the agent isn't sure how big the file is. Multi-line signatures
(Go interface bodies, struct definitions) are flattened to their
first line with…appended so the marker stays single-line.
Response carries aStats { TotalSymbols, ExpandedSymbols, OriginalBytes, ReturnedBytes }block plus anExpandedlist of
surviving symbols with their original[StartLine, EndLine]ranges
so agents can map back to source. Wire-up: newFocusfield on
FindSymbolParams/GetFileOutlineParams/GetNeighborhoodParams,
newReadFocusedParams+MethodReadFocused, daemon dispatch,
MCP tool schema entry, HTTP route auto-derived from the
dispatcher,--focusflag onmyco query find|outline|neighbors,
andmyco read <path> --focus "<q>"(with--statsfor the
collapse counters on stderr).
Measured
-
read_focusedbyte reduction (self-index, Tiger Lake). Three
representative queries on this repo:file focus returned/original reduction cmd/myco/main.go (44 KB) "telemetry recorder" 8443 / 44337 81% cmd/myco/main.go (44 KB) "skills compile" 8909 / 44337 80% internal/daemon/daemon.go (9 KB) "dispatch read_focused" 6540 / 9163 29% Results vary with focus specificity and file shape — large files with many independent symbols collapse aggressively, small dense files less so. We're explicitly not claiming SWE-Pruner's 23–54% range against a trained reranker; the lexical filter trades precision for distribution simplicity. -
Static skills tree (Pillar L, v2.3 in the v3 plan). New
internal/skillspackage +myco skills compileCLI generate a
deterministic Markdown tree under.mycelium/skills/that an agent
can navigate with only theReadtool. Layout: per-package
SKILL.md(one per directory of source, language unified for
mixed-language directories), rootINDEX.mdlisting every package,
and anaspects/subtree with four cross-cutting filters
(error-handling, context-propagation — clean signature matches;
config-loading, logging — heuristic ref-driven, frontmatter-flagged).
Output islanguage: complementaryto MCP — SKILL.md is lean
(≤~160 lines on the largest mycelium package), points the reader at
myco query refs/neighborsfor specifics. New reader helpers
(*query.Reader).PackageRefAggregates,
SymbolsBySignatureLike,SymbolsByOutboundRefkeep the "query is
the only reader" rule intact.--packageand--aspectflags
scope regen for fast iteration; both correctly skip everything
outside their scope. Self-dogfood on the mycelium repo: 28 packages
/ 88 files / 589 symbols, full tree compiles in ~52ms; tree
gitignored as a sibling ofindex.db. Incremental hash-gated
regeneration is v2.5. -
Opt-in telemetry log (Pillar K, v2.2 in the v3 plan). New
internal/telemetrypackage with aRecorderinterface and a
JSONLFileRecorder. Off by default; enabled via
telemetry: { enabled: true }in.mycelium.yml. When on, the
daemon dispatcher ininternal/daemon/daemon.gorecords one line
per IPC/MCP call to.mycelium/telemetry.jsonl(timestamp, tool
name, input bytes, output bytes, wall-clock ms, ok). No network,
no aggregation off-host — purely a local file the user can
tail -f. Open failure falls back toDisabledso observability
never gates daemon startup. -
myco stats --telemetryaggregator: streams the JSONL log and
prints per-tool counts, byte totals, and p50/p95 durations, plus
anallrollup. Friendly hints when telemetry is off in config or
when no records exist yet, so users who flipped the flag but
haven't generated traffic understand what they're seeing.
Fixed
- sqlite-vec extension entrypoint.
LoadExtensionwas being called
with an empty...
v2.0.0-rc1
Changelog
All notable changes to this project are documented here. Format follows
Keep a Changelog; the project adheres
to Semantic Versioning.
[Unreleased]
[v2.0.0-rc1] — 2026-04-24
First release candidate for v2.0 ("precision and scale"). No new
functional changes since v1.7; this tag consolidates the v1.1 → v1.7
series into a single release and gates the remaining v2.0 work.
Per-milestone details remain in the sections below.
Delivered against the v2.0 acceptance criteria
- Type-aware references for Go, TypeScript, Python. Self-index
reportsself_loop_count = 0,unresolved_ref_ratio = 0.0%.
(v1.2, v1.3) - Workspace mode: one daemon, one SQLite, N sub-projects with
per-project config and optionalprojectfilter on every query
tool. (v1.5) - Graph-native tools:
impact_analysis,critical_path. (v1.6) - PR-scoped queries:
--since <ref>on five read methods. (v1.6) - Doctor + quality signals:
myco doctorexits 0/1/2 on
pass/warn/fail with configurable thresholds. (v1.1, v1.2, v1.7) - Watchman opt-in behind
watcher.backend. (v1.7) - sqlite-vec integration compiled in; brute-force fallback
measured. (v1.4)
Architectural invariants from v1.0 are preserved: SQLite is still
source of truth and query engine; internal/query is the sole
reader; internal/pipeline is the sole writer; no new top-level
processes; all schema changes are additive.
Known gaps before the final v2.0 tag
- sqlite-vec p95 unmeasured. The vec0 code path compiles on
every release target but the "p95 < 50ms at 100k chunks"
benchmark from the roadmap has not been run on a machine with
the extension installed. Brute-force numbers on Tiger Lake
(768 dims): 114ms / 555ms / 1100ms at 10k / 50k / 100k. libsqlite_vec.{so,dylib,dll}not bundled in the release
tarball. Users installsqlite-vecmanually per the README.- No 100k+ file monorepo validation.
myco doctor,
workspace mode, and the inotify-headroom check have only been
exercised against the self-index and the committed fixtures.
[v1.7.0] — 2026-04-24
"Watchman opt-in" — the seventh v2.0 milestone (Pillar G). Pluggable
watcher backend so users on 100k+ file repos can escape the
fs.inotify.max_user_watches ceiling without changing anything
else about how mycelium runs.
Added
internal/watch/watchman/— minimal in-tree watchman client.
Talks JSON-over-unix-socket:get-sockname,watch-project,
subscribe,unsubscribe. Read pump demultiplexes command
responses vs subscription deliveries so one connection handles
both.$MYCELIUM_WATCHMAN_SOCKoverrides sockname discovery for
container setups.- Watcher backend selection. New
watcher.backendconfig field
("fsnotify"default,"watchman"opt-in) plus
myco daemon --watcher-backend <name>CLI override. Unknown
values are a hard error; watchman unavailability falls back to
fsnotify with a stderr warning so the daemon still starts. internal/watchrestructure. Old monolithicwatch.gosplit
intowatcher.go(publicWatcherinterface +Options),
common.go(shared debounce/coalesce/filter wrapper), and
per-backend sources:fsnotify.go,watchman.go. Both backends
route through the same wrapper so behavior is identical — the
two honest-surface bugs the old struct had (unused
MaxFileSizeKB, unusedCoalesceMS) are fixed once, not twice.CoalesceMSis now wired. Bursts of debounced events within
a coalesce window flush as one batch to the output channel.- Doctor:
inotify_headroomcheck. Linux-only. Counts repo
directories vs/proc/sys/fs/inotify/max_user_watchesand warns
above 50%, fails above 90%. The warn message suggests either
switching towatcher.backend: watchmanor raising the sysctl.
Changed
watch.Newsignature went from positional args to anOptions
struct (source-incompatible; migrates cleanly — all call-sites
updated).daemon.Daemon.Watcheris nowwatch.Watcher(interface) rather
than*watch.Watcher(struct pointer), matching the new backend
split.
Fixed
- Shutdown race in the watcher's shared wrapper: coalesce/debounce
timers could firew.sendafter the output channel closed. Pump
now owns every write toout; timers signal through internal
channels.go test -race ./internal/watch/...confirms.
[v1.6.0] — 2026-04-24
"Graph-native tools + PR scope" — the sixth v2.0 milestone (Pillars E
- F). Two new graph traversals that become cheap once v1.2/v1.3's
type-aware resolvers landed, plus a--since <ref>path filter on the
existing read surface for PR-scoped queries.
Added
-
impact_analysis(symbol)— new MCP tool and CLImyco query impact. Returns the transitive inbound closure around a symbol as
a flat list ranked by distance (1 = direct caller). Optionalkind
filter narrows the reported set (typical use:kind=methodto find
test methods covering the target). Default depth 5, hard ceiling
10. Composes withprojectandsince— they scope the reported
callers, not the walk, so cross-file / cross-project chains still
surface. -
critical_path(from, to)— new MCP tool and CLImyco query path. Returns up tokshortest outbound call paths. Bounded BFS
at depth ≤ 8 via a single recursive CTE; cycles prevented by the
SQLiteinstr()idiom on a comma-delimited accumulated path
column. Hydrates the distinct vertices in one second-pass query to
avoid the N+1 fan-out. Default k = 5. -
--since <ref>filter onfind_symbol,get_references,
list_files,search_lexical,search_semantic. Resolved via
git -C <root> diff --name-only <ref>...HEADat the transport
boundary (daemon RPC handler and CLI offline fallback), then passed
to the reader aspathsIn []string. Three-dot form uses the merge-
base so "files on my branch" stays correct after the base advances. -
internal/gitref/— thin helper (ResolveSince) that runs the
git diffwith a 5s timeout and surfaces stderr verbatim on
failure. Returns a non-nil empty slice when the ref has no diff
against HEAD so the reader's zero-row sentinel distinguishes "no
changes" from "no filter." -
internal/query/graph.go—ImpactAnalysis,CriticalPath,
ImpactHit,Impact,PathVertex,CriticalPathResult. Reuses
resolveSeedandloadNodefromneighborhood.go. -
internal/query/paths.go— sharedpathsInClausesplicer
renders theAND f.path IN (?, ?, ...)WHERE fragment used across
the five filtered methods. Caps the path list at 500 entries
(SQLite's 999-parameter limit) and returns a clear error when a PR
diff expands beyond that — the correct fix is a tighter base ref. -
Reader signature change (additive, source-incompatible) — five
methods gained a finalpathsIn []stringargument:FindSymbol(ctx, name, kind, project, limit, pathsIn)GetReferences(ctx, target, project, limit, pathsIn)ListFiles(ctx, language, nameContains, project, limit, pathsIn)SearchLexical(ctx, pattern, pathContains, project, k, repoRoot, pathsIn)Searcher.SearchSemantic(ctx, query, k, kind, pathContains, project, pathsIn)
pathsIn = nilis "unscoped";pathsIn = []string{}is an
explicit zero-row sentinel. Existing callers passnilto preserve
prior behavior. An options-struct refactor was considered and
rejected for mid-release API churn. -
MCP tool schemas — two new tool entries (
impact_analysis,
critical_path), plus asinceinput onfind_symbol,
get_references,list_files,search_lexical,
search_semantic. MCP server dispatch ininternal/mcp/server.go
routes the two new tools. -
CLI subcommands —
myco query impact <symbol>andmyco query path <from> <to>.--since <ref>added tofind,refs,files,
grep,search. Offline fallback path runsgitref.ResolveSince
locally so--sinceworks even without the daemon. -
Integration tests at
graph_integration_test.go:TestIntegration_ImpactAnalysis— seeds onauth.normalizeEmail
and assertsauth.AuthService.fingerprintat distance 1 and
auth.AuthService.issueTokenat distance 2. Subtests for the
kind-filter narrowing and the depth-clamp note.TestIntegration_CriticalPath— asserts the pathissueToken → fingerprint → normalizeEmailsurfaces.TestIntegration_PathsInFilter— exercises the reader-level
filter (no git process) across three cases: matching file,
non-matching file, empty-slice sentinel.
-
internal/gitref/resolve_test.go— temp-git-repo tests covering
the happy path (two-commit diff), empty ref (error), unknown ref
(error), and the no-changes case (non-nil empty slice).
Notes
vec0KNN fast path is skipped whensearch_semanticis called
with aprojectfilter (v1.5) or asincefilter (v1.6) —
vec0 MATCHdoesn't compose with arbitraryWHEREclauses.
Brute-force cosine handles scoped semantic search.impact_analysisis intentionally not a superset of
get_neighborhood(direction=in). The shapes serve different
workflows: graph (nodes + edges) vs. flat distance-ranked list; 2
vs. 5 default depth; 5 vs. 10 max; no kind filter vs. yes.- Cross-repo federation (N worktrees, one graph) remains a v3
non-goal.
Verification
Integration suite green on TestIntegration_IndexAndQuery,
TestIntegration_WorkspaceMode, TestIntegration_ImpactAnalysis,
TestIntegration_CriticalPath, TestIntegration_PathsInFilter, plus
all four internal/gitref cases. go vet -tags sqlite_fts5 ./...
clean. No schema changes, no migration.
[v1.5.0] — 2026-04-23
"Workspace mode" — the fifth v2.0 milestone (Pillar C). One daemon, one
SQLite, N sub-projects under one worktree. Not cross-repo federation
(that's v3): the unit of isola...