Skip to content

Releases: spencerbraun/backdraft

backdraft 0.16.0

Choose a tag to compare

@spencerbraun spencerbraun released this 30 Oct 13:08

A week on saying what the registry already knows: which drafts a new snapshot touches, where cited words went when the chunker re-cut them, and when a draft has changed since its record judged it.

  • locate finds cited words the chunker changed a hash around without changing a word: a short heading merged into the cited chunk, or a paragraph grown past the cut so the cut runs through the words. The moved line says whole inside a chunk that now holds more or across two adjoining chunks, and a span proposes both tokens ;-separated, which --apply writes as is. bind records moved_to for the first case; a span writes none, since the key is one token in the artifact format.
  • ingest that makes a new generation names each draft in the project whose last bind cited that source, read from the records under .backdraft/records/, with the exact backdraft locate to run for it; show on a drifted token closes with the same commands instead of a <doc.md> placeholder. Unreadable records are counted, so a short list is never mistaken for a complete one.
  • A draft kept outside its project is refused with the directory that was searched and, when there is one, the registry that does not cover it and the two ways to connect them (move the draft, or set BACKDRAFT_HOME), instead of advice to backdraft init that would make an empty registry beside it.
  • render refuses a draft edited since its bind — a claim added, reworded, re-cited, or moved by an edit above it — at exit 1 with nothing written and the first such claim named, and the re-bind it names carries the record's --session. Prose edits that move no claim still render.

1730 tests.

backdraft 0.15.0

Choose a tag to compare

@spencerbraun spencerbraun released this 23 Oct 13:13

A week on drift: the record and the artifact now know when cited words only moved, locate can make the swap, and a dry run says what an ingest will cost before it runs.

  • bind writes moved_to on a drifted citation whose cited words stand unchanged at exactly one place in the source's current snapshot, and the artifact's card says where they stand instead of word-diffing them against the unrelated paragraph that took over their address. The status stays drifted; the key is optional under artifact-v1, so every record without one is byte-identical apart from the legend. The footnotes projection and the artifact skill follow.
  • backdraft locate <doc.md> --apply writes each moved citation's new token over its old one inside that claim's link — whole ;-separated pieces only, through a temporary file and a rename — and touches nothing else: not an ambiguous or gone citation, not prose, not the registry or the ledger. It refuses at exit 1, writing nothing, if the document changed while it was reading. The plain run's closing line now names --apply rather than telling you to edit tokens by hand.
  • backdraft session show --in <slug> lists, per page, the chunks this session was shown and the ones it was not (p1 shown c1-c25 not shown c26-c57) and closes with the read that covers the first gap — the question to ask after losing context mid-document, instead of re-reading from the top. A sheet reports whether its page was shown and a count of cells. Mints nothing.
  • backdraft ingest --dry-run ends each line with the extractor that would run, chosen the way the ingest chooses it, so a PDF says before the batch whether it goes to vlm — a vision-model call per page — or to the free text layer; an unknown --extractor or a --config key the chosen one never reads fails the dry run with the ingest's own reason. A source already ingested says what it was extracted with and whether this run would re-extract it.

1686 tests.

backdraft 0.14.0

Choose a tag to compare

@spencerbraun spencerbraun released this 16 Oct 16:55

A week on getting from where a command points to the passage it means: chunk reads, search excerpts that show the match, and reports that say what to do next.

  • backdraft read <slug> p1.c9 and p1.c9-c14 read the chunks a table of contents lists, by the locator it prints. What a chunk read prints is minted exactly as a page read's is; a range clips to the chunks that exist, runs past the budget with the same continuation line, and a backwards or empty range exits 1 naming the page's chunk span. A sheet refuses the form and says how it is read and cited.
  • search excerpts now open on the words that matched. A hit whose match the first 160 characters already show prints exactly what it did; otherwise the excerpt moves to the densest run of matches, at a word, elided at whichever end it was cut. Display only: no token, receipt or record moves.
  • bind and verify reports close with one next: line per kind of failure they printed, naming the exact command to run with the document and session filled in — show for not_shown, search for unresolved, locate for drifted, a rebuild for an edited artifact. A clean report is byte-identical to before; --json is unchanged by it.
  • verify now re-checks not_shown against the ledger session the record names, when that session is in the registry it checks against — the one check that says the author read what they cited. A ledger: line says when it could not be asked (the session is not in this registry, or the bind had none). verify --json gains sources.ledger.
  • A spreadsheet or CSV is no longer marked little text for being short: a sheet is marked only when none of its cells holds a value, so a three-row rate table is cited like any other source.
  • A registry locked by another writer, or otherwise unopenable, is no longer called damaged with advice to restore it; the message says nothing changed and to run again when the other writer finishes.

1645 tests.

backdraft 0.13.0

Choose a tag to compare

@spencerbraun spencerbraun released this 09 Oct 13:07

A week on saying the thing a caller would otherwise learn the hard way: what this install can do, what was withdrawn, which session a bind judged against, and when a flag was ignored.

  • backdraft doctor reports every optional capability at once — the registry, page images (poppler), the vision model, legacy .xls, the entail check and math rendering — each ready or missing. A missing one gets a costs line saying what the verbs do without it, and its reason is the exact string the verb prints when it hits the gap, so the two cannot disagree. Keys are shown as set or absent, never their values. Read-only, and always exit 0. Run it first in an unfamiliar environment.
  • backdraft ls --withdrawn lists what forget took out, each with the date it went and the backdraft ingest that brings it back. Plain ls is unchanged, except that a registry whose every source is withdrawn now says so instead of claiming to be empty.
  • bind names the session it judged not_shown against on its first line (against session <id>), and closes with a warning when that is the shared default session. The artifact carries the same meaning: the legend's not_shown entry says what a default session costs, and backdraft verify prints a session: default line from the file alone.
  • ingest --slug on a source already ingested now says --slug not applied and names the slug the document keeps. Nothing renames a document — not --slug, and not forget followed by a re-ingest, which brings it back under the same slug. The dry run says the same.
  • A damaged or foreign .backdraft/registry.db is now one exit-1 line naming the file, instead of a traceback on every command; nothing is repaired or replaced, and doctor reports it as the registry's gap.

1578 tests.

backdraft 0.12.0

Choose a tag to compare

@spencerbraun spencerbraun released this 18 Sep 13:26

A week on what happens after the draft: output an agent can act on, a check on a file somebody sent you, and citations that survive a source changing under them.

  • backdraft bind --json and backdraft verify --json print one JSON object instead of the report, with the same exit codes. bind --json is the record itself, byte for byte what bind writes. verify --json is a new backdraft/verify-v1 object whose findings each carry a kind: receipt means the file was edited after it was written, source means the sources no longer stand behind a citation. Both used to be the same exit 2, and now a hook or an agent can tell them apart without reading a sentence.
  • backdraft verify <artifact> --against <project> checks a file you were sent against a project you have, without copying it into the checkout. The flag is you saying where the file came from, and verify still never infers that. A path with no registry in it is refused rather than quietly checking the file alone.
  • A claim can straddle two chunks, and the gate now says so. A search hit whose text may run on into the next chunk carries that chunk's token too, indented under its excerpt. That covers a paragraph the chunker cut in two and every page break. A page read that reaches the end of its page closes with the next page's first chunk. Both tokens are minted, so a sentence that runs on is cited by both halves.
  • backdraft locate <doc.md> finds where a re-ingest moved the text a document cites. A paragraph inserted above a citation shifts its chunk number, so it comes back drifted even when its words never changed. For each drifted citation, locate finds the exact cited text in the new snapshot: moved gives the token that holds it now, ambiguous names the places when there are several (or when the citation is a cell), and gone proposes nothing. It is read-only and rewrites nothing.
  • backdraft show on a drifted token now points at locate. What stands at the old address after an edit can be a different paragraph.
  • bind, locate and render refuse a document that is not UTF-8, naming it, where locate and render used to stop with a traceback. render says where it looked for a missing record, including .backdraft/records/, which is where bind writes one. bind --help now states what it writes and its exit codes.
  • Ingesting an .xls without the [xls] extra says to install it, instead of saying the format is unsupported.

1534 tests.

backdraft 0.11.0

Choose a tag to compare

@spencerbraun spencerbraun released this 11 Sep 13:27

A week on sizing up a source before spending on it — what it will be called, how much of it a read costs, and whether there is anything in it.

  • backdraft ingest <source> --dry-run says what each source would be called before that is permanent: the slug and media type it would take, whether it is already ingested (under which slug, and whether withdrawn), and the numbered slug a taken name would land on. Nothing is fetched, written or minted. A slug is fixed once a token carries it, so this is how to see the default before --slug has to overrule it.
  • A page read has a budget. One backdraft read shows 12,000 characters — 200 rows of a sheet — unless --limit says otherwise, so a scraped article no longer spends a context window in a single call with nothing to say so. A read that stops short closes with the total and the exact command that continues it. The cut never lands inside a chunk, so every token still names text you were shown whole, and ordinary pages print exactly what they did.
  • A web page's table of contents is its chunks: backdraft read <slug> on a one-page source lists p1.c1, p1.c2, … with what each opens with, under the name the page gave itself in its <title> — the map for finding where the article starts past the site's navigation. The artifact names a fetched page by that title too, wherever the record carries one.
  • A source that came back a shell says so wherever it is listed. backdraft read's document list, that source's table of contents and backdraft ls all mark it little text: N chars, so a registry ingested in another session still tells the writer which sources are login walls and scans.
  • Every hint the gate prints — search's, show's, the table of contents', session show's and a short read's continuation — now carries a --session you typed, so following one no longer reads into a different ledger from the one you bind against.
  • ingest --dry-run refuses a malformed --config the way ingest does, rather than predicting a run that cannot start.

1440 tests.

backdraft 0.10.0

Choose a tag to compare

@spencerbraun spencerbraun released this 04 Sep 13:24

A week on saying what happened in a form an agent can act on — and one on taking something back.

  • backdraft forget <slug> withdraws a source that ingest should not have taken: out of read, search and ls, with nothing removed, so a token already written into somebody's draft or artifact still shows its receipt and bind reports it unresolved naming the withdrawal rather than passing it silently. Ingesting the source again is the undo, as the same document under the same slug.
  • backdraft session show reads the ledger back: a total and one line per document, so "have I read enough to write this yet?" is answerable before the draft exists rather than by binding it and counting not_shown. It also says out loud, at exit 0, what the shared default session costs.
  • An unreadable source now says what to do about it instead of relaying an errno — a directory wants the files inside it, a missing path wants its spelling checked, an unreadable file wants its permissions fixed — and a source with no bytes in it is a failure rather than a document with nothing to cite. Same rule for the URL half, including a server that sends an empty body.
  • The markdown projection bind --bound writes now calls a fetched source by its page, the way ingest, ls, the gate's list and the artifact already did. That is the form that travels into a pull request or an email, where the artifact cannot follow.
  • An unknown slug is told about the sources a withdrawal hid, rather than being told the registry is empty.

1340 tests.

backdraft 0.9.0

Choose a tag to compare

@spencerbraun spencerbraun released this 28 Aug 13:23

A week on the reader's side: the recipient gets a command instead of a procedure, search stops hiding what it cut, and a formula renders as a formula.

  • backdraft verify — the check the recipient can make. Hand it the .backdraft.html or the .backdraft.json and it runs the whole of spec/artifact.md § Checking an artifact: every snippet rehashed against the sha256 recorded beside it, every token checked against the anchor it names, summary recounted from claims. That tier needs nothing but the file, which is the point — it is the check someone who was emailed the artifact can still make. Run it inside the project the document was bound in and it also re-resolves every citation against the registry, so a source that moved since shows up rather than going unnoticed. Read-only: no session, no minting, because an audit must not make its subject citable. It found a real bug on the way in — the spec and every artifact's embedded legend said the token's hash must prefix the anchor's, which is false for every drifted citation by construction, and implemented as written would have called every drifted artifact forged. Both statements corrected.
  • search reports the size of the answer, not the size of the page. --limit defaults to 20 and a capped run said 20 results whether twenty matched or two hundred did. It now says 20 of 56 results and closes with the exact widening command, shell-quoted. An uncapped run prints what it always printed, byte for byte.
  • Math renders. LaTeX written the ordinary way — $...$, $$...$$, \(...\), \[...\] — becomes MathML in the artifact behind the new [math] extra: static markup the browser lays out itself, so the artifact stays one file with no script and no font to fetch. Currency ($250 per unit, $1.2M) is never mistaken for a formula, and neither are shell variables in code. Without the extra a formula renders verbatim rather than wrong, and render now says so and names the install, so an artifact of raw TeX cannot pass for one of typeset math.
  • A verifier's skip says why it skipped. overlap: pass 13, skip 4 named four unchecked citations without saying whether that was benign; the reasons now print under the method, grouped. That matters more, not less, for checks that never gate.
  • The registry export is a specified format. backdraft export has written backdraft/registry-v1 since v0 — a version string promising a specification that did not exist. spec/registry.md joins tokens, chunking and artifact as normative prose, pinned by a test that fails in both directions, so an undocumented key and a documented-but-absent one are equally a failure.
  • Fixes. snake_case_name is no longer rewritten to snake<em>case</em>name by the renderer — intraword _ is not a delimiter, per CommonMark. verify's help no longer prints markdown asterisks at the terminal, and its exit code no longer decides by reading the wording of its own report.

1274 tests.

backdraft 0.8.0

Choose a tag to compare

@spencerbraun spencerbraun released this 21 Aug 13:20

A week on web sources: a fetched page is named, slugged and shown as the page it came from, and ingest finally says what it did.

  • The demo cites a web page. backdraft.dev/demo.html now carries three sources, one of them a Wikipedia article ingested at its permanent link (?oldid=), so a citation into the web is visible in the artifact anybody actually looks at — and stays resolved rather than reporting drifted the next time somebody edits the page.
  • Every surface names a fetched source by its URL. ingest, ls, backdraft read's document list and its table-of-contents headline all print the page rather than the filename the fetch invented to stage the bytes in. ls loses its fifth column: one true name replaces two names of which one was fictional.
  • A URL's slug names its site when its path names nothing. Two sites' index.html pages used to become index and index-2; they are now named for their hosts. Directory indexes, generic handlers and bare numeric ids earn the host in front of them; a path that already names its page (/reports/q4-2025) is untouched. Truncated slugs no longer keep a dangling hyphen.
  • ingest says which of its three outcomes happened, and how much text came out. Each line carries the extracted character count, plus unchanged for a no-op or new generation when the bytes moved — the moment older citations can start reporting drifted, now named alongside the command that checks. A source that came back thin — a scan with no text layer, a login wall, a deck of images — gets a note naming the likely cause and what to do, at exit 0.
  • Fixes. The artifact-reading skill described the JSON island's escaping wrongly, which would send an auditing agent reaching for an HTML unescape that does nothing; backdraft ls --help no longer points a user at a module path.

1190 tests.

backdraft 0.7.0

Choose a tag to compare

@spencerbraun spencerbraun released this 14 Aug 13:21

Tokens run backwards, and a source list runs to the end.

  • backdraft show <token>... — the inverse of minting. Hand it a token from an artifact, a draft, or a bind report and it prints that token's bind status, its locator, and the verbatim snippet. drifted prints both the snippet that was cited and the one standing there now; unresolved says whether the slug or the locator is the wrong half; malformed names the grammar. It is the gate, so what it shows is minted and citable — a token copied out of somebody else's artifact becomes yours to cite by showing it. Exit 1 if any token named nothing.
  • ingest finishes its list. A source that cannot be read no longer ends the run: everything else still lands, and the command exits 1 printing N of M sources ingested with one line per failure and its reason. Re-running the same list after a fix re-ingests nothing that already landed unchanged.
  • --config keys are declared and checked. Each extractor declares the keys it reads, and the parsed config is validated against the extractor that was actually chosen — so --config dpy=300 fails and names the keys that would have worked, instead of being silently ignored. The keys are listed in backdraft ingest --help, pinned to the declarations by a test.
  • Bind report line items name the claim. ! unresolved: <token> — <the claim's own words> @<offset> — exit 2 now says which sentence to go edit, and the offset is what tells two line items carrying the same token apart.
  • Maintenance. The drift lookup that had landed in both the gate and bind is now one registry-owned function; DESIGN.md's decision table is un-split and pinned by a test; show no longer prints the same next step twice; EXIT_USAGE documents what it actually covers.

1146 tests.