Documentation
¶
Overview ¶
Package refs finds the inbound references to a topic document and moves a source into and out of quarantine without leaving dead links (spec §7.1).
References are resolved with internal/resolve exactly like lint does: topic-relative path, topic-slug-prefixed path, vault-relative path or file stem, with Obsidian semantics (titles and aliases never resolve a link).
Index ¶
Constants ¶
const ( OpMove = "move" OpEdit = "edit" OpRestore = "restore" )
Ledger operations.
const ( // MarkerIndex marks an index-line removal. MarkerIndex = "index" // MarkerMove marks the file move (and, on restore rows, the move back). MarkerMove = "move" )
Ledger line_or_key markers.
const ( TriageQuarantined = "quarantined" TriageKept = "kept" )
Triage values written on quarantine and restore (spec §6).
const ( // KindIndexLine is a list item, numbered item or table row of a // `wiki/index/` document that links to the target. KindIndexLine = "index-line" // KindFrontmatter is a frontmatter value that links to the target. KindFrontmatter = "frontmatter" // KindBody is a body wikilink or markdown link to the target. KindBody = "body" )
Reference kinds.
const IndexDir = "wiki/index/"
IndexDir is the topic-relative directory whose list items and table rows are removed when every link they carry points to quarantined files.
const LedgerFile = "quarantine-ledger.jsonl"
LedgerFile is the quarantine ledger inside <topic>/.decisions/.
Variables ¶
var ErrLocked = errors.New("refs: file is locked (locked: true); not quarantined")
ErrLocked is returned when the file to quarantine carries `locked: true`: kb never moves or edits a locked file.
var RemovableKeys = []string{
"sources", "related", "extends", "prerequisite", "example_of", "contradicts",
"affects", "supersedes", "concepts", "informed_by",
}
RemovableKeys are the frontmatter list keys whose items pointing to a quarantined file are removed (spec §7.1).
Functions ¶
func LedgerPath ¶
LedgerPath returns <topic>/.decisions/quarantine-ledger.jsonl.
Types ¶
type LedgerEntry ¶
type LedgerEntry struct {
QuarantineID string `json:"quarantine_id"`
Time string `json:"time"`
Op string `json:"op"`
File string `json:"file"`
Original string `json:"original,omitempty"`
LineOrKey string `json:"line_or_key"`
Line int `json:"line,omitempty"`
Before string `json:"before,omitempty"`
After string `json:"after,omitempty"`
HashBefore string `json:"hash_before,omitempty"`
HashAfter string `json:"hash_after,omitempty"`
Reason string `json:"reason,omitempty"`
}
LedgerEntry is one row of the quarantine ledger. Edit rows record the exact bytes removed (Before) and written instead (After, "" for a pure deletion) at 1-based Line, plus the whole-file sha256 before and after the edit. The move row records the quarantine path in File, the original path in Original, and the file hash before and after the triage keys were set.
func LoadLedger ¶
func LoadLedger(topicRoot string) ([]LedgerEntry, error)
LoadLedger reads every well-formed ledger row in file order (a missing ledger is empty).
type Options ¶
type Options struct {
// VaultPath is the vault root (the parent of the topic directories).
VaultPath string
// TopicRoot is the topic directory.
TopicRoot string
// Path is the topic-relative file: `raw/...` for Quarantine; the original
// or the quarantine path for Restore (used when ID is empty).
Path string
// Reason is the triage_reason written on quarantine (off_topic, thin, ...).
Reason string
// ID is the quarantine id; Quarantine derives one when empty.
ID string
// Writer writes the triage keys; one is opened on the topic when nil.
Writer *corpus.Writer
// State, when set, keeps the state row's written hashes in step with the
// removal of items from kb-owned list keys, so a kb-written relation
// stays kb-owned after quarantine and restore.
State *corpus.StateStore
// Now defaults to time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
Options configures Quarantine and Restore.
type Quarantined ¶
type Quarantined struct {
ID string `json:"id"`
Path string `json:"path"`
Original string `json:"original"`
Reason string `json:"reason"`
Time string `json:"time"`
Edits int `json:"edits"`
}
Quarantined is one active quarantine.
func List ¶
func List(topicRoot string) ([]Quarantined, error)
List returns the active quarantines of a topic (move rows without a matching restore of the move), oldest first.
type Ref ¶
type Ref struct {
// File is the topic-relative path of the referring document.
File string `json:"file"`
// Kind is KindIndexLine, KindFrontmatter or KindBody.
Kind string `json:"kind"`
// Key is the frontmatter key for KindFrontmatter references.
Key string `json:"key,omitempty"`
// Line is the 1-based line number in the referring file.
Line int `json:"line"`
// Text is the referring line (index lines, frontmatter items) or the
// link markup (body links).
Text string `json:"text"`
}
Ref is one inbound reference to a topic document.
type RestoreResult ¶
type RestoreResult struct {
ID string
// Restored lists the ledger rows that were replayed.
Restored []LedgerEntry
// Manual lists edit rows that were not replayed because the touched file
// changed since the quarantine; they are ready to print for manual repair.
Manual []LedgerEntry
// Path is the topic-relative path the file was moved back to.
Path string
}
RestoreResult reports a restore.
func Restore ¶
func Restore(ctx context.Context, opts Options) (RestoreResult, error)
Restore replays the ledger of one quarantine in reverse: every removed span is put back at its recorded line when the touched file still hashes to the recorded hash_after (otherwise the row is returned in Manual and the file is not touched), the file is moved back, `triage: kept` is written and `triage_reason` removed through the corpus writer, and `restore` rows are appended. After a quarantine and restore with no edits in between, every touched index and frontmatter file is byte-identical to the original; the restored file itself differs only in its triage keys (`triage: kept` instead of no key or another value, `triage_reason` removed).
type Result ¶
type Result struct {
ID string
// NewPath is the topic-relative quarantine path.
NewPath string
// Removed lists the ledger rows of every removal (move row first).
Removed []LedgerEntry
// BodyRefs are references left in place: body links and index lines that
// also link to files that are not quarantined. Lint reports them as
// link-to-quarantined.
BodyRefs []Ref
// Unremoved are frontmatter references that could not be removed line by
// line (flow-style or scalar values, keys outside RemovableKeys, locked
// documents); they are left for the owner.
Unremoved []Ref
// SkippedUserKeys lists triage keys the writer left alone because the
// user owns them.
SkippedUserKeys []string
}
Result reports a quarantine.
func Quarantine ¶
Quarantine moves a source to raw/_quarantine/ and removes the references that would otherwise dangle (spec §7.1):
- `triage: quarantined` and `triage_reason` are written through the corpus writer (a locked file returns ErrLocked and nothing changes);
- the file is moved to raw/_quarantine/<path without raw/> (refused when the destination exists);
- index lines in wiki/index/ whose every link points to quarantined files are removed;
- items of RemovableKeys frontmatter lists pointing to the file are removed from other topic documents, deleting exactly their lines;
- body links are left in place and returned in BodyRefs.
Recovery information is durable before each mutation: the move row is appended before the file moves and every edit row before its file is rewritten, so Restore can always undo what happened. A row whose mutation never took place (the process failed in between) is recognized by Restore from the file state: the quarantined file is still at its original path, or a touched file still hashes to the row's hash_before. When the move row cannot be recorded, or the move itself fails, the triage keys are rolled back and nothing is left moved.