Files
open-tasks/docs/architecture/decisions/006-bunglob-over-parsedirectory.md
glm-5.1 34d1802d30 Add TaskSource abstraction, config schema, and Bun.Glob file scanning
Architecture updates to support the plugin's I/O and configuration layer:

- TaskSource interface abstracts task loading from I/O, making future
  sources (API, database, test) swappable without operation changes
- FileSource implements v1: Bun.Glob for directory scanning, Bun.file
  for reading, parseFrontmatter for parsing (single-pass I/O)
- SourceResult provides raw file content (for show) and per-file error
  detail (for validate) that parseTaskDirectory couldn't offer
- Config schema uses TypeBox (already a dep via taskgraph) for
  compile-time types, runtime validation, and JSON Schema export
- ADR-005: TaskSource abstraction rationale
- ADR-006: Bun.Glob over parseTaskDirectory rationale
- Performance benchmark added (43 tasks full pipeline: ~150ms)
- AGENTS.md updated with config section and source structure
2026-04-28 10:06:18 +00:00

2.8 KiB

status, last_updated
status last_updated
draft 2026-04-28

ADR-006: Bun.Glob Over parseTaskDirectory

Context

@alkdev/taskgraph provides parseTaskDirectory(dirPath) — a convenience function that recursively scans a directory for .md files and returns TaskInput[]. It uses node:fs/promises.readdir for directory traversal and silently skips files with invalid frontmatter.

The plugin needs more than what parseTaskDirectory provides:

  1. Raw file content — the show operation returns full markdown body (frontmatter + description + acceptance criteria + notes). parseTaskDirectory only returns parsed frontmatter.
  2. Error detail by filename — the validate operation reports which file failed and why. parseTaskDirectory silently skips invalid files with no error reporting.
  3. Bun-native runtime — the plugin targets Bun. Bun.Glob and Bun.file() are native APIs with no Node compat overhead.
  4. Single-pass I/O — read each file once. parseTaskDirectory + separate file reads for show would be two passes.

Decision

Use Bun.Glob("**/*.md") for directory scanning, Bun.file().text() for reading, and parseFrontmatter() (singular, from @alkdev/taskgraph) for parsing. The FileSource class orchestrates this into a SourceResult.

We still use parseFrontmatter() for the YAML/schema validation — we just don't use parseTaskDirectory or parseTaskFile (which does the same thing but with node:fs/promises.readFile).

Consequences

Positive:

  • Single I/O pass per operation call — glob scan, read all files, parse in memory
  • rawFiles Map gives full content for show without a second read
  • errors array gives per-file error detail for validate
  • Bun-native APIs (Bun.Glob, Bun.file()) — no Node compat layer
  • Consistent with the TaskSource abstraction (see ADR-005)

Negative:

  • Not using parseTaskDirectory means reimplementing directory scanning — but Bun.Glob is ~2 lines and more flexible
  • Not using parseTaskFile means we call parseFrontmatter() directly after reading the file ourselves — same outcome, slightly more code
  • The rawFiles Map keeps all file content in memory — acceptable for typical task sets (≤50 files, ≤100KB total)

Benchmark

43 task files, all analysis functions, Bun runtime:

  • Bun.Glob scan: ~1ms
  • File read + parseFrontmatter (43 files): ~140ms
  • TaskGraph.fromTasks: ~5ms
  • All 6 analysis functions: ~17ms
  • Total: ~150ms

The Rust CLI is faster on raw I/O/parsing (native binary), but the plugin eliminates subprocess overhead and plain-text parsing by the LLM. Overall tool call latency favors the plugin.

References

  • @alkdev/taskgraph frontmatter/file-io.tsparseTaskFile and parseTaskDirectory implementations
  • Bun API docs: Bun.Glob, Bun.file()