source

package
v0.13.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package source is the personal sources corpus and its provenance ledger (itd-76, spc-31): a local-only store of documents the agent may consult, a CSL-JSON bibliography describing them, and one append-only influence ledger per consuming repository. It never prints and never exits — front doors under internal/surface/* format its results.

The trust boundary it enforces is adr-41 / brief invariant 9, cited rather than restated: documents and ledgers never leave the user tier, and a public citation needs both the source's permission_status and a human-flipped ledger line.

Layout of a corpus directory (the user-level home's `sources/`, by default ~/.abcd.noindex/sources):

sources.json            CSL-JSON array; each entry's `custom` block carries
                        confidential, permission_status, keywords, aliases,
                        ban_authors and file
confidential/<key>/     original.<ext>, text.md, and derived artefacts
public/<key>/           the same shape for a freely citable source
ledger/<repo>.jsonl     append-only influence records for one repository

FOLDER LOCATION IS THE CLASSIFICATION. `custom.confidential` mirrors it, and a corpus where the two disagree is refused wholesale by every step that derives a ban from it (the safe direction: the block already written keeps banning). The corpus is itself a git repository with no remote; its history is the tamper-evidence layer, and every write here is committed.

Matching is not this package's. The projection of a confidential entry into patterns and every scan run through banlist.PhrasePattern and banlist.ScanText, the private name layer's one matcher, so the pre-commit guard and cite-check cannot disagree about what a confidential source is called.

Index

Constants

View Source
const (
	ClassConfidential = "confidential"
	ClassPublic       = "public"
)

Classes. The folder a source sits in is one of these, and nothing else decides it.

View Source
const (
	PermissionCitable          = "citable"
	PermissionNoPublicCitation = "no-public-citation"
	PermissionInternal         = "internal-never-cite"
	PermissionAIGenerated      = "ai-generated-never-cite"
	PermissionAskAuthor        = "ask-author"
)

Permission statuses. PermissionCitable is the ONLY value that grants the right to cite (adr-41 gate 1); every other value, including one this vocabulary does not know, withholds it.

View Source
const (
	SourcesFile = "sources.json"
	LedgerDir   = "ledger"
	TextFile    = "text.md"
)

File and directory names inside a corpus.

View Source
const BlockOwner = "sources"

BlockOwner names the generated block the corpus owns in a repository's private banlist, and heads every key in it: `sources/<key>/<field>`.

Variables

View Source
var (
	// ErrNoCorpus reports that the corpus directory does not exist. Every step that
	// needs a corpus returns it, and a front door turns it into one loud line.
	ErrNoCorpus = errors.New("sources corpus is absent")
	// ErrCorpusInvalid reports a corpus directory that exists but cannot be used.
	ErrCorpusInvalid = errors.New("sources corpus is not usable")
	// ErrInvalidEntry rejects a source entry.
	ErrInvalidEntry = errors.New("invalid source entry")
	// ErrIdentifyingKey rejects a confidential entry whose key names it: the key is
	// the one handle every refusal and every scan prints.
	ErrIdentifyingKey = errors.New("a confidential source's key would name it")
	// ErrDuplicateSource rejects a key the corpus already carries.
	ErrDuplicateSource = errors.New("source key already exists")
	// ErrUnknownSource reports a key the corpus does not carry.
	ErrUnknownSource = errors.New("unknown source key")
	// ErrClassMismatch reports a corpus whose folders and entries disagree.
	ErrClassMismatch = errors.New("the corpus's classes disagree")
	// ErrInvalidLedger rejects a ledger record, or reports a ledger that does not read.
	ErrInvalidLedger = errors.New("invalid ledger record")
	// ErrCitationRefused is the two-gate refusal of a cited_publicly flip.
	ErrCitationRefused = errors.New("public citation refused")
)

Sentinel errors. No message carries a confidential title, alias or author: a refusal is output like any other, and keys are the only handle output names.

View Source
var Influences = []string{"supports", "contradicts", "method", "background"}

Influences is the closed vocabulary of a ledger line's influence.

Permissions is the closed vocabulary `add` and `declassify` accept.

Functions

func DefaultDir

func DefaultDir() (string, error)

DefaultDir is the corpus's default location: the user-level home's `sources/`. Relocating the home is itd-77's concern; a caller may pass any directory.

Types

type AddRequest

type AddRequest struct {
	Corpus string
	Key    string
	Title  string
	// Type is the CSL item type; "document" when empty.
	Type  string
	Class string
	// Permission is the permission_status; by class when empty (confidential:
	// no-public-citation, public: citable).
	Permission string
	Authors    []Name
	Year       int
	Venue      string
	URL        string
	Keywords   []string
	Aliases    []string
	BanAuthors bool
	// Original is the document to store (any format); Text is its extracted text.
	// A Markdown or plain-text original is its own text.
	Original string
	Text     string
}

AddRequest registers one source. Class is required and has no default: the classification is declared once, at ingestion, and a forgotten flag must not file a confidential document as public.

type AddResult

type AddResult struct {
	Key        string   `json:"key"`
	Class      string   `json:"class"`
	Permission string   `json:"permission_status"`
	Folder     string   `json:"folder"`
	Files      []string `json:"files"`
}

AddResult is what a registration or a declassification did, named by key.

func Add

func Add(req AddRequest) (AddResult, error)

Add registers a document: the CSL-JSON entry with its custom block, the original and its extracted text under <class>/<key>/, committed in the corpus repository. Every check runs before the first write, and no refusal quotes a title, alias or author.

func Declassify

func Declassify(corpus, key, permission string) (AddResult, error)

Declassify is the visible move of a published confidential source: the folder moves confidential/<key> → public/<key> by `git mv`, the entry's confidential flag and permission_status follow, and the corpus commits both as one change. The next banlist sync drops the key's strings, and its ledger lines become flippable when the permission set here grants citation (citable, the default).

type AppendRequest

type AppendRequest struct {
	Corpus      string
	Repo        string
	DecisionRef string
	Claim       string
	SourceKey   string
	Locator     string
	Influence   string
	UsedIn      []string
	// Corrects is the 1-based line this record corrects, or 0.
	Corrects int
	Now      time.Time
}

AppendRequest records one influence.

type AppendResult

type AppendResult struct {
	Path   string `json:"path"`
	Line   int    `json:"line"`
	Record Record `json:"record"`
}

AppendResult is a line written (or read), with its number.

func Append

func Append(req AppendRequest) (AppendResult, error)

Append adds one influence record to repo's ledger and commits it. cited_publicly is always false here: exercising the right to cite is Flip's, and only Flip's.

func Flip

func Flip(corpus, repo string, line int, now time.Time) (AppendResult, error)

Flip exercises the right to cite for one ledger line (adr-41 gate 2), and only after gate 1 grants it: the line's source must sit in public/ and carry permission_status citable. A refusal names the failing gate and appends nothing. A successful flip is itself a new line — the original with cited_publicly true and flips naming it — so the ledger keeps who cited what, and when.

A person flips a line; an agent never does. The binary cannot tell the two apart, so the command pages carry that rule.

func List

func List(corpus, repo string) ([]AppendResult, error)

List returns repo's ledger, numbered from 1. An absent ledger is empty.

type CiteReport

type CiteReport struct {
	// Sources counts the confidential sources checked against.
	Sources  int       `json:"sources"`
	Findings []Finding `json:"findings"`
}

CiteReport is a cite-check's outcome.

func CiteCheck

func CiteCheck(corpus string, text []byte) (CiteReport, error)

CiteCheck scans text for every confidential source's projected strings through the private layer's matcher — the engine the pre-commit guard runs — and reports offenders by key, field and position only.

func (CiteReport) Clean

func (r CiteReport) Clean() bool

Clean reports whether the text named no confidential source.

type Corpus

type Corpus struct {
	Dir     string
	Entries []Entry
	// contains filtered or unexported fields
}

Corpus is a loaded corpus: its entries, the raw bytes of each (so a rewrite preserves what this package does not model), and the folder each key sits in.

func (*Corpus) Class

func (c *Corpus) Class(key string) string

Class is the class a key's folder declares, or "" when it has none or two.

func (*Corpus) Lookup

func (c *Corpus) Lookup(key string) (Entry, bool)

Lookup returns the entry for key.

func (*Corpus) Problems

func (c *Corpus) Problems() []Problem

Problems lists every inconsistency between entries and folders, by key only.

func (*Corpus) Projection

func (c *Corpus) Projection() ([]banlist.KeyedPattern, error)

Projection is every confidential source's patterns, in bibliography order: the title and aliases always, the authors only where the entry opts in with ban_authors. A source is confidential by its FOLDER; a corpus where folder and entry disagree never reaches here (every caller requires consistency first).

type Custom

type Custom struct {
	Confidential     bool     `json:"confidential"`
	PermissionStatus string   `json:"permission_status"`
	Keywords         []string `json:"keywords,omitempty"`
	Aliases          []string `json:"aliases,omitempty"`
	BanAuthors       bool     `json:"ban_authors,omitempty"`
	File             string   `json:"file,omitempty"`
}

Custom is the entry's abcd block.

type Date

type Date struct {
	DateParts [][]int `json:"date-parts"`
}

Date is a CSL date.

type Entry

type Entry struct {
	ID             string `json:"id"`
	Type           string `json:"type"`
	Title          string `json:"title"`
	Author         []Name `json:"author,omitempty"`
	Issued         *Date  `json:"issued,omitempty"`
	ContainerTitle string `json:"container-title,omitempty"`
	URL            string `json:"URL,omitempty"`
	Custom         Custom `json:"custom"`
}

Entry is one CSL-JSON item as this package reads it. Fields it does not name are kept byte-for-byte when the bibliography is rewritten.

type Finding

type Finding struct {
	Source string `json:"source"`
	Field  string `json:"field"`
	// Line is the 1-based line the match is on.
	Line int `json:"line"`
	// Offset is banlist.Hit's: the 0-based byte offset from the start of the WHOLE
	// scanned text (never a column in Line) to the start of the matched span, which
	// can be the one boundary byte before the phrase.
	Offset int `json:"offset"`
}

Finding is one place a scanned text names a confidential source: the source's key, which of its strings matched (title, alias-N, author-N), and where. It carries no matched text, so a report is safe to relay.

type LedgerCount

type LedgerCount struct {
	Repo  string `json:"repo"`
	Lines int    `json:"lines"`
}

LedgerCount is one ledger file's size.

type Name

type Name struct {
	Family  string `json:"family,omitempty"`
	Given   string `json:"given,omitempty"`
	Literal string `json:"literal,omitempty"`
}

Name is a CSL name: family and given, or one literal.

type Problem

type Problem struct {
	Key    string `json:"key"`
	Reason string `json:"reason"`
}

Problem is one inconsistency, named by key only.

type Record

type Record struct {
	TS            string   `json:"ts"`
	Repo          string   `json:"repo"`
	DecisionRef   string   `json:"decision_ref"`
	Claim         string   `json:"claim"`
	SourceKey     string   `json:"source_key"`
	Locator       string   `json:"locator"`
	Influence     string   `json:"influence"`
	UsedIn        []string `json:"used_in,omitempty"`
	CitedPublicly bool     `json:"cited_publicly"`
	Corrects      int      `json:"corrects,omitempty"`
	Flips         int      `json:"flips,omitempty"`
}

Record is one ledger line. The first eight fields are the spec's; used_in traces the influence to the consuming documents; corrects and flips name, by 1-based line number, the earlier line a correction or a citation flip refers to. A line is never edited: a correction and a flip are both new lines.

type StatusReport

type StatusReport struct {
	Present      bool          `json:"present"`
	Dir          string        `json:"dir"`
	Confidential int           `json:"confidential"`
	Public       int           `json:"public"`
	Problems     []Problem     `json:"problems"`
	Ledgers      []LedgerCount `json:"ledgers"`
	Remotes      int           `json:"remotes"`
}

StatusReport is the corpus as the bare verb shows it. It holds counts and keys' numbers only — no title, alias or author.

func Init

func Init(dir string) (StatusReport, error)

Init creates a corpus at dir: the directory (0700), a no-remote git repository, an empty bibliography, and the manual, in one commit. An existing corpus is refused, as is a location inside another repository's working tree, where one `git add -A` would carry documents into it (adr-41).

func Status

func Status(dir string) (StatusReport, error)

Status reports the corpus at dir. An absent corpus is Present false and no error.

type SyncOptions

type SyncOptions struct {
	// Refresh is the pre-commit guard's mode: update a private store that already
	// exists and declares the keyed format, and never create one
	// (banlist.RefreshGeneratedBlock). Without it the sync is the person's by-hand
	// act, which creates the store when there is something to ban.
	Refresh bool
}

SyncOptions says how a banlist sync may treat the private store.

type SyncResult

type SyncResult struct {
	// Sources counts the confidential sources projected.
	Sources int `json:"sources"`
	// Block is the private store's generated-block outcome.
	Block banlist.GeneratedResult `json:"block"`
}

SyncResult is what a banlist sync did.

func SyncBanlist

func SyncBanlist(corpus, repoRoot string, opts SyncOptions) (SyncResult, error)

SyncBanlist projects the corpus's confidential entries into repoRoot's untracked private banlist (the itd-74 private layer), as the generated block the corpus owns. Hand-written entries outside the block survive; a declassified source's strings leave it on the next sync. A corpus whose classes disagree is refused before anything is written, so the block already there keeps banning.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL