Skip to content

Releases: Daigtas/glyph

v2.0.1 — Silent-Failure Fixes

Choose a tag to compare

@Daigtas Daigtas released this 03 Sep 05:38

Two silent failures found while dogfooding v2.0.0 on real projects. Both reported success while quietly doing nothing — the worst kind of bug in an indexer, because the output looks complete.

Unreadable paths were skipped without a word

walk_source_files() and the parse worker both swallowed OSError, so any file or directory the user lacked read permission on simply never entered the index. No warning, no count — and the scan line still printed a healthy-looking summary.

On a real project this hid five root-level config files, including a Next.js middleware.ts, from an index that otherwise looked complete and passed glyph doctor. find had counted those files (listing needs only directory permission), so even a manual cross-check said the index was whole.

Scans now report every path they could not read:

[glyph] myproject: 284 parsed, 0 skipped — 6,462 symbols, 8,750 edges (3,954 linked)
  ⚠ 2 path(s) skipped — the index is incomplete:
      middleware.ts — Permission denied
      lib/admin — Permission denied

glyph refresh shows a per-project count, and the scan result dict gains an unreadable list.

glyph fallow <project> dupes ingested nothing against Fallow 3.x

The ingester looked for a top-level duplications key containing files[].path. Fallow 3.x emits clone_groups containing instances[].file. Every run printed dupes: 0 issues (fallow v3.22.0) and exited 0 — indistinguishable from a genuinely clean codebase.

Both shapes are accepted now. The same project went from 0 to 4,261 duplicate blocks, matching Fallow's own clone_instances stat exactly.

Upgrading

git pull && ./install.sh
glyph refresh          # re-scan; watch for any ⚠ lines
glyph fallow myproject dupes

No schema change and no re-index required — but if you have ever scanned a tree with mixed permissions, re-scan and check for warnings. Your index may have been missing files this whole time.

Full changelog: CHANGES.md · v2.0.0...v2.0.1

v2.0.0 — Correctness Rewrite

Choose a tag to compare

@Daigtas Daigtas released this 03 Sep 05:20

If you used v1.x, re-index. v1's graph was largely non-functional — three defects made most of what Glyph advertised return nothing. Each was verified against a live 57,121-symbol index before and after the fix.

The three that mattered

1. Symbol names were corrupt — 46% of them.
Extraction sliced a decoded string using tree-sitter's byte offsets. One non-ASCII character (an em-dash, a box-drawing char, an emoji) shifted every later name in that file. Measured: 26,240 of 57,121 symbols came out as fragments like '\n\n const totalPushed = ...'. Extraction now slices the source bytes and decodes per node.

2. Graph traversal returned nothing, ever.
Edge resolution hardcoded source_id = None, so 0 of 18,927 edges had a source. glyph deps, glyph path and glyph bridges were permanently empty on every project. Edges now carry a real source symbol, tracked through the AST walk, plus a src_file_id so module-level imports stay attributable.

3. The index rotted as you used it.
An incremental scan deleted the inbound edges of every re-parsed file and never rebuilt them — because those edges live in other files that weren't re-parsed. Measured: editing 10 files dropped one repo from 3,265 to 2,109 edges (−35%). Edges now store target_name beside target_id, and a repair pass re-points them after a re-parse. Verified lossless across repeated edit cycles.

Also fixed

  • orphans always returned zero rows — NOT IN against a subquery containing NULL yields NULL, never true.
  • Imports were essentially never extracted: the walker looked up a field named clause, which doesn't exist in the TS grammar (it's import_clause). 24 import edges across 4,710 files.
  • .d.ts and .min.js were never ignored — Path("x.d.ts").suffix is .ts.
  • watch called scan_project(name, "") with an empty root and was a silent no-op.
  • Destructuring bindings (const { a, b } = x) were stored as one unsearchable blob; each name is now bound individually.
  • A cursor was reused inside its own iteration, silently truncating after the first project.

Resolution now enforces export visibility — a non-exported symbol is only reachable inside its own file. That alone removed 678 false edges on one repo, where member calls like parsed.error.flatten() were linking to an unrelated module-local flatten().

Performance

Same machine, two production Next.js codebases:

v1.2.0 v2.0.0
Full scan — 3,161 files 7.79 s 2.57 s (3.0×)
Full scan — 1,576 files 3.31 s 0.99 s (3.3×)
No-op incremental 0.29 s 0.17 s
Edges extracted (1,576 files) 3,265 31,491
Edges resolved to a symbol 0 14,048
Corrupt symbol names 46% 0%

From stat()-based change detection (unchanged files are never read or hashed), parallel parsing across processes, cached parsers, iterative AST walking, up-front id allocation, and GROUP BY joins replacing correlated subqueries.

Accuracy spot-checked against grep: 260 caller files for withAuth vs grep's 263; 405 vs 401 for the db singleton.

New

  • glyph context <project> <symbol> — definition, line range, absolute path, every caller, every callee, and the file's other symbols in one JSON call. Built for AI agents.
  • glyph doctor — index self-check: malformed names, dangling edge targets, resolution rate.
  • glyph refresh — incrementally re-scan every indexed project (0.28 s for 5,015 files across 3 projects; cheap enough to cron).
  • glyph hotspots — churn × structural centrality, from git history.
  • --json on every query command, with a consistent contract on not-found paths.
  • argparse CLI with real flags, ON DELETE CASCADE throughout, schema versioning.

Upgrading

The v1 database is migrated automatically on first run. Your project names and paths are kept; derived data is rebuilt on the next scan. The old DB is backed up to ~/.glyph/glyph.db.v1-backup-<timestamp> first.

v1 data isn't salvageable — 46% corrupt names and zero edge sources — so a re-scan is required, not optional:

git pull && ./install.sh
glyph scan myproject /path/to/repo --full
glyph doctor

Known limits

Roughly 45–50% of edges resolve to a project symbol; the rest are calls into node_modules/stdlib, which Glyph deliberately does not index. An unresolved edge means "points outside the project", not "not called". Dynamic dispatch (obj[key](), DI containers, string-keyed handlers) is invisible to AST analysis. Use Glyph to find and orient fast, then read the code before you change it.

Full changelog: CHANGES.md · v1.2.0...v2.0.0