ubgo is in early alpha: libraries and APIs may still change.

Skip to content
ubgo Alpha

docsync

Finds the sentences in your docs that stopped being true when the code they describe changed.

go get github.com/ubgo/docsync
Language
Go
Go version
1.26
Core deps
0 direct
Latest tag
v0.1.6
License
Apache-2.0
Go modules
6

How it fits together

Highlights from the README

  • Cites by id, not by path and line. A block is marked once in the file that owns it; the id travels with it through moves, renames and reformatting, so a doc keeps pointing at the right code.
  • Nothing is copied. Values, code snippets and permalinks are rendered from live source at build time, so a doc cannot hold a stale copy.
  • Every finding says what to do. Each one carries the diff since the sentence was last approved and the exact command to approve it or the place to edit it.
  • An approval is for one sentence, and it is recorded. An ack names the sentence, the block’s hash, and who approved it; rewriting the sentence needs a new one. Over MCP an agent may ack only by naming the human who delegated it.
  • Built for agents. ds mcp gives agents map, find, read, context, impact, check, def and ack, with every payload fenced so repository text is never read as an instruction; ds init --agents wires it up.
  • Boring core. The library is Go’s standard library only, never writes a file or opens the network itself, holds 100% statement coverage, and is specified by a conformance suite rather than by its own behaviour.

Code from the README

sys, err := docsync.New(
    docsync.WithFS(os.DirFS(repo)),
    docsync.WithConfig(cfg),               // parsed by you; the library never reads files it was not given
    docsync.WithPrevious(ledger, refs),    // the committed state to diff against; zero for a first run
    docsync.WithAcks(acks),
    docsync.WithOldContent(gitShow),       // func(ledger.Row) (string, bool): the body a previous row had, for diffs and change classes
)
report, err := sys.Check(ctx, docsync.CheckOptions{})
for _, f := range report.Findings {
    fmt.Println(f.State, f.Doc, f.Line, f.Remedy)
}
res, err := sys.Define(ctx, "internal/store/write.go#Store.Persist", docsync.DefineOptions{Owner: "@auth"})
// res.Edit is a proposed change to the source file; you decide whether to apply it

From the docsync README.

At a glance (from the README)

PackageHolds
directivethe one directive grammar: ds:<verb> key=value…, comment and link carriers, continuation folding, parse and format round-trip
idids as <label>-<8-char suffix>; the suffix is the identity, the label is a hint
blockblocks, references, kinds, carriers, stability policies, change classes
extractthe built-in tiers: markdown and HTML, AsciiDoc and reStructuredText (the document tier), config (yaml, toml, ini, env, properties), heuristic code, plain text; Locate for binding a symbol or line without a directive; exported hooks (ScanCode, Prelude, NewDef) for grammar tiers outside the root
pickpick= schemes for narrowing a block to one value or range
sentencebinding a link to the sentence, list item, or table cell that contains it
scanwalking an fs.FS under globs, remote file= defs, duplicate detection
ledgerthe committed TSV state: ledger, reverse index, append-only acks
matchprevious ledger against current scan: ok, moved, changed, moved-unmarked, rewritten, deleted, new; change classification
checkthe findings engine: every state in the spec’s table with severity, remedy, and exit code
renderbuild-time expansion of directives to permalinks, values, code, chains, tables
config.ds/config.toml in the TOML subset the spec uses
procpluginboth sides of the process-plugin protocol for ds-pick-*, ds-resolve-*, ds-records-*, ds-<verb> executables
workspacemerging published ledgers and refs across repositories, the index layout, JUnit outcomes for assert=
recordsthe frontmatter record source for ds:table: a directory of markdown files, filtered by kind, where, sort, cols, limit
ext/structured (own module)the structured tier: YAML (yaml.v3), TOML (go-toml), and HCL (hcl/v2) extractors that bind keys, tables, blocks, and attributes by parsed extent; structured.All() registers the three
ext/treesitter (own module, cgo)the syntax tier: Go, TypeScript, TSX, JavaScript, Python, and SQL through tree-sitter; a def binds to the next declaration as the grammar sees it, nested declarations become Class.method, single literals are values, and hashes are taken over the token stream so formatters never cry wolf; treesitter.New(Grammar{…}) registers any other grammar
ext/records/sqlite (own module)the SQLite record source for ds:table on modernc.org/sqlite (pure Go); kind= names the table, the rest of the query is the shared records language
integrations/githubthe composite GitHub Action and the three workflows from spec §24: pull request comments, publish after merge, the nightly job that alone enables --run and --resolve
integrations/hugo, integrations/docusaurus, editors/vscode, .pre-commit-hooks.yamlthe Hugo module reading ds export hugo data, the Docusaurus remark plugin over ds render plus freshness from ds status --json, the VS Code client for ds lsp, and pre-commit hooks for impact --staged and report --literals (at the repository root, which is the only place pre-commit looks)
rootNew with functional options; Scan, Check, Snapshot, Render, Define (returns an Edit), Facts, Why, Map, Context, Impact, Find, Read, Ack, Triage, Audit, Rename, Graph, Blame, Report, Adopt