Skip to content

Let a harvest be taken back out - #23

Merged
R204570 merged 1 commit into
mainfrom
docs-architecture
Aug 20, 2026
Merged

Let a harvest be taken back out#23
R204570 merged 1 commit into
mainfrom
docs-architecture

Conversation

@R204570

@R204570 R204570 commented Aug 20, 2026

Copy link
Copy Markdown
Owner

The store could only ever grow. A harvest can be the wrong project, a partial
copy, or a table of contents stored as though it were the documentation — and
there was no way to remove one.

kb_store has had delete() on both backends the whole time. Nothing could ask
it to: no route, no UI, no CLI, no tool. The capability was stranded.

There is a live example of why this matters. The astro currently in the
store is 194 blog pages and 1 documentation page out of 323, marked
complete: True — harvested before the fixes in #22. Until now there was no way
to clear it.

Three human surfaces

DocsStore. Each version row gets a delete control; the card gets a second
one for the whole technology. Two clicks — the button becomes the
confirmation and reverts on its own after four seconds, which suits the weight
of the act better than a modal. Icon-only until armed, then it says "Really
delete?" in the danger colour.

The CLI, which is the one to reach for when clearing several:

python docsforge.py --forget pydantic@1.10        # one version
python docsforge.py --forget astro                # every version of it
python docsforge.py --forget a --forget b --yes   # several, no prompt

It prints what it is about to remove — pages and characters — and does nothing
without a typed yes or --yes.

HTTP, which is what the UI drives:

curl -X DELETE http://127.0.0.1:8000/api/library/astro
curl -X DELETE http://127.0.0.1:8000/api/library/pydantic/1.10

The model gets no such power by default

Deleting is the one irreversible thing DocsForge does, and a model that has
just mis-resolved a name is the last caller who should hold that lever
— 703
pages of Effect are one confident hallucination away. DOCSFORGE_ALLOW_DELETE=1
adds a forget_documentation tool for anyone who wants it.

The tool description also says plainly that re-harvesting does not need it:
harvesting the same name again replaces that version on its own. "Refresh this"
should never route through a delete.

Two details worth calling out

isatty() is not a sufficient guard. A pipe or a test harness can look like
a terminal and still hand back EOF, which raised EOFError in the middle of the
confirmation prompt on the first attempt. Anything other than a person typing
yes is now read as no — the alternative is deleting someone's corpus on the
strength of a closed stdin.

A null complete rendered as "partial harvest" in the version list. Three
states need three labels; "coverage unknown" is a different warning from a copy
known to be incomplete. Fixed while I was in there.

Verified

  • 375 passed, 22 skipped across both backends, with coverage for each
    surface: one version removed while the others survive, the files actually
    gone from disk
    rather than merely unlisted, a spelling like Effect.ts
    resolving to the stored effect before deleting, and the CLI refusing to act
    on an unconfirmed prompt.
  • Rendered in Chromium at 1280 and 420 in both themes against the real store:
    no console errors, no horizontal overflow.
  • Every surface exercised end to end — CLI, HTTP, and the tool.

The store could only ever grow. A harvest can be the wrong project, a partial
copy, or a table of contents stored as though it were the documentation -- and
there was no way to remove one. kb_store had delete() on both backends the
whole time; nothing could ask it to. The capability was stranded with no
route, no UI, no CLI and no tool.

Three human surfaces, because the person who harvested something is the one
who should decide it was a mistake.

DocsStore. Each version row gets a delete control and the card gets a second
one for the whole technology. Both take two clicks: the button becomes the
confirmation and reverts on its own after four seconds, which suits the weight
of the act better than a modal. Icon-only until armed, then it says "Really
delete?" in the danger colour. Verified in Chromium at 1280 and 420 in both
themes: no console errors, no horizontal overflow.

The CLI, for clearing several at once:

  docsforge --forget pydantic@1.10
  docsforge --forget astro
  docsforge --forget a --forget b --yes

It prints what it is about to remove -- pages and characters -- and does
nothing without a typed "yes" or --yes. isatty() alone is not enough of a
guard: a pipe or a harness can look like a terminal and still hand back EOF,
so anything other than a person typing yes is read as no. `url` becomes
optional to make room for this, with a clear usage error when neither is
given.

HTTP DELETE on /api/library/{tech} and /api/library/{tech}/{version}, which is
what the UI drives.

The model gets no such power by default. Deleting is the one irreversible
thing DocsForge does, and a model that has just mis-resolved a name is the
last caller who should hold that lever -- 703 pages of Effect are one
confident hallucination away. DOCSFORGE_ALLOW_DELETE=1 adds a
forget_documentation tool for anyone who wants it, and the tool description
says plainly that re-harvesting does not need it: harvesting the same name
again replaces that version on its own.

Also fixed while in the version list: a null `complete` rendered as "partial
harvest". Three states need three labels, and "coverage unknown" is a
different warning from a copy known to be incomplete.

375 passed, 22 skipped across both backends, with coverage for each surface --
one version removed while the others survive, the files actually gone from
disk rather than merely unlisted, a spelling like "Effect.ts" resolving to the
stored `effect` before deleting, and refusing to act on an unconfirmed prompt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APThnqHvfP2oBjBCBxBhd6
@R204570
R204570 merged commit f93c184 into main Aug 20, 2026
4 checks passed
@R204570 R204570 self-assigned this Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant