review

package
v0.0.11 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package review owns a topic's review queue, its labels (human verdicts, including imported ones) and calibration with a holdout (spec §12).

Index

Constants

View Source
const (
	// MinDevLabels is the dev floor per purpose.
	MinDevLabels = 30
	// MinHoldoutLabels is the holdout floor per purpose.
	MinHoldoutLabels = 10
	// TargetPrecision is the dev precision the sweep looks for.
	TargetPrecision = 0.90
)

Calibration floors and targets (spec §12.3).

View Source
const (
	MatchURL   = "url"
	MatchPath  = "path"
	MatchDOI   = "doi"
	MatchPMCID = "pmcid"
)

Match keys accepted by ImportLabels.

View Source
const (
	PurposeRelevance   = string(decisions.PurposeRelevance)
	QuestionRole       = "role"
	PurposeLink        = string(decisions.PurposeLink)
	QuestionShouldLink = "should_link"
	PurposeQuality     = string(decisions.PurposeQuality)
)

Label identities (purpose and question ids) used by imports and calibration.

View Source
const (
	QueueGate            = "gate"
	QueueSkip            = "skip"
	QueueLink            = "link"
	QueueContradiction   = "contradiction"
	QueueConceptProposal = "concept-proposal"
	QueueOKFType         = "okf-type"
	QueueRecapture       = "recapture"
	QueueRemove          = "remove"
)

Queue names (spec §12.1).

View Source
const (
	StatusPending  = "pending"
	StatusAccepted = "accepted"
	StatusRejected = "rejected"
)

Item statuses.

View Source
const (
	QueueFile  = "review.jsonl"
	LabelsFile = "labels.jsonl"
)

Files under <topic>/.decisions/.

View Source
const (
	VerdictPositive   = "positive"
	VerdictNegative   = "negative"
	OriginReview      = "review"
	OriginImportLinks = "import-links"
)

Label verdicts and origins.

View Source
const CalibrationFile = "calibration.json"

CalibrationFile is <topic>/.decisions/calibration.json. The JSON shape is the one internal/session reads (session.Calibration); it is mirrored here because session imports review.

View Source
const InsertedLinksFile = "inserted-links.jsonl"

InsertedLinksFile is the log of body links inserted by `kb link`, inside <topic>/.decisions/. Rows: {time, subject, target, text, mode}.

View Source
const OriginImportPrefix = "import:"

OriginImportPrefix prefixes the origin of labels imported from a file.

View Source
const PreviewsFile = "previews.jsonl"

PreviewsFile is the impact-preview ledger inside <topic>/.decisions/.

Variables

View Source
var DefaultKeepValues = []string{"include", "true"}

DefaultKeepValues are the decision values read as "keep" when ImportOptions.KeepValues is empty.

View Source
var ErrNotEnoughLabels = errors.New("review: not enough labels; nothing written")

ErrNotEnoughLabels is returned by WriteCalibration when no purpose has a recommendation.

Queues lists every queue in display order.

Functions

func AppendPreview

func AppendPreview(topicRoot string, p Preview) error

AppendPreview appends p to previews.jsonl; an empty Time is set to now.

func DevBucket

func DevBucket(subject string) bool

DevBucket reports whether subject falls in the dev split: the first byte of sha256(subject) mod 10 is 0–6.

func ImportLinks(vaultPath, topicRoot string, c *corpus.Corpus) (int, error)

ImportLinks turns the existing links between topic documents into positive `link` labels (question `should_link`, subject = linking document, target = linked document, origin `import-links`), which gives calibration a gold set (spec §12.2). Counted: body wikilinks and markdown links plus the `sources` and relation frontmatter lists. Excluded: links kb inserted (inserted-links.jsonl rows not in shadow mode) and relation lists kb wrote that nobody edited since (state row `written` hash equals the current value hash). Links to quarantined or non-corpus files and self links are ignored. A subject/target pair that already has a link label is skipped, so re-running writes nothing new. Returns the number of labels written.

func ItemID

func ItemID(queue, subject, target, question string) string

ItemID derives the stable id of an item from its identity fields.

func NormalizeURL

func NormalizeURL(raw string) string

NormalizeURL reduces a URL to a comparison key: scheme, `www.`, default ports, fragment, trailing slash and utm_* parameters are dropped, the host is lower-cased and the remaining query parameters are sorted.

func RenderCalibration

func RenderCalibration(w io.Writer, report Report) error

RenderCalibration writes a calibration report: one block per purpose with label counts, metrics at the current and chosen thresholds on dev and holdout, imported-label metrics and the dev sweep.

func RenderList

func RenderList(w io.Writer, view ListView) error

RenderList writes the queue counts and the pending items as tables.

func WriteCalibration

func WriteCalibration(topicRoot string, report Report) error

WriteCalibration stores every recommended threshold in topic.yaml `decisions.thresholds` (other thresholds are kept) and records the calibration of those purposes in <topic>/.decisions/calibration.json (entries of other purposes are kept). Without any recommendation nothing is written and ErrNotEnoughLabels is returned.

Types

type CalibrateOptions

type CalibrateOptions struct {
	// ContractHash is the active contract hash. Only receipts judged under
	// it are joined, and labels shown in a preview made under another
	// contract are forced to dev.
	ContractHash string
	// Model is the active decision model; only receipts it produced are
	// joined.
	Model string
	// Thresholds are the current thresholds; nil means the defaults merged
	// with topic.yaml `decisions.thresholds`.
	Thresholds decisions.Thresholds
	// Now defaults to time.Now.
	Now func() time.Time
}

CalibrateOptions configures Calibrate.

type ImportOptions

type ImportOptions struct {
	// From is the JSONL, JSON (array of objects) or CSV file to import.
	From string
	// IDField is the column holding the source identifier.
	IDField string
	// Match is how IDField is matched to a topic source: url|path|doi|pmcid.
	Match string
	// DecisionField is the column holding the screening decision.
	DecisionField string
	// KeepValues are the decision values meaning keep/relevant
	// (case-insensitive); every other value is a negative label.
	KeepValues []string
	// DecidedByField names the column copied to Label.DecidedBy; when empty
	// `screening_stage` or `decided_by` are used when present.
	DecidedByField string
	// Corpus is the loaded topic; loaded from the topic root when nil.
	Corpus *corpus.Corpus
	// Now defaults to time.Now.
	Now func() time.Time
}

ImportOptions configures ImportLabels.

type ImportReport

type ImportReport struct {
	// Rows is the number of data rows read.
	Rows int
	// Matched counts rows matched to a topic source.
	Matched int
	// Unmatched counts rows with no matching source.
	Unmatched int
	// UnmatchedIDs lists the identifiers of unmatched rows in file order.
	UnmatchedIDs []string
	// Positive and Negative count the labels written by this import.
	Positive int
	Negative int
	// Duplicates counts matched rows already imported from the same file.
	Duplicates int
	// Origin is the origin recorded on every label.
	Origin string
}

ImportReport summarizes an import.

func ImportLabels

func ImportLabels(topicRoot string, opts ImportOptions) (ImportReport, error)

ImportLabels imports relevance labels from a topic's own screening or curation file (spec §12.2). Each row is matched to a source by the chosen key; a decision in KeepValues is a positive (keep) label, anything else a negative one, with purpose `relevance`, question `role` and origin `import:<file base name>@<sha256 of the file>`. Re-importing the same file writes nothing new; when one subject appears on several rows the last row wins.

type ImportedPart

type ImportedPart struct {
	Dev     Metrics `json:"dev"`
	Holdout Metrics `json:"holdout"`
}

ImportedPart scores imported labels alone, at the chosen threshold (or the current one when there is no recommendation).

type Item

type Item struct {
	ID          string         `json:"id"`
	Queue       string         `json:"queue"`
	Purpose     string         `json:"purpose"`
	Subject     string         `json:"subject"`
	Target      string         `json:"target,omitempty"`
	Question    string         `json:"question,omitempty"`
	Probability float64        `json:"probability"`
	ReceiptKey  string         `json:"receipt_key,omitempty"`
	Evidence    string         `json:"evidence,omitempty"`
	Action      map[string]any `json:"action,omitempty"`
	Status      string         `json:"status"`
	Created     string         `json:"created"`
	Resolved    string         `json:"resolved,omitempty"`
}

Item is one review-queue entry. Status changes are appended as new rows with the same ID; readers keep the last row per ID.

type Label

type Label struct {
	Time       string `json:"time"`
	Subject    string `json:"subject"`
	Purpose    string `json:"purpose"`
	Question   string `json:"question,omitempty"`
	Target     string `json:"target,omitempty"`
	Verdict    string `json:"verdict"` // positive | negative
	ReceiptKey string `json:"receipt_key,omitempty"`
	Origin     string `json:"origin"` // review | import:<file>@<sha256> | import-links
	DecidedBy  string `json:"decided_by,omitempty"`
	ItemID     string `json:"item_id,omitempty"`
}

Label is a human verdict on a decision (spec §12.2).

func LoadLabels

func LoadLabels(topicRoot string) ([]Label, error)

LoadLabels reads <topic>/.decisions/labels.jsonl (missing → empty).

type ListItem

type ListItem struct {
	Item
	Title string `json:"title,omitempty"`
}

ListItem is one pending item with its subject title resolved.

type ListView

type ListView struct {
	// Counts are the pending items per queue.
	Counts map[string]int `json:"counts"`
	// Items are the pending items shown.
	Items []ListItem `json:"items"`
}

ListView is the data behind `kb review list`.

func BuildListView

func BuildListView(store *Store, queue string, title func(subject string) string) (ListView, error)

BuildListView collects the pending items of one queue ("" = all) with their subject titles (title returns "" when unknown).

type Metrics

type Metrics struct {
	// Total is the number of labels in the split.
	Total int `json:"total"`
	// N is the number of labels joined to a judged gate quantity.
	N int `json:"n"`
	// Predicted counts joined labels at or above the apply threshold.
	Predicted int `json:"predicted"`
	// TruePositive counts predicted labels whose verdict is the target.
	TruePositive int `json:"true_positive"`
	// Precision = TruePositive / Predicted.
	Precision float64 `json:"precision"`
	// Recall = TruePositive / joined labels whose verdict is the target.
	Recall float64 `json:"recall"`
	// Coverage = N / Total: the share of labels the model judged.
	Coverage float64 `json:"coverage"`
	// ReviewRate = joined labels in [review, apply) / N.
	ReviewRate float64 `json:"review_rate"`
}

Metrics are the scores of one split at one threshold. Labels without a judged gate quantity count in Total only.

type Preview

type Preview struct {
	Time     string   `json:"time"`
	Contract string   `json:"contract"`
	Subjects []string `json:"subjects"`
}

Preview records the documents a contract impact preview showed (or used to estimate precision) under one contract hash. Calibration demotes the labels of those subjects to dev once the contract changes (spec §12.3).

func LoadPreviews

func LoadPreviews(topicRoot string) ([]Preview, error)

LoadPreviews reads previews.jsonl (missing → empty; malformed lines are skipped).

type PurposeReport

type PurposeReport struct {
	Purpose  string `json:"purpose"`
	Quantity string `json:"quantity"`
	// Banks maps the built-in bank asking the gate quantity to the version
	// scores were joined under (with Report.Contract and Report.Model, the
	// decision context of this calibration).
	Banks map[string]string `json:"banks"`
	// ApplyName and ReviewName are the threshold names calibrated.
	ApplyName  string  `json:"apply_name"`
	ReviewName string  `json:"review_name"`
	Current    float64 `json:"current"`
	Review     float64 `json:"review"`
	// DevLabels and HoldoutLabels count joined labels per split (imported
	// ones included); ImportedLabels counts the imported ones among them.
	DevLabels      int `json:"dev_labels"`
	HoldoutLabels  int `json:"holdout_labels"`
	ImportedLabels int `json:"imported_labels"`
	// ReviewDevLabels counts the joined dev labels given in `kb review`
	// (not imported); the dev floor must be met by these alone (spec §12.2:
	// imported labels are never the only evidence behind an apply).
	ReviewDevLabels int `json:"review_dev_labels"`
	// ImportedOnly flags a purpose whose dev floor is met only with imported
	// labels: metrics are reported, no threshold is recommended.
	ImportedOnly bool `json:"imported_only,omitempty"`
	// Demoted counts labels forced to dev by a preview under another
	// contract.
	Demoted int `json:"demoted"`
	// Unjoined counts labels with no judged gate quantity.
	Unjoined int `json:"unjoined"`
	// Recommended reports whether Chosen is a recommendation.
	Recommended bool    `json:"recommended"`
	Chosen      float64 `json:"chosen,omitempty"`
	// Note explains a missing recommendation ("not enough labels", ...).
	Note           string       `json:"note,omitempty"`
	DevCurrent     Metrics      `json:"dev_current"`
	HoldoutCurrent Metrics      `json:"holdout_current"`
	DevChosen      Metrics      `json:"dev_chosen"`
	HoldoutChosen  Metrics      `json:"holdout_chosen"`
	Imported       ImportedPart `json:"imported"`
	Sweep          []SweepPoint `json:"sweep"`
}

PurposeReport is the calibration of one purpose.

type Report

type Report struct {
	Time     string          `json:"time"`
	Contract string          `json:"contract"`
	Model    string          `json:"model"`
	Purposes []PurposeReport `json:"purposes"`
}

Report is a calibration report over every calibrated purpose.

func Calibrate

func Calibrate(topicRoot string, opts CalibrateOptions) (Report, error)

Calibrate measures, per purpose, precision, recall, coverage and review rate at the current thresholds and over a sweep, from labels only (spec §12.3). Each label is joined to a decided receipt of the active decision context only: judged under opts.ContractHash, by opts.Model, with the current version of the purpose's built-in bank. A receipt of another context scores different questions and is never joined; such labels count as unjoined until current scores exist. Relevance and quality labels use Label.ReceiptKey when that receipt is compatible and carries the needed answer, else the latest compatible receipt for the same subject. Link labels join only through their producing evidence: the `should_link_<id>` question id and receipt key of the label or of its review item (candidate ids are positional, so another receipt of the subject may ask the same id about a different target). Labels are split by the first byte of sha256(subject) mod 10: buckets 0–6 are dev, 7–9 holdout; subjects that appeared in a preview under another contract are forced to dev. Thresholds 0.50..0.95 (step 0.05) are swept on dev only: the lowest threshold with dev precision ≥ 0.90 wins, else the best F0.5. Below 30 dev or 10 holdout joined labels a purpose gets "not enough labels" and no recommendation; when the dev floor is met only with imported labels (fewer than 30 dev labels given in `kb review`) the purpose is flagged "imported labels only" and gets no recommendation either (spec §12.2, §20).

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is a topic's review queue and label log. Safe for concurrent use.

func Open

func Open(topicRoot string, now func() time.Time) *Store

Open returns the review store of a topic (files are read lazily).

func (*Store) Add

func (s *Store) Add(item Item) (bool, error)

Add enqueues an item as pending. An item whose ID already exists (pending or resolved) is left alone, so a verdict is never re-asked; returns whether a row was written.

func (*Store) AddLabel

func (s *Store) AddLabel(label Label) error

AddLabel appends a label to labels.jsonl.

func (*Store) Dir

func (s *Store) Dir() string

Dir returns <topic>/.decisions.

func (*Store) Get

func (s *Store) Get(id string) (Item, bool, error)

Get returns an item by id.

func (*Store) Items

func (s *Store) Items(queue, status string) ([]Item, error)

Items returns every item (latest row per id) in insertion order, filtered by queue ("" = all) and status ("" = all).

func (*Store) Labels

func (s *Store) Labels() ([]Label, error)

Labels reads every label of the topic.

func (*Store) Pending

func (s *Store) Pending(queue string) ([]Item, error)

Pending returns pending items of one queue ("" = all), highest probability first.

func (*Store) PendingCounts

func (s *Store) PendingCounts() (map[string]int, error)

PendingCounts counts pending items per queue.

func (*Store) Resolve

func (s *Store) Resolve(id, status string) (Item, error)

Resolve marks an item accepted or rejected.

type SweepPoint

type SweepPoint struct {
	Threshold float64 `json:"threshold"`
	Dev       Metrics `json:"dev"`
}

SweepPoint is the dev score at one candidate threshold.

Jump to

Keyboard shortcuts

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