Linkwarden AI Tagger
An imported bookmark library began with 1,950 bookmarks in a top-level Unorganized collection. The number is provenance for the clean baseline, not a claim about current live state. The engineering problem was not simply to ask a language model for labels: classification needed stable destinations, consistent metadata, useful human review, and a mutation path that could not treat plausible model output as trusted state.
The resulting classifier reads bookmarks, sends bounded classification requests to a private inference service, validates the structured response against repository-owned policy, and writes an auditable result. Live classification is read-only by default. Explicit apply mode permits only high-confidence, freshly revalidated results to change Linkwarden.
A controlled taxonomy
The taxonomy is source-controlled Markdown rather than prompt text or live application state. One file owns the collection hierarchy, another owns canonical tags, aliases, roles, ordering, and selection rules, and a rulebook owns classification and workflow policy. Python code parses and validates these sources, so prompts and synchronization consume the same representation instead of maintaining parallel lists.
Collections answer where a bookmark primarily belongs. Tags carry reusable detail such as platform, content type, domain, discipline, technology, concept, or qualifier. The classifier may select only exact existing collection paths and canonical tags. Aliases are normalized through explicit mappings; fuzzy matching is not allowed to turn a near-spelling into a write.
Unorganized remains a workflow fallback for unclear subjects, taxonomy gaps, or cases where forcing a destination would be misleading. The model may propose one collection and up to three missing tags for review, but suggestions never enter the applied tag list and never create live taxonomy as a side effect.
Separating API access from policy
The generic Linkwarden API client authenticates, reads raw owned collections and bookmarks, updates links, and verifies returned state. It does not decide which bookmarks are eligible or how confidence changes behavior.
Classifier policy is split again by responsibility:
- pure validation checks model output against the loaded taxonomy and schema;
- inference owns private worker requests and response extraction;
- workflow owns eligibility, queue state, writes, verification, and recovery;
- reporting owns machine-readable audit records and deterministic review documents.
That dependency direction also lets taxonomy synchronization reuse the Linkwarden API without importing classifier behavior. Network access does not implicitly authorize policy decisions, and policy validation does not perform filesystem or remote mutation.
The inference boundary
The worker is a private authenticated llama.cpp API on a separate GPU workstation. The classifier sends bookmark metadata plus the controlled policy and expects one structured JSON object. Bookmarked content is untrusted input: instructions embedded in it do not override classification policy.
The model returns an exact collection path, controlled permanent tags, confidence, uncertainty, a recommended action, a reason, optional provenance evidence, and review-only taxonomy suggestions. The inference response is a proposal until deterministic code accepts it.
Deterministic validation
Validation rejects unknown fields, non-existent collections, uncontrolled tags, workflow tags selected as permanent metadata, invalid confidence/uncertainty combinations, and malformed suggestions. High confidence must have no uncertainty and recommend acceptance. Medium and low confidence must identify a concrete uncertainty and a human action that can resolve it.
Canonical permanent tags are limited to nine. Exact duplicates may be removed and valid tags may be reordered by their defined roles; both normalizations are recorded. Other invalid output is not repaired by fuzzy matching or partially written. Failed output remains available for targeted retry or review.
This boundary keeps model reasoning flexible while making accepted state changes finite and mechanically checkable.
Read-only first, mutation by explicit authority
Export input is always read-only. Live Linkwarden input is also read-only unless apply mode is explicitly selected. Read-only mode authenticates, verifies live response shapes and taxonomy agreement, discovers candidates, classifies them, and writes local reports without adding queue markers or changing bookmarks.
Apply mode authorizes mutation but does not guarantee it. Only high-confidence results may advance to a write. Medium- and low-confidence classifications remain report-only with their proposed collection, tags, uncertainty, and recommended action. Invalid results change nothing.
Guarded writes and stale-state protection
Before any authorized high-confidence write, the workflow re-fetches the bookmark and compares all classifier inputs with the state that was classified. A changed bookmark invalidates the proposal rather than allowing a stale classification to overwrite newer human or application changes.
The workflow records the existing collection and full tag state before mutation. It preserves existing permanent tags, merges controlled suggested tags without duplicates, enforces the tag limit, applies an exact existing collection, and removes workflow markers only when the high-confidence transition completes.
The update is followed by a fresh read that verifies the complete remote collection and tag state. A successful HTTP request alone is not considered a successful mutation.
Recovery behavior
A queue change, final write, verification failure, or interruption after mutation begins triggers restoration of the recorded state. Recovery is itself re-read and verified. If restoration cannot be completed and verified, the run records both the original and recovery failures, marks remote state as uncertain, and aborts instead of processing more bookmarks.
This is recovery for classifier-owned Linkwarden state, not a database restore. PostgreSQL backup, Meilisearch migration, and workload reconstruction remain separate responsibilities of the Linkwarden architecture.
Audit and reproducibility
Every run gets a timestamped local report directory. Machine-readable JSONL records hold classifications, errors, write outcomes, and—when apply mode is used—pre-mutation rollback state. Deterministically rendered Markdown groups low and medium confidence ahead of high-confidence results so human work is visible first.
Run metadata distinguishes the input source from mutation authority. It records lifecycle status and counts, the hashes of the exact taxonomy and rulebook inputs, source identity for export runs, and Git revision and working-tree state when available. A clean finish becomes completed; handled fatal errors and interruptions become aborted; an unclean termination can leave running, which means only that no clean completion was recorded.
Runtime reports can contain private bookmark titles and URLs, so they remain ignored and outside this public repository. The reproducibility design is described here without publishing its sensitive output.
Classification QA and the feedback loop
Medium- and low-confidence classifications are not failed high-confidence writes. They preserve uncertainty and a concrete recommended action for human review. Untouched Unorganized bookmarks can be selected again later, while an explicit reclassify workflow tag requests another pass after a wrong result, taxonomy change, or accepted suggestion.
Collection and tag suggestion reports group repeated gaps and attach supporting bookmark IDs and reasons. Frequency prioritizes review but does not approve a change. A human accepts, edits, or rejects a proposal; only a manual edit to the canonical taxonomy followed by validation and synchronization makes the new term available. Affected bookmarks are then selectively reclassified rather than rerunning the entire library.
The loop improves classification QA without granting the model authority to evolve taxonomy or mutate ambiguous results. Human decisions remain the source of structural change; automation makes those decisions consistent, reviewable, and repeatable.
Validation surface
Offline tests use mocked Linkwarden and inference communication. Coverage includes schema and taxonomy validation, live read-only behavior, candidate selection, explicit mutation entry, preservation of existing tags, stale-input refusal, medium/low report-only behavior, complete-state verification, restoration after queue/write/verification failures, fatal handling when recovery cannot be verified, audit lifecycle metadata, and taxonomy synchronization differences.
The architecture deliberately stops short of broader guarantees. The classifier does not back up PostgreSQL or Meilisearch, rebuild the K3s workload, make the workstation highly available, or convert uncertain recommendations into unattended writes.