Documentation
¶
Overview ¶
Package capture is abcd's transport-agnostic issue-ledger engine: the write side of a per-repo issue ledger that replaces the free-form .work/issues.md. Every capability is a function taking a structured request and returning a structured result; nothing here writes to stdout or knows about a CLI, MCP, or prompt surface. The front doors under internal/surface/* marshal these results for their transport.
The ledger lives at <repoRoot>/.abcd/work/issues with three status directories (open/, resolved/, wontfix/) whose folder membership IS the status signal — there is no status: frontmatter field. Each issue is a YAML-frontmatter + Markdown-body file named iss-<N>-<slug>.md with an unpadded, per-repo id namespace.
This package ports scripts/abcd/_issue_lib.py + issue_workflow.py to Go.
Index ¶
- Constants
- Variables
- func CheckReadingCitations(req IngestReadingRequest) error
- func ComparativeRunFor(repoRoot, run string) (string, error)
- func ConstrualFingerprint(doc string) (string, error)
- func GlossaryFingerprint(files map[string][]byte) string
- func IngestConsistency(repoRoot string, payload []byte, date string, mc *match.Config) (intent.ConsistencyIngestResult, error)
- func ItemFate(repoRoot, run, item string) (issueschema.ItemFate, error)
- func LedgerRoot(cwd string) (string, error)
- func MatchCandidates(repoRoot string, cfg match.Config) ([]match.Candidate, error)
- func PromotedInto(repoRoot string, iss Issue) (string, error)
- func ReadRefusal(content, status, path string) error
- func RefuseRedirectedLedger(repoRoot string) error
- func ScopeFingerprint(doc string) string
- func ValidateReadingRecord(content string) (map[string]any, error)
- func WithLedgerLock(repoRoot string, fn func() error) error
- type AdmitRequest
- type AdmitResult
- type CaptureRequest
- type CaptureResult
- type Category
- type DeferRequest
- type DeferResult
- type DispositionRequest
- type DispositionResult
- type DrainOutcome
- type DrainPlan
- type DrainPlanRequest
- type DrainRule
- type DrainVerdict
- type DriftFinding
- type FieldValueError
- type Frame
- type FrameSurface
- type IngestReadingRequest
- type IngestReadingResult
- type Issue
- type IssueDriftRequest
- type IssueDriftResult
- type LinkRequest
- type LinkResult
- type ListRequest
- type ListResult
- type MentionEvidence
- type MentionRow
- type MentionsRequest
- type MentionsResult
- type MigrateChange
- type MigrateRequest
- type MigrateResult
- type PromoteRequest
- type PromoteResult
- type ReadingItem
- type ReadingMatch
- type ReadingRecordRef
- type ReframeRequest
- type ReframeResult
- type RemedyRequest
- type RemedyResult
- type ResolveRequest
- type ResolvedBy
- type Severity
- type SkipLayer
- type SkipRecord
- type Source
- type State
- type StatusRequest
- type StatusResult
- type SurpriseRequest
- type SurpriseResult
- type TransitionResult
- type WontfixRequest
Constants ¶
const ( SkipLayerName = issuerecord.LayerName SkipLayerRead = issuerecord.LayerRead SkipLayerFrontmatter = issuerecord.LayerFrontmatter SkipLayerSchema = issuerecord.LayerSchema SkipLayerInvariant = issuerecord.LayerInvariant )
The reader's stages, in the order a file meets them; see core/issuerecord.
const ( // DriftOneSided: an intent names a ledger record in related_issues that does // not name the intent back in related_intents — or a reading item names an // intent that does not name it back. An ISSUE naming an intent that does not // name it back is not drift: an issue may be captured already related to an // intent it was never promoted into, and the reading item carries no such // loose relation, which is why the two ends are judged differently. DriftOneSided = "one_sided" // DriftDangling: either end names a record this tree does not hold. DriftDangling = "dangling" // DriftShippedUnresolved: a shipped intent names an issue that is not in // resolved/ — the intent delivered, and the observation it graduated from // still reads as unaddressed (or as declined, in wontfix/). DriftShippedUnresolved = "shipped_unresolved" // DriftRetiredField: a record still carries a retired back-link key, which no // reader reads; `abcd capture migrate --apply` rewrites it. DriftRetiredField = "retired_field" )
The drift kinds IssueDrift reports. Each names one way the promote join (itd-4 AC3) can stop reading the same from both ends.
const ( // StrengthRecord — the id was named by a commit that touched only the record // tiers (.abcd/**). Somebody wrote ABOUT the record. That is the weakest // signal there is, and it is listed last. StrengthRecord = "record" // StrengthTree — the id was named by a commit that changed something outside // the record tiers. Somebody did work and said which record it was about. StrengthTree = "tree" // StrengthResolves — a commit DECLARED `Resolves: iss-N` and the record is // still in open/. Nothing about this one is ambiguous: somebody said the // change fixed it and the ledger never moved. StrengthResolves = "resolves" )
The strength vocabulary, weakest to strongest. A row carries the strongest evidence found for its id; every piece of evidence is still listed under it.
const ( ReframeHalfWhole = "whole" ReframeHalfOpen = "open" ReframeHalfCompleted = "completed" )
The three halves a reframe write can be.
const BlockedByFlagHelp = "comma-separated iss-N ids this issue is blocked by; each must exist in the ledger — " + blockedByDocs
BlockedByFlagHelp is the --blocked-by flag's help on both verbs that take it, exported so the front doors render one sentence rather than two copies.
const DuplicatesFlagHelp = "" /* 158-byte string literal not displayed */
DuplicatesFlagHelp is the --duplicates flag's help, exported so the front door renders the sentence the core documents.
const LedgerName = "the issue ledger"
LedgerName is the noun the ledger's root refusals are phrased with. It is the ledger's half of gitutil.CheckoutRoot's contract — the resolution and the refusal policy are shared, and only the noun is this store's.
const LedgerRelPath = recordid.IssuesRelDir
LedgerRelPath is the ledger root relative to the repo worktree.
const PositionComparative = "comparative"
PositionComparative is the comparative position's token, as the run record spells it. It is stated here rather than imported because core/reading imports this package and not the other way round; issueschema.ReadingPositions is the vocabulary both are held to.
Variables ¶
var ( // ErrUnknownIssueID means the id was absent from all three dirs. ErrUnknownIssueID = errors.New("unknown issue id") // ErrTransitionConflict means the id was found but not in open/ (already // resolved/wontfixed), or a concurrent move consumed it. ErrTransitionConflict = errors.New("transition conflict") // ErrDuplicateIssueID means a ForceID (or on-disk state) collided. ErrDuplicateIssueID = errors.New("duplicate issue id") // ErrAllocatorContention means the lock timed out or the O_EXCL retry // budget was exhausted. ErrAllocatorContention = errors.New("allocator contention") // ErrChecksumMismatch means a concurrent edit occurred during a transition. ErrChecksumMismatch = errors.New("checksum mismatch") // ErrAlreadyPromoted means the issue already names, and is named by, an // intent other than the one this call is joining — refused rather than // promoted twice. ErrAlreadyPromoted = errors.New("already promoted") // ErrGroundsRefused means the triage's grounds argument was absent, outside // the closed vocabulary, malformed, or below the substance floor. It is one // sentinel for every one of those because they are one thing to a caller — // the argument was not usable and nothing was written — and a surface that // distinguished them by exit code would teach a script that a misspelled // token is a different KIND of failure from a missing one // (iss-2608300930057882). ErrGroundsRefused = errors.New("grounds refused") // ErrRequestRefused means a member of a transition request is outside its // shape or names nothing: an impact outside its set, a provenance reference // that is malformed or absent, a release tag or production mode that is not // one. Like ErrGroundsRefused it is the caller's input to fix with nothing // written, and the surface gives both the one exit code a refusal takes // (iss-2609260552251398). ErrRequestRefused = errors.New("request refused") // ErrRemedyRequired means a new issue was filed without a remedy: every new // issue carries the fix it proposes (ruling BX3 of 2026-09-29, itd-82 // decision 6), and an automatic filer with none writes // issueschema.MachineRemedy. It is a request refusal, with nothing written. ErrRemedyRequired = errors.New("remedy required") // The record reader's own sentinels (core/issuerecord), re-exported: one // value under either name, so errors.Is holds across the two packages. // // ErrInvariantViolation means frontmatter passed the schema but violates a // folder-status cross-field invariant. ErrInvariantViolation = issuerecord.ErrInvariantViolation // ErrMalformedFrontmatter means frontmatter could not be parsed or failed // schema validation. ErrMalformedFrontmatter = issuerecord.ErrMalformedFrontmatter // ErrMissingRequiredField means a schema-required field was absent. ErrMissingRequiredField = issuerecord.ErrMissingRequiredField // ErrPathUnsafe means the ledger root or a status dir is a symlink. ErrPathUnsafe = issuerecord.ErrPathUnsafe // ErrNotCharacterised means a disposition or an admission was asked for at // the widening position before a committed comparative run named the item's // run. The design characterises first and admits second, and the refusal // names the run it is waiting on (spc-2609020626040342). ErrNotCharacterised = errors.New("not yet characterised") )
Sentinel errors the surface maps to exit codes and messages. Core never prints them.
var ErrDrainRuleUnrecorded = drainrule.ErrUnrecorded
ErrDrainRuleUnrecorded is the refusal when the drained repository holds no record of its eligibility rule (itd-82 decision 4, criterion 11; ruling BX2).
var ErrUnresolvedCitation = errors.New("capture: unresolved record citation")
ErrUnresolvedCitation is the refusal of host prose citing a record id that names no record.
var FrameSurfaces = []FrameSurface{
{Name: "construal", Path: ".abcd/development/brief/01-product/06-framing.md", Heading: "Construal"},
{Name: "glossary", Path: ".abcd/development/brief/glossary"},
{Name: "scope", Path: ".abcd/development/brief/01-product/04-scope.md"},
}
FrameSurfaces is the frame, as ONE table (the intent's first scope condition): the paths and the heading are constants, and a repository whose frame lives elsewhere is outside this verb's scope. The order is the record's, and the names are issueschema.FrameSurfaceNames.
Functions ¶
func CheckReadingCitations ¶ added in v0.11.1
func CheckReadingCitations(req IngestReadingRequest) error
CheckReadingCitations holds every free-text value of a reading's items — the pattern and each body field, the host's own words — to the record gate the item's record must pass, and refuses naming the item, the field and the ids. Each value is asked as the frontmatter line its record carries it on (the writer renders every value on one line), so what is judged is what the gate will read. IngestReading asks it before it writes anything; the reading ingest asks it too, before its orphan sweep and its stage, so a refusal there leaves the tree exactly as it found it.
func ComparativeRunFor ¶ added in v0.8.0
ComparativeRunFor returns the lowest committed comparative run id whose `candidate_run` names run, or the empty string when none does.
It reads the RUN RECORDS under the durable readings family, which is where a run's commit marker lives: a directory holding a parked manifest and no run record is a run that never happened, and it answers nothing here.
A committed run is the channel's PAIR, not a run record alone (iss-2609251842111593). The ingest promotes the run's manifest into its durable directory before it writes the run record, and both carry the run id and the candidate join, so a run record that names this widening run is held to them: its run_id names its own directory, and the manifest beside it names the same run, the comparative position and the same candidate_run. A record that names the widening run and fails either is refused by name, as a record contradicting itself, never read as "no run yet" — the id-less, manifest-less two-key marker a session could write by hand is exactly that record. Whether the pair is tracked by git is not asked: the gate answers between an ingest and the commit that carries it, which is the order the channel is used in.
The LOWEST match rather than any match, so two comparative runs over one widening run — a legitimate state, since a second comparative run before any disposition is a second run — resolve to one answer that does not depend on the order the filesystem returned.
func ConstrualFingerprint ¶ added in v0.11.1
ConstrualFingerprint fingerprints the framing chapter's construal section: the body under the one H2 titled `Construal`, down to the next heading of level two or shallower or the end of the file, line endings normalised and blank edges trimmed. The heading itself is a constant and is not hashed. A not-yet-real marker the section opens with is part of the section: a change to it is a change to what the construal states about itself.
A chapter with no such section, or with more than one, is refused by name.
func GlossaryFingerprint ¶ added in v0.11.1
GlossaryFingerprint fingerprints the committed glossary terms: every `.md` file handed to it except each README.md (the index is a render of the terms, held by core/glossary) and the _template.md scaffold, sorted by path, as the SHA-256 over path, NUL, normalised content, NUL for each. A term added, removed, renamed or edited moves it; an index regeneration does not. Paths are repo-relative and slash-separated, so every reader keys them alike.
func IngestConsistency ¶ added in v0.11.1
func IngestConsistency(repoRoot string, payload []byte, date string, mc *match.Config) (intent.ConsistencyIngestResult, error)
IngestConsistency ingests a consistency findings payload with the ledger as its filer. date is the report's date (YYYY-MM-DD; empty is today in UTC). mc, when non-nil, runs capture's filing-time match on every record the pass files, so a finding that doubles a record in other words is filed with a typed link naming it; nil files unmatched.
func ItemFate ¶ added in v0.8.0
func ItemFate(repoRoot, run, item string) (issueschema.ItemFate, error)
ItemFate reports what the ledger says has happened to one reading item of one run: the dispositions standing over it, and the admissions naming it as their proposal.
The admission side is keyed on the (run, proposal) PAIR, exactly as core/lint's admittedProposals keys it, and for the reason that function records: keying on the proposal alone made a proposal id a global silencer (iss-2608300935215868). An admission whose own `run` field disagrees with the directory it sits in counts under neither — the record contradicts itself about which candidate set it joined, and honouring either half would let the other lie.
An absent store is a STATE, not a fault: a repository that has dispositioned or admitted nothing has an item with no fate, which is exactly what a comparative assembly needs to find.
func LedgerRoot ¶ added in v0.8.0
LedgerRoot answers the question every front door has to ask before it touches the ledger: which checkout's ledger does a caller standing in cwd address?
It is the ledger's door onto gitutil.CheckoutRoot and holds no resolution of its own. The question is not ledger-specific — the decision store asked it next, and reached the same failure one directory further out (iss-2609091707224329) — so the three-state answer, git's toplevel or one of two refusals, is resolved once there and named here. What is ledger-specific is the noun the refusals carry, which is all this function supplies.
It exists because the CLI used to skip asking at all: every capture verb handed its working directory to resolveRoots as an explicit repo root, so the discovery below was never reached and a verb run from a subdirectory addressed a ledger that was not there — a read reported open 0 against a populated checkout, and a write minted a second ledger under the subdirectory and reported success with a repo-relative path that looked ordinary (iss-2609090951291524).
discoverRepoRoot, the core's own discovery for a request that names no root, asks the same question through the same function, so the two cannot come to disagree about which directory is a checkout (iss-2609090947359464).
func MatchCandidates ¶ added in v0.11.1
MatchCandidates is the candidate set for a filing in the checkout at repoRoot, for a caller outside this package: the quoted-text intent create.
func PromotedInto ¶ added in v0.10.0
PromotedInto names the intent iss was promoted into, or "" — the read-side form of the question promote asks before it writes, for a surface that renders a record's next move.
func ReadRefusal ¶ added in v0.11.1
ReadRefusal reports the error the ledger's read path would skip a committed record with, or nil when it reads the record: the same parse, the same strict schema validation and the same folder/filename invariants List runs, in that order. status is the status directory the record sits in and path its file path (only the basename is read).
It exists so record-lint's record_schema gate refuses exactly the records this reader refuses, by asking the reader rather than re-deriving its grammar: a record the reader skips is invisible to every capture surface while it still sits in the ledger, and each private copy of the grammar the gate carried drifted from this one somewhere (iss-2608300205044566, iss-2608300244483405). The judgement itself is core/issuerecord's, the one the ledger reader runs.
func RefuseRedirectedLedger ¶ added in v0.11.1
RefuseRedirectedLedger judges, creating nothing, every directory the default ledger of repoRoot is reached through — `.abcd`, `.abcd/work`, the issues root and its status directories — and refuses any that is a symlink or not a directory. It is ledgerDirs' read form, the one resolveRoots applies to every verb, exported for a reader outside this package that walks the ledger itself (the scribe's context assembler), so that reader judges the ancestors by this rule rather than by a copy of it. An absent directory is not a fault.
func ScopeFingerprint ¶ added in v0.11.1
ScopeFingerprint fingerprints the committed scope chapter whole, after its frontmatter is stripped and its line endings normalised.
func ValidateReadingRecord ¶ added in v0.8.0
ValidateReadingRecord parses one committed reading record and validates it against the family's schema, returning its frontmatter.
It is the WRITER's own check, offered to a reader. The cold-reading assembler admits one widening run's records as a comparative reading's candidate set (adr-2609021016272867), and it has to know that what it is about to hand over is a reading record at the widening position rather than whatever a file of that name happens to hold. Exporting this check rather than writing a second one is the rule this file's header already states for the two READERS of the disposition question: two readers are tolerable, two answers are not.
func WithLedgerLock ¶ added in v0.7.0
WithLedgerLock runs fn while holding this ledger's exclusive allocator lock, for a caller OUTSIDE this package that mutates the ledger's files directly.
The cold-reading ingest verb is the one such caller: its orphan sweep unlinks reading records whose run never committed, and without this that delete races this package's own readers. Exporting the lock rather than the path is what keeps one file the lock: a caller that rebuilt the path would be holding a different lock the day this one moves.
It is NOT reentrant — an flock blocks a second acquisition in the same process — so a caller must not hold it across any exported verb of this package, every one of which takes it internally.
Types ¶
type AdmitRequest ¶ added in v0.11.1
type AdmitRequest struct {
RepoRoot string
IssuesRoot string
// Item is the widening proposal admitted (rdi-N).
Item string
// Grounds is why it is admitted: free text with no `<token>:` prefix, held to
// the substance floor every grounds primitive applies.
Grounds string
}
AdmitRequest admits one widening item into its run's candidate set.
type AdmitResult ¶ added in v0.11.1
type AdmitResult struct {
// Admission is the admission record's id (adm-N) and Path its repo-relative
// path.
Admission string `json:"admission"`
Path string `json:"path"`
// Disposition is the item's `accepted` disposition — the one this act wrote,
// or the one already standing — and DispositionPath its repo-relative path.
Disposition string `json:"disposition"`
DispositionPath string `json:"disposition_path"`
// DispositionWritten reports whether this act wrote the disposition (true)
// or found an `accepted` one standing and wrote the admission alone (false).
DispositionWritten bool `json:"disposition_written"`
Item string `json:"item"`
Run string `json:"run"`
Grounds string `json:"grounds"`
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
}
AdmitResult is the outcome of a successful Admit.
func Admit ¶ added in v0.11.1
func Admit(req AdmitRequest) (AdmitResult, error)
Admit records one admission as one act. It refuses, writing nothing: an item that is not a widening item; an item already admitted; a standing disposition in any state but `accepted`, or an `accepted` one stating another ground; a contested or cyclic disposition set; a ground below the substance floor; and any admission before a committed comparative run names the item's run.
type CaptureRequest ¶
type CaptureRequest struct {
RepoRoot string
IssuesRoot string
Text string // markdown body
Severity Severity
Category Category
Source Source
Slug string // caller-supplied; normalised to kebab-case
FoundDuring string // required, non-empty
FoundAt string // optional; "" omits the field
// LapsedAt is the RFC 3339 instant the discipline gave way. There is no
// default and none may be invented: the wall clock at write-up is exactly the
// value the lapse log exists to distinguish itself from (spc-60).
LapsedAt string
// Remedy is the proposed fix, written as `remedy:` (itd-82 decision 6).
// Required: a blank one is refused with ErrRemedyRequired (ruling BX3 of
// 2026-09-29), and an automatic filer with no fix yet passes
// issueschema.MachineRemedy (ruling H12), which a drain skips. One line:
// the serialiser refuses a scalar carrying a line break.
Remedy string
RelatedIntents []string
RelatedSpecs []string
BlockedBy []string // iss-N dependency edges; each must match ^iss-[0-9]+$
ForceID string // migrator-only; "" = auto-allocate
// ProductionMode is how the issue's text was produced (itd-178): one of the
// closed provenance vocabulary, or empty for the vocabulary's default. There
// is no free-text form. The record's `origin` has no request member at all —
// it is derived from which command ran, and a capture is researcher-authored
// by construction.
ProductionMode string
// Match, when non-nil, matches the text against every open and resolved
// issue and every intent before the record is written, under the ledger
// lock, and writes a `duplicates:` or `refines:` link naming each likely
// double (itd-2609212137116617). It never refuses the capture: a match that
// cannot run says why on the result and the record is filed without it.
// nil files the record unmatched.
Match *match.Config
// MatchText, when non-empty, is the text the match compares in place of
// Text: the finding's own words, for a filer whose record also carries
// lines every record it files shares (the inbox's provenance, the
// consistency pass's evidence line). Matched on those, two unrelated
// records would link each other on the boilerplate alone. Empty compares
// Text.
MatchText string
// MatchExcept names records the match does not compare with: a filer
// that files several records in one pass passes the ones it has already
// filed, so two findings of one pass are never linked as doubles of each
// other.
MatchExcept []string
}
CaptureRequest is the input to Capture (append a new issue).
type CaptureResult ¶
type CaptureResult struct {
ID string `json:"id"`
Slug string `json:"slug"`
Path string `json:"path"`
Status State `json:"status"` // always "open"
// Redacted counts the spans the ledger redactor rewrote on write, and
// Degraded is non-empty when it ran with a weakened pattern set. Both exist
// so a surface can SAY the text was altered: redacting in silence would edit
// a finding's content without telling whoever filed it (loud-staging).
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
// Uncommitted is true when git reports the record just written as not yet
// committed — always, in a checkout git answers for, since the file is new.
// It exists so the write can SAY the record reaches no other branch and no
// gate until it is committed (iss-2609100508570527).
Uncommitted bool `json:"uncommitted,omitempty"`
// NoLocation is true when the capture named no found_at: legitimate for a
// conceptual finding, and still worth saying, because nothing then ties the
// record to the repository it is filed into — the shape every misfiled
// record of iss-2609120511058115 had (iss-2609231156260287).
NoLocation bool `json:"no_location,omitempty"`
// Match is the filing-time match's outcome when the request asked for one:
// the links written, the near misses below the threshold with their scores,
// or why nothing was compared.
Match *match.Outcome `json:"match,omitempty"`
}
CaptureResult is the outcome of a successful Capture. The timestamp-numeric mint (adr-45) consults no refs, so the max+1 era's mint_warning degrade note no longer exists on this result.
func Capture ¶
func Capture(req CaptureRequest) (CaptureResult, error)
Capture appends a new issue to open/ with an auto-assigned (or forced) iss-N. The write is transactional: a zero-byte placeholder is reserved, and on any failure it is swept. Returns the committed path under open/.
type DeferRequest ¶ added in v0.11.1
type DeferRequest struct {
RepoRoot string
IssuesRoot string
ID string
// After is the anchor tag, which must be the checkout's newest release tag:
// the cut reads the waiver by string equality against its own base, and a
// waiver naming any other tag is one the cut refuses.
After string
// Reason is why the finding is carried past this cut rather than fixed.
Reason string
}
DeferRequest carries a release-cut waiver for one open record (iss-2609181223260994): the anchor tag the finding is carried past and the reason it is.
type DeferResult ¶ added in v0.11.1
type DeferResult struct {
ID string `json:"id"`
Path string `json:"path"`
Status State `json:"status"`
DeferredAfter string `json:"deferred_after"`
DeferralReason string `json:"deferral_reason"`
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
}
DeferResult reports a deferral in the shape a transition reports: the record, where it is, and the waiver written. The record stays in open/.
func Defer ¶ added in v0.11.1
func Defer(req DeferRequest) (DeferResult, error)
Defer writes the release cut's waiver pair — deferred_after and deferral_reason — onto an open major or critical record, and appends a dated `## Deferral` section to its body, so the waiver is a validated write rather than a hand edit of frontmatter (iss-2609181223260994).
It refuses, with nothing written, everything the cut's own reader would not honour: a tag that is not the checkout's current anchor, an empty reason, a record that is not open, and a grade the guard never blocks on. A record already deferred past an earlier anchor is re-deferred: the pair is replaced and a new body section is appended, so the history of each deferral stays in the record. A record already deferred past the SAME anchor has the pair and that cycle's section replaced, so the body keeps one section per cycle.
type DispositionRequest ¶ added in v0.7.0
type DispositionRequest struct {
RepoRoot string
IssuesRoot string
// Item is the reading item this answers (rdi-N).
Item string
// State is one of issueschema.DispositionStates, subject to the per-position
// availability rule.
State string
// Grounds is disposition_grounds: free text, required on every state except
// held.
Grounds string
// ExitCondition is required on held and refused elsewhere.
ExitCondition string
// Supersedes names the standing disposition this one replaces (dsp-N) — the
// only exit from a hold.
Supersedes string
// Recurs cites prior item ids: the recorded form of the researcher's
// confirmed recognition of a persistence, never a state. The machine's
// proposal of the same thing is the item's own duplicates:/refines: link,
// written by the filing-time match when the item was stored (ruling DQ2b).
Recurs []string
// HoldFrameLocation / HoldMoscow are the RESERVED two-axis hold field. Both
// are refused while populated; the grammars are stated and dormant.
HoldFrameLocation string
HoldMoscow string
}
DispositionRequest writes one disposition, keyed to one reading item.
type DispositionResult ¶ added in v0.7.0
type DispositionResult struct {
ID string `json:"id"`
Item string `json:"item"`
State string `json:"state"`
Position string `json:"position"`
Path string `json:"path"`
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
}
DispositionResult is the outcome of a successful Disposition.
func Disposition ¶ added in v0.7.0
func Disposition(req DispositionRequest) (DispositionResult, error)
Disposition writes the researcher's answer to one reading item, into a directory keyed by the ITEM and a file keyed by the disposition's own id.
The two keys settle requirements that pull against each other. Status is the presence of the keyed directory — one probe, never a folder-membership question. And a disposition still has an id of its own, so the only exit from a held state (a superseding disposition that CITES the one it replaces) has something to cite. The superseded record stays in place: a hold that vanished when it was answered would take its own exit condition with it (adr-3 — the location is the state, and git is the history).
type DrainOutcome ¶ added in v0.12.0
type DrainOutcome string
DrainOutcome is the disposition a drain gives one open issue.
const ( // DrainEligible: every field rule passes, so the issue would go to a lane. // The host judgement over the remedy (scope 3) may still hand it back; it // can never make an issue eligible. DrainEligible DrainOutcome = "eligible" // DrainHandBack: the issue is a person's, by severity or category. DrainHandBack DrainOutcome = "handback" // DrainIneligible: a field the rule reads is missing (the remedy), or holds // the machine value an automatic filer writes in place of one. DrainIneligible DrainOutcome = "ineligible" // DrainSkipped: an open record blocks it; the blocker is named. DrainSkipped DrainOutcome = "skipped" // DrainUnreadable: the reader refuses the record, so no field can be read. DrainUnreadable DrainOutcome = "unreadable" )
The dispositions, one per open issue.
type DrainPlan ¶ added in v0.12.0
type DrainPlan struct {
// Record is the repository's own decision record the rule is read from.
Record string `json:"record"`
// Rule is the rule as that record states it.
Rule drainrule.Rule `json:"rule"`
// Loosened names every floor the record loosens against abcd's baseline
// (ruling H11); an empty list when it loosens none.
Loosened []string `json:"loosened"`
// Anchor is the checkout's newest release tag, the one a live deferral
// names, when any open record carries a deferral; empty otherwise.
Anchor string `json:"anchor,omitempty"`
// AnchorUnknown reports that an open record carries a deferral and the
// checkout holds no release tag (a shallow clone fetches none), so every
// record carrying a deferral is handed back rather than judged.
AnchorUnknown bool `json:"anchor_unknown,omitempty"`
// AnchorStale names the newest release tag an open record is deferred
// past that is newer than Anchor, so the checkout lacks it (one not
// fetched since the last cut); empty when no deferral names such a tag.
// Every record deferred past a tag the checkout lacks is handed back
// rather than judged.
AnchorStale string `json:"anchor_stale,omitempty"`
Order string `json:"order"`
Dispositions []DrainVerdict `json:"dispositions"`
Counts map[DrainOutcome]int `json:"counts"`
}
DrainPlan is what a drain would do over the open ledger, and writes nothing: every open issue's disposition, the eligible ones first in the order a drain takes them and the rest after them by id, with the repository's rule, the decision record it is stated in, and every floor it loosens.
func PlanDrain ¶ added in v0.12.0
func PlanDrain(req DrainPlanRequest) (DrainPlan, error)
PlanDrain classifies every open issue by field, under the repository's own rule. It refuses, before reading the ledger, a repository whose rule is unrecorded, ambiguous or malformed. Read-only: it takes no lock and writes nothing, as List does.
type DrainPlanRequest ¶ added in v0.12.0
DrainPlanRequest is the input to PlanDrain.
type DrainRule ¶ added in v0.12.0
type DrainRule string
DrainRule names the rule that decided a disposition.
const ( RuleUnreadable DrainRule = "unreadable" RuleNotOpen DrainRule = "not-open" RuleBlocked DrainRule = "blocked" RuleSecurity DrainRule = "security" RuleCategory DrainRule = "category" RuleSeverity DrainRule = "severity" // RuleDeferred: the record's deferral past the current anchor tag is live, // so a person carried it past this release. RuleDeferred DrainRule = "deferred" // RuleWaitsOnRuling: the remedy opens "Waits on", so the fix it proposes // waits on a ruling a person has not given. RuleWaitsOnRuling DrainRule = "waits-on-ruling" RuleRemedy DrainRule = "remedy" RuleFields DrainRule = "fields" )
The rules, in the order eligibility asks them.
type DrainVerdict ¶ added in v0.12.0
type DrainVerdict struct {
ID string `json:"id"`
Path string `json:"path"`
// Title is the record's one-line summary: its first non-blank body line.
Title string `json:"title,omitempty"`
Severity Severity `json:"severity,omitempty"`
Category Category `json:"category,omitempty"`
Outcome DrainOutcome `json:"outcome"`
Rule DrainRule `json:"rule"`
Reason string `json:"reason"`
// Blockers are the open records a skipped issue waits on.
Blockers []string `json:"blockers,omitempty"`
}
DrainVerdict is one open issue's disposition, with the rule that decided it and the reason in words.
type DriftFinding ¶ added in v0.10.0
type DriftFinding struct {
Kind string `json:"kind"`
Record string `json:"record"`
Other string `json:"other"`
Path string `json:"path"`
Message string `json:"message"`
}
DriftFinding is one broken join. Record is the record carrying the edge the finding is about, Other the record that edge names; Path is Record's path, repo-relative.
type FieldValueError ¶ added in v0.11.1
FieldValueError is a capture request member outside its closed vocabulary. It names the member, the value and the accepted set, and nothing about frontmatter: the value came from the caller's request, not from a record, so the refusal speaks about the request (iss-2608290810037524). A front door maps Field to its own spelling of the input — the CLI's flag of the same name.
func (*FieldValueError) Error ¶ added in v0.11.1
func (e *FieldValueError) Error() string
type Frame ¶ added in v0.11.1
type Frame struct {
Construal string `json:"construal"`
Glossary string `json:"glossary"`
Scope string `json:"scope"`
}
Frame is one frame state: the fingerprint of each surface.
type FrameSurface ¶ added in v0.11.1
FrameSurface is one of the frame's committed surfaces: its name in the record, its repo-relative path, and, for a surface that is one section of a chapter, the H2 heading that section is titled.
type IngestReadingRequest ¶ added in v0.7.0
type IngestReadingRequest struct {
RepoRoot string
IssuesRoot string
// Run is the run identifier (rdg-N) minted by the assembler.
Run string
// Manifest is the content-hash reference to the run's committed manifest,
// which is what makes the assembly re-runnable and diffable.
Manifest string
Position string
Regime string
Items []ReadingItem
// Match, when non-nil, runs the filing-time match on every item as it is
// stored (ruling DQ2b, adr-2609300821558671): each finding is compared, by
// its own words, with the open and resolved issues, the intents and every
// earlier reading item, and a likely repeat is written onto the item as a
// `duplicates:` or `refines:` link and reported. The items this ingest
// files are never candidates for each other. nil stores the items
// unmatched.
Match *match.Config
}
IngestReadingRequest writes one run's items into the ledger. Position and Regime come from the reading's DEFINITION, never from operator input, and are therefore run-level rather than per-item.
type IngestReadingResult ¶ added in v0.7.0
type IngestReadingResult struct {
Run string `json:"run"`
Records []ReadingRecordRef `json:"records"`
// Redacted counts the spans the ledger redactor rewrote across every item
// written, and Degraded is non-empty when it ran with a weakened pattern set.
// Both exist so a surface can SAY the text was altered.
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
// Matches is the filing-time match's outcome per record written, in the
// order the records were, when the request asked for a match. It is kept
// off ReadingRecordRef because that type is also the run record's
// committed list, which the match does not belong in.
Matches []ReadingMatch `json:"matches,omitempty"`
}
IngestReadingResult is the outcome of a successful ingest.
func IngestReading ¶ added in v0.7.0
func IngestReading(req IngestReadingRequest) (IngestReadingResult, error)
IngestReading writes one reading record per item under readings/<run-id>/rdi-<N>.md. Every id is minted (adr-45) and never derived from the item's content, so two runs returning the same tension carry different ids for free — a re-raise stays distinguishable from its first appearance, and the recurrence signal survives.
It is the ledger's record WRITER, not a payload validator: the output contract that "validated" refers to belongs to the cold-reading output contract, which calls this and adds no second validation path. What is enforced here is the RECORD schema — the envelope, the position's own body, and the reserved fields — which is this package's to refuse.
type Issue ¶
type Issue struct {
SchemaVersion int `json:"schema_version"`
ID string `json:"id"`
Slug string `json:"slug"`
Severity Severity `json:"severity"`
Category Category `json:"category"`
Source Source `json:"source"`
FoundDuring string `json:"found_during"`
FoundAt string `json:"found_at,omitempty"`
// LapsedAt is the RFC 3339 instant at which a recorded discipline gave way —
// the lapse itself, never the write-up (spc-60). Required exactly when
// Category is lapse; optional, and rarely meaningful, for every other.
LapsedAt string `json:"lapsed_at,omitempty"`
// Remedy is the proposed fix (itd-82 decision 6): the `remedy:` value, or
// the older `suggested_fix:` a record carries in its place
// (issueschema.RemedyOf). A drain takes no issue without one.
Remedy string `json:"remedy,omitempty"`
RelatedIntents []string `json:"related_intents,omitempty"`
RelatedSpecs []string `json:"related_specs,omitempty"`
RelatedIssues []string `json:"related_issues,omitempty"`
BlockedBy []string `json:"blocked_by,omitempty"` // iss-N dependency edges
// Duplicates and Refines are the typed links the filing-time match writes
// (itd-2609212137116617): the iss-N or itd-N this record is a near-identical
// double of, or a narrower case of. A person confirms a link by leaving it
// and removes it by deleting the line; a record without either is ordinary.
Duplicates []string `json:"duplicates,omitempty"`
Refines []string `json:"refines,omitempty"`
// Grounds is the record's recorded conjectures, in the order they were
// written: one `<token>: <text>` value in the shared core/grounds vocabulary
// per grounds-bearing act. Appended by promote, resolve and wontfix; never by
// the create path, because an observation being filed is not yet a conjecture
// being pursued.
//
// It is a LIST because recording is append-only. A record promoted and then
// resolved carries both conjectures, and the earlier one is precisely what a
// later reader checks the outcome against (iss-2608301657354776). The values
// live in the body's `## Grounds` section, not in frontmatter: a frontmatter
// scalar is set, and setting is what destroyed the first of them.
Grounds []string `json:"grounds,omitempty"`
Resolution string `json:"resolution,omitempty"`
WontfixReason string `json:"wontfix_reason,omitempty"`
ResolvedBy *ResolvedBy `json:"resolved_by,omitempty"`
Status State `json:"status"` // derived from folder
Path string `json:"path"` // repo-relative locator (iss-81)
Body string `json:"body"`
// BlockedByOpen is the derived subset of BlockedBy whose targets are still in
// open/ (the priority projection populated by List/Status). Not a stored
// field: an empty slice means the issue is unblocked.
BlockedByOpen []string `json:"blocked_by_open,omitempty"`
// Uncommitted is true when git reports the record's file untracked or
// changed in this checkout (iss-2609100508570527): folder membership is a
// status signal only once the file is committed, so an uncommitted record is
// in no state to any other branch, worktree or gate. Derived at read time by
// Status and List, never stored; false when git cannot answer.
Uncommitted bool `json:"uncommitted,omitempty"`
// contains filtered or unexported fields
}
Issue is a fully-read ledger entry (frontmatter + provenance + body).
type IssueDriftRequest ¶ added in v0.10.0
IssueDriftRequest is the input to IssueDrift. Now stamps the receipt; the zero value means the current time.
type IssueDriftResult ¶ added in v0.10.0
type IssueDriftResult struct {
Scanned int `json:"scanned"`
Findings []DriftFinding `json:"findings"`
// ReceiptPath is the repo-relative report this run left in the local tier.
ReceiptPath string `json:"receipt_path"`
}
IssueDriftResult is the outcome of one drift walk.
func IssueDrift ¶ added in v0.10.0
func IssueDrift(req IssueDriftRequest) (IssueDriftResult, error)
IssueDrift walks the intent store and the issue ledger (readings included) and reports every place the promote join does not read the same from both ends: the bidirectional cross-reference check itd-4 AC3 names, in the shape the predecessor store's spc-23 gave it. It writes nothing to either store; it leaves one report under .abcd/.work.local/logs/audit/issue-drift-<ts>/, and the front door decides what the findings mean for its exit code.
type LinkRequest ¶ added in v0.10.0
type LinkRequest struct {
RepoRoot string
IssuesRoot string
ID string // the subject iss-N, in any status folder
BlockedBy []string // iss-N ids to append to the record's blocked_by
Unblock []string // iss-N ids to remove from it
}
LinkRequest edits one record's blocked_by edges AFTER capture (iss-2609200951237670). Unblock is applied first, then BlockedBy, so the same id on both sides is removed and re-added — a documented net no-op. At least one of the two must be non-empty.
type LinkResult ¶ added in v0.10.0
type LinkResult struct {
ID string `json:"id"`
Path string `json:"path"`
BlockedBy []string `json:"blocked_by"`
}
LinkResult is the outcome of a successful Link: the subject, its repo-relative path, and the blocked_by list AS WRITTEN. An emptied list comes back as an empty list, never null — the field is the point of the verb.
func Link ¶ added in v0.10.0
func Link(req LinkRequest) (LinkResult, error)
Link appends to, or removes from, one record's blocked_by list — the post-hoc form of capture's --blocked-by, for the ordinary case the create-time flag cannot serve: the blocker captured after the blocked record, or in another lane. The subject may sit in ANY status folder and never moves: a resolved record's edges are history, still editable, and the write is the same in-place stamp promote uses from any folder (find, checksum-read, rewrite the frontmatter, validate the result against the folder it is in, atomic write under the ledger lock). The derived-priority reader picks the change up with no other change, because it reads the field this rewrites.
Every refusal is raised before anything is written: the targets through validateBlockers (capture's own validator), and an --unblock of an id the list does not currently hold, which is refused naming the current list — removing an edge that is not there is a wrong belief about the record, not a no-op.
type ListRequest ¶
ListRequest queries one state (or "all").
type ListResult ¶
type ListResult struct {
Issues []Issue `json:"issues"`
Skipped []SkipRecord `json:"skipped"`
}
ListResult is Issues sorted ascending by numeric N plus a corrupt roster.
func List ¶
func List(req ListRequest) (ListResult, error)
List scans one state (or all) and returns issues sorted ascending by numeric N plus a roster of unparseable files. Read-only: no preamble, no dir creation, tolerant of a virgin/absent ledger.
type MentionEvidence ¶ added in v0.9.0
type MentionEvidence struct {
Commit string `json:"commit"`
Date string `json:"date"`
Subject string `json:"subject"`
Strength string `json:"strength"`
}
MentionEvidence is one commit that named a record, and how it named it.
type MentionRow ¶ added in v0.9.0
type MentionRow struct {
ID string `json:"id"`
Severity Severity `json:"severity,omitempty"`
Path string `json:"path,omitempty"`
Strength string `json:"strength"`
Evidence []MentionEvidence `json:"evidence"`
}
MentionRow is one open record the walk found named, with every mention that named it. A row with no evidence is never emitted: a row nobody can check is an accusation, not a listing.
type MentionsRequest ¶ added in v0.9.0
type MentionsRequest struct {
RepoRoot string
IssuesRoot string
// Ref is the history to walk. Empty means the repository's default branch,
// resolved without touching the network.
Ref string
}
MentionsRequest asks for the listing over one repository.
type MentionsResult ¶ added in v0.9.0
type MentionsResult struct {
Ref string `json:"ref"`
Commits int `json:"commits"`
OpenRecords int `json:"open_records"`
Rows []MentionRow `json:"rows"`
Skipped []SkipRecord `json:"skipped"`
}
MentionsResult is the advisory listing. Ref and Commits say what was walked, so a reader can tell an empty listing from an unwalked history.
func Mentions ¶ added in v0.9.0
func Mentions(req MentionsRequest) (MentionsResult, error)
Mentions lists open records whose ids are named by the history of Ref with no resolution behind them. Strictly read-only: it runs read-only git commands and the ledger's own List, and writes nothing anywhere.
type MigrateChange ¶ added in v0.10.0
type MigrateChange struct {
ID string `json:"id"`
Path string `json:"path"` // repo-relative
// Retired is the retired line the record carries, `key: value`, or empty
// when the record carries none and changes only because the OTHER end of a
// join it belongs to did.
Retired string `json:"retired,omitempty"`
// Key is the successor list the record carries after the change, and List
// its full value then.
Key string `json:"key"`
List []string `json:"list"`
}
MigrateChange is one record the migration rewrites (or would rewrite).
type MigrateRequest ¶ added in v0.10.0
MigrateRequest is the input to Migrate. Apply false reports what would change and writes nothing.
type MigrateResult ¶ added in v0.10.0
type MigrateResult struct {
Applied bool `json:"applied"`
// Scanned counts the ledger records and intents read.
Scanned int `json:"scanned"`
Changes []MigrateChange `json:"changes"`
// Notes are joins the migration completed from one end only because the
// other end names a record this tree does not hold. The drift check reports
// the same joins as dangling; the migration writes what it can.
Notes []string `json:"notes,omitempty"`
}
MigrateResult reports a migration run.
func Migrate ¶ added in v0.10.0
func Migrate(req MigrateRequest) (MigrateResult, error)
Migrate rewrites the promote join's retired back-links into the names itd-4 AC3 gives them, across the issue ledger, the readings store and the intent store: `promoted_to: itd-M` on a ledger record becomes a member of its `related_intents`, and `promoted_from: <id>` on an intent becomes the first member of its `related_issues`.
"Promoted" is the two-sided PAIR under the new names (see promotedInto), where the old scheme let either end say it alone. So a join an older abcd wrote from one end only — a link-mode promote of a hand-filed draft stamped the ledger half alone — is completed from the other end as well, or the migrated tree would stop saying the record was promoted at all.
Nothing else moves: a loose `related_intents` entry is kept where it is, and a record carrying no retired key and joined to nothing that did is left byte-identical. The run is idempotent. It holds the ledger lock and, inside it, the intent store's lock for the whole apply, so no verb writes either store between the read and the rewrite.
type PromoteRequest ¶
type PromoteRequest struct {
RepoRoot string
IssuesRoot string
// ID is the record to graduate: an iss-N, or the rdi-N of a reading item
// that has been dispositioned.
ID string
LinkIntent string // itd-N; "" mints
// Grounds is the REQUIRED conjecture behind the promotion, in the shared
// `<token>: <text>` grammar (core/grounds). A capture routed to an intent
// draft is a conjecture being pursued, and there is nothing to stage here:
// promote mints the value in the same call, so it has no corpus to fix.
Grounds string
// ProductionMode is how the MINTED DRAFT's seed text was produced (itd-178),
// or empty for the vocabulary's default. It has no effect in stamp-only mode,
// where nothing is minted. The draft's `origin` is not a member here: promote
// is the one shipped path that derives a record from another record, so it
// derives extracted-from-record from what it did rather than from what it was
// told.
ProductionMode string
// Match, when non-nil, runs the filing-time match on the draft a reading
// item's promotion mints (ruling DQ2b): the item's finding, in its own
// words, is compared with the open and resolved issues and the intents,
// and each likely double is written onto the draft as capture writes it.
// nil, stamp-only mode, and the issue route mint unmatched.
Match *match.Config
}
PromoteRequest is the input to Promote: graduate an issue into an intent. LinkIntent, when non-empty, selects the stamp-only mode that links an EXISTING draft (itd-N) instead of minting one — the repair path after a stamp failure, and the "I already filed the intent by hand" path.
type PromoteResult ¶
type PromoteResult struct {
IssueID string `json:"issue_id"`
// IssueStatus is the source record's status. For an issue that is its status
// directory; for a reading item it is the STANDING DISPOSITION's state,
// because that family's status signal is the presence of the keyed
// disposition and never folder membership.
IssueStatus State `json:"issue_status"`
IssuePath string `json:"issue_path"`
IntentID string `json:"intent_id"`
IntentPath string `json:"intent_path"`
Linked bool `json:"linked"`
// BackEdgeKept names the record a linked draft's `related_issues` ALREADY
// carried first, when this call appended beside it. An intent occasioned by
// several records is promoted from one and joined to the others, so an
// existing back-edge is kept first and reported rather than refused or
// overwritten (itd-2609020625400169, first scope condition). Empty on every
// other outcome, the mint included.
BackEdgeKept string `json:"back_edge_kept,omitempty"`
// Redacted / Degraded mirror TransitionResult: the grounds text is free prose
// written to the same committed ledger, so it goes through the same redactor
// and reports the same way. Rewriting somebody's reasoning in silence is worse
// than not recording it.
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
// Match is the minted draft's filing-time match, when one was asked for.
Match *match.Outcome `json:"match,omitempty"`
}
PromoteResult is the outcome of a successful Promote. Paths are repo-relative. Linked reports stamp-only mode (no draft minted this call).
func Promote ¶
func Promote(req PromoteRequest) (PromoteResult, error)
Promote graduates an issue into an intent without retyping (spc-24, step 2 of the record walk). Default mode mints an intent draft — slug reused from the issue, body carrying a by-id pointer to the issue rather than a copy (SSOT), the issue named in the draft's `related_issues` — then appends the minted itd-N to the issue's `related_intents`. The two halves are itd-4 AC3's bidirectional join, and the pair — an intent the record names that names the record back — is what "promoted" means (see promotedInto). Promotion is orthogonal to fix-status: the issue may sit in any status directory and never moves.
Ordering + residue contract: mint first, stamp second. No cross-store lock is attempted — the ledger lock alone guards the stamp, exactly as transition does — so a failure after the mint leaves an orphan draft; the returned error names the draft and the stamp-only remedy (`capture promote <iss-N> --intent <itd-N> --grounds '...'`), carrying the promotion's own grounds, single-quoted, so the remedy runs as printed in any shell — an interactive one included.
Every refusal that can be established from the bytes in hand is therefore raised BEFORE the mint: the grounds text, whether the record is one the validators would let the stamp write at all, and whether it can take the append. What is left to the stamp is what only a write under the lock can discover, which is the residue the remedy above is for — never a deterministic refusal that would leak one draft per attempt.
type ReadingItem ¶ added in v0.7.0
type ReadingItem struct {
// Pattern is the pattern named — required at every position and in every
// supply regime.
Pattern string
// Body carries exactly the fields the run's position declares
// (issueschema.ReadingBodyFields). A field from another position's body is
// refused: one record type, four bodies, and an item belongs to one of them.
Body map[string]string
}
ReadingItem is one thing the instrument returned: the pattern it named (an envelope field, because a universal core condition must not live in a variant part) plus the position-typed body.
type ReadingMatch ¶ added in v0.12.0
ReadingMatch is one stored reading item's filing-time match.
type ReadingRecordRef ¶ added in v0.7.0
ReadingRecordRef is one written reading record: its minted id and its repo-relative path.
type ReframeRequest ¶ added in v0.11.1
type ReframeRequest struct {
RepoRoot string
IssuesRoot string
// OccasionedBy is the reading item, disposition or surprise that occasioned
// the reframe (rdi-N, dsp-N or srp-N), and nothing else.
OccasionedBy string
// Grounds is why the frame moved, held to the grounds floor.
Grounds string
// Open records the first half, before the rewrite is committed.
Open bool
// Complete names the open record (rfm-N) to finish after the commit. It
// takes no occasion, ground or --open: those are the first half's.
Complete string
}
ReframeRequest records one reframe, or completes an open one.
type ReframeResult ¶ added in v0.11.1
type ReframeResult struct {
ID string `json:"id"`
Path string `json:"path"`
OccasionedBy string `json:"occasioned_by"`
Before Frame `json:"before"`
After Frame `json:"after"`
Changed []string `json:"changed"`
// Half says which half this invocation wrote: whole, open or completed.
Half string `json:"half"`
// Commits is how many commits touching the frame the walk crossed between
// the before and the after state.
Commits int `json:"commits"`
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
}
ReframeResult is the outcome of a successful Reframe.
func Reframe ¶ added in v0.11.1
func Reframe(req ReframeRequest) (ReframeResult, error)
Reframe writes one reframe record, its first half, or its second half.
The whole write (no flag) requires the working tree to match HEAD, walks the surfaces' history to the previous distinct committed state, and writes both halves at once. `Open` writes the before half from HEAD's triple. `Complete` finishes an open record once HEAD's triple differs from its before triple, walking back until it finds that triple within the bound. Every refusal writes nothing.
type RemedyRequest ¶ added in v0.12.0
type RemedyRequest struct {
RepoRoot string
IssuesRoot string
ID string
// Remedy is the proposed fix. Whitespace runs, line breaks included, are
// folded to single spaces, since the field is one line.
Remedy string
}
RemedyRequest writes a person's remedy onto one open record.
type RemedyResult ¶ added in v0.12.0
type RemedyResult struct {
ID string `json:"id"`
Path string `json:"path"`
Status State `json:"status"`
Remedy string `json:"remedy"`
Previous string `json:"previous,omitempty"`
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
}
RemedyResult reports the remedy written and the one it replaced, so a replacement is never silent. The record stays in open/.
func SetRemedy ¶ added in v0.12.0
func SetRemedy(req RemedyRequest) (RemedyResult, error)
SetRemedy writes or replaces the `remedy:` of an open record. It refuses, with nothing written, a malformed id, an empty remedy, the machine value (which only an automatic filer writes, and which would leave the record as the drain already skips it), and a record that is not open. A record still carrying the older `suggested_fix:` keeps it; `remedy:` is read first.
type ResolveRequest ¶
type ResolveRequest struct {
RepoRoot string
IssuesRoot string
ID string
Resolution string
// Impact is the product judgement resolved/ requires (issue_impact_valid):
// one of the shared changelog enum's values (additive|breaking|fix|internal).
// There is no default — an empty or invalid value is refused, never invented,
// so a resolved record the tool mints always satisfies its own blocker.
Impact string
// ShippedIn optionally names the release that already carried this work, as a
// tag (v0.6.2). It is a MIGRATION mechanism for the ledger-hygiene case:
// closing a record for a fix released long ago. A repository abcd manages from
// its first commit should never need it, because RS001 makes resolution ride
// the fixing commit and the cut is then right by construction. The derivation reads it and leaves such a record out
// of the current cut, so the release record cannot announce old work as new
// (iss-2608241612087533). Absent by default — the ordinary resolution is for
// work shipping in the release being prepared, and it must never be guessed.
ShippedIn string
// ByIntent / BySpec / ByCommit are the optional resolved_by provenance
// members (spc-25): the intent, spec, or commit that fixed the issue.
// Ids must exist in their record store (any bucket); the sha is
// shape-checked only. All optional — absent members are never defaulted.
ByIntent string // itd-N
BySpec string // spc-N
ByCommit string // 7–64 hex chars (64 covers a SHA-256 repo)
// Grounds is the REQUIRED conjecture behind the resolution, in the shared
// `<token>: <text>` grammar (core/grounds). There is no default and none may
// be invented: a route recorded without its reasoning is the evaporation
// itd-179 exists to close, and resolve mints the value in the same call, so
// it has no corpus to fix and refuses from the start.
Grounds string
// ProductionMode RESTAMPS the record's production_mode (itd-178). A
// resolution note is new text with its own mode, so the key is not frozen at
// mint — but an empty value leaves the existing stamp alone rather than
// overwriting it with a default, because a transition that declares nothing
// has made no claim about how the note was produced. `origin` is never
// rewritten: where a record came from does not change when it is resolved.
ProductionMode string
}
ResolveRequest moves an open issue to resolved/.
type ResolvedBy ¶
type ResolvedBy struct {
Intent string `json:"intent,omitempty"`
Spec string `json:"spec,omitempty"`
Commit string `json:"commit,omitempty"`
}
ResolvedBy is an optional structured pointer to what resolved an issue.
type SkipLayer ¶ added in v0.11.1
type SkipLayer = issuerecord.Layer
SkipLayer is the reader stage that refused a ledger file, in scan order. It is the record reader's own type (core/issuerecord), so the board and the committed-ledger gate name a refusal with one vocabulary.
type SkipRecord ¶
type SkipRecord struct {
Path string `json:"path"`
Layer SkipLayer `json:"layer"`
Error string `json:"error"`
}
SkipRecord surfaces a corrupt/invalid ledger file without failing the scan.
Layer names WHICH reader stage refused the file (iss-2609120452071388). A skip reported as a bare error left the reader unable to tell whether the writer or the validator was the side that was wrong: a name the grammar refuses, a leaf the guarded read refuses, and a value the schema refuses are three different defects with three different remedies.
type State ¶
type State string
State is a ledger status directory (or "all" for a cross-status scan).
type StatusRequest ¶
StatusRequest is the input to the read-only status render.
type StatusResult ¶
type StatusResult struct {
OpenCount int `json:"open_count"`
ResolvedCount int `json:"resolved_count"`
WontfixCount int `json:"wontfix_count"`
// SkippedCount is the number of files that claim to be records and that
// none of the three totals counts, because the reader refused them. It is
// len(Skipped), carried as a count beside the others so the board states
// what it excluded next to what it counted (iss-2609120452071388).
SkippedCount int `json:"skipped_count"`
// UncommittedCount is the number of readable records across the three
// folders that git reports untracked or changed (iss-2609100508570527).
UncommittedCount int `json:"uncommitted_count"`
RecentOpen []Issue `json:"recent_open"` // up to 10, newest first
Skipped []SkipRecord `json:"skipped"`
}
StatusResult is the bare-invocation status snapshot (guaranteed no mutation).
func Status ¶
func Status(req StatusRequest) (StatusResult, error)
Status is a pure read: counts per status dir plus up to 10 most-recent open issues (newest first). Guaranteed no mutation.
type SurpriseRequest ¶ added in v0.11.1
type SurpriseRequest struct {
RepoRoot string
IssuesRoot string
// OccasionedBy is the record that occasioned it: an rdi-N, adm-N or dsp-N
// this ledger holds, and nothing else.
OccasionedBy string
// Text is the surprise itself, held to the grounds floor.
Text string
}
SurpriseRequest records one surprise.
type SurpriseResult ¶ added in v0.11.1
type SurpriseResult struct {
ID string `json:"id"`
OccasionedBy string `json:"occasioned_by"`
Path string `json:"path"`
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
}
SurpriseResult is the outcome of a successful Surprise.
func Surprise ¶ added in v0.11.1
func Surprise(req SurpriseRequest) (SurpriseResult, error)
Surprise writes one surprise entry. It refuses, writing nothing, an occasion that is not a handle of the three families or that resolves to nothing, and a text below the substance floor.
type TransitionResult ¶
type TransitionResult struct {
ID string `json:"id"`
Path string `json:"path"`
FromStatus State `json:"from_status"`
ToStatus State `json:"to_status"`
ResolvedBy *ResolvedBy `json:"resolved_by,omitempty"`
// Redacted / Degraded mirror CaptureResult: a resolution or wontfix note is
// free text written to the same committed ledger, so it goes through the
// same redactor and reports the same way.
Redacted int `json:"redacted,omitempty"`
Degraded string `json:"redaction_degraded,omitempty"`
// Relinked lists every relative markdown link the transition repointed
// because it named the issue's old path in open/ (iss-2609250846525896).
Relinked []relink.Rewrite `json:"relinked,omitempty"`
// RelinkError is a NON-FATAL report of a repoint that failed part-way: the
// issue has moved and the transition stands, so the surface prints it
// loudly, and record-lint's links_resolve names any link left behind.
RelinkError string `json:"relink_error,omitempty"`
}
TransitionResult is the outcome of a Resolve or Wontfix. ResolvedBy echoes the provenance members a Resolve wrote (nil on a flagless resolve and on every Wontfix — a non-action points at nothing).
func Resolve ¶
func Resolve(req ResolveRequest) (TransitionResult, error)
Resolve moves an open issue to resolved/, writing the resolution note and the product impact. resolved/ is gated by issue_impact_valid, so the impact is validated against the shared changelog enum up front (empty or invalid is refused, never defaulted) and stamped bare alongside the note — the tool's own resolve path can never mint a record its own blocker rejects.
The optional ByIntent/BySpec/ByCommit members land as the structured resolved_by object (spc-25) in the same atomic transition. Ids are validated for existence in their record store before anything is written; the sha is shape-checked only (its home may be the remote, and shallow or rebased states must not refuse a legitimate resolution). No members → no resolved_by key at all: provenance is optional, never defaulted.
func Wontfix ¶
func Wontfix(req WontfixRequest) (TransitionResult, error)
Wontfix moves an open issue to wontfix/, writing the wontfix_reason note and the `declined:` grounds derived from it. wontfix/ carries no impact (issue_impact_valid gates resolved/ only), so no judgement is stamped.
It needs no new required flag: transition already refuses an empty reason, so a wontfix could never be recorded without grounds — what it lacked was the TYPE. Grounds overrides the text when the conjecture is worth stating separately from the user-facing reason.
type WontfixRequest ¶
type WontfixRequest struct {
RepoRoot string
IssuesRoot string
ID string
Reason string
// Grounds optionally overrides the text stamped as `declined: <text>`. A
// wontfix can never be recorded without grounds — transition already refuses
// an empty Reason — so what it lacked was the TYPE, not the text, and the
// reason supplies the default. The override exists because the user-facing
// reason and the conjecture are not always the same sentence. The token stays
// declined: a non-action is what that value names.
Grounds string
// ProductionMode restamps production_mode on the same terms as
// ResolveRequest's: declared restamps, absent leaves the stamp alone.
ProductionMode string
// Duplicates names the records this one duplicates (iss-N or itd-N), written
// to the typed `duplicates` link the filing-time match also writes, so a
// machine reader sees a duplicate closure rather than a wontfix whose
// relation lives only in the reason's prose (iss-2609291118049254). Each
// target must exist, in any status folder or intent bucket, and may not be
// the record itself; a link the record already carries is kept.
Duplicates []string
}
WontfixRequest moves an open issue to wontfix/.
Source Files
¶
- admit.go
- alloc.go
- auditowed.go
- capture.go
- citationgate.go
- consistency.go
- deferral.go
- drift.go
- eligible.go
- foundat.go
- grounds.go
- itemfate.go
- ledgerroot.go
- link.go
- match.go
- mentions.go
- migrate.go
- promote.go
- reading.go
- recordref.go
- redact.go
- reframe.go
- remedy.go
- roots.go
- serialize.go
- surprise.go
- uncommitted.go
- validate.go
- workflow.go