Documentation
¶
Overview ¶
Package intent is abcd's transport-agnostic native intent store (intent lifecycle, itd-80). It owns the in-memory model of intent records and the disk operations that plan, link, and summarise them. Every function takes a structured request and returns a structured result; nothing here writes to stdout or knows about a CLI, MCP, or hook surface — the front doors under internal/surface/* marshal these results for their transport.
An intent record is a markdown file under <repoRoot>/.abcd/development/intents/{drafts,planned,shipped,disciplines, superseded}/itd-N-<slug>.md. The bucket directory IS the lifecycle state (directory-as-truth: there is no status: frontmatter field), mirroring the native spec store. The load-bearing field is spec_id: spc-N, the intent's derived side of the bidirectional link to the spec that realises it (the spec's reciprocal side is intent: itd-N).
Frontmatter is read by the shared internal/core/frontmatter line scanner, not a YAML parser — the package pulls in zero new dependencies. Ids are validated against strict regexes before any path is built, so a hostile id can never traverse out of the intent store.
Index ¶
- Constants
- Variables
- func CheckOwedCap(max int) error
- func DuplicateConditionIDs(conds []ScopeCondition) []string
- func IDOlder(a, b string) bool
- func IsClaimPrompt(body string) bool
- func IsSeedNote(text string) bool
- func MalformedMarkerOrdinals(conds []ScopeCondition) []int
- func MultiplyMarkedConditions(conds []ScopeCondition) []int
- func ParseGrounds(content string) []grounds.Grounds
- func PickEntryText(runID string, date time.Time, p Pick) string
- func PickLess(a, b PickCandidate) bool
- func PickOrder(c []PickCandidate)
- func PrepareGrounds(repoRoot string, g grounds.Grounds) (grounds.Grounds, int, error)
- func ReEmitCommand(intentID string) string
- func ReadConsistencyFindings(path string) ([]byte, error)
- func ReadPrepassFindings(path string) ([]byte, error)
- func ReadVerdict(verdictPath string) ([]byte, error)
- func SetAuditLedger(file func(AuditOwed) (AuditFiling, error), clear func(AuditCleared) error)
- func SetLedgerLock(lock func(repoRoot string, fn func() error) error)
- func SetProseCitationGate(fn func(repoRoot, rel, text string) ([]UnresolvedCitation, error))
- func SupersessionChainOf(repoRoot string, links []ChainLink, id string) (chain []string, endID, endBucket, problem string, err error)
- func Targets(repoRoot string) ([]launch.TargetedIntent, error)
- func UnmarkedConditionOrdinals(conds []ScopeCondition) []int
- func Validate(it Intent) error
- func WithLedgerThenMintLock(repoRoot string, ledger func(func() error) error, fn func() error) error
- func WithMintLock(repoRoot string, fn func() error) error
- type AuditCleared
- type AuditEmitOptions
- type AuditEmitResult
- type AuditFiling
- type AuditOwed
- type BundleMemberClose
- type BundleOptions
- type BundleResult
- type ChainLink
- type ClaimState
- type Claims
- type ConditionRequest
- type ConditionResult
- type ConditionStandingView
- type ConsistencyCheck
- type ConsistencyEmitOptions
- type ConsistencyEmitResult
- type ConsistencyEnd
- type ConsistencyFiler
- type ConsistencyFiling
- type ConsistencyFinding
- type ConsistencyIngestRequest
- type ConsistencyIngestResult
- type ConsistencyRow
- type Corpus
- type Created
- type DeliveryAudit
- type DeliveryVerdict
- type DraftOptions
- type DrainStep
- type EdgeRequest
- type EdgeResult
- type GroundsResult
- type HoldResult
- type IngestVerdictResult
- type Intent
- func AddRelatedIssue(repoRoot, intentID, source string) (Intent, error)
- func CreateDraft(repoRoot string, opts DraftOptions) (Intent, error)
- func CreateDraftMatched(repoRoot string, opts DraftOptions) (Intent, *match.Outcome, error)
- func CreateFromText(repoRoot, text string, opts TextOptions) (Intent, error)
- type IntentListing
- type LinkResult
- type LinkedPair
- type MatchText
- type Matcher
- type OccasionCitation
- type OwedCriterion
- type PathMove
- type Pick
- type PickCandidate
- type PlanOptions
- type PlanResult
- type PrepassBriefResult
- type PrepassIndexEntry
- type PrepassInput
- type PrepassInvariant
- type PrepassPrinciple
- type QueuedReview
- type ReadinessPart
- type ReadinessScore
- type ReadinessWeights
- type ReadyCheck
- type ReadyResult
- type ReclassifyRequest
- type ReclassifyResult
- type ReconcileResult
- type RemainderRequest
- type ReviewEntry
- type ReviewListing
- type ReviewQueue
- type ScopeCondition
- type ShippedOn
- type StandingEntry
- type StartChecks
- type StartRow
- type StatusView
- type TargetResult
- type TargetRewrite
- type TextOptions
- type UnholdResult
- type UnresolvedCitation
Constants ¶
const ( MechanismPrompt = "" /* 293-byte string literal not displayed */ ScopeConditionsPrompt = "" /* 300-byte string literal not displayed */ )
The two claim-section prompts this package seeds. They are the contract a human replaces, not a claim a human made, so a gate that reads one has to be able to say which it is looking at.
const ( ShippedDated = "dated" ShippedUncommitted = "uncommitted" ShippedUnknown = "unknown" )
The states of a queued review's shipped day. An undated entry is either uncommitted (the history was read and does not hold it yet) or unknown (no history was read at all); the two sort alike and mean different things.
const ( BucketDrafts = "drafts" BucketPlanned = "planned" BucketShipped = "shipped" BucketDisciplines = "disciplines" BucketSuperseded = "superseded" )
Lifecycle buckets. The directory an intent file lives in is its state.
const ( KindStandalone = "standalone" KindBundleMember = "bundle-member" KindDiscipline = "discipline" )
The binding kinds (itd-34). KindStandalone is the default Plan writes (a 1:1 intent↔spec); a bundle-member shares one spec with its bundle-mates; a discipline is a cross-cutting rule on disciplines/, with no spec of its own.
const ( // ACStateReal is an Acceptance Criteria section holding at least one // top-level bullet: the bar plan checks. ACStateReal = "real" // ACStateSeeded is a section holding no bullet — the placeholder the create // path seeds, or nothing — so the intent cannot be planned yet. ACStateSeeded = "seeded" )
The two values of IntentListing.ACState.
const ( ReviewOwed = "OWED" ReviewIngested = "INGESTED" ReviewDeadLetter = "DEAD_LETTER" ReviewNone = "none" )
Review states. The first three are the marker's own words; ReviewNone is a shipped intent with no marker at all (shipped before markers existed, or a ship whose emit failed).
const ( PrepassInputType = "abcd/intent-prepass-input/v1" PrepassFindingsType = "abcd/intent-prepass-findings/v1" )
PrepassInputType and PrepassFindingsType are the two documents' _type values.
const ( CheckBucket = "bucket" CheckAcceptanceCriteria = "acceptance_criteria" CheckMechanismClaim = "mechanism_claim" CheckScopeConditions = "scope_conditions" CheckSpecLink = "spec_link" CheckSpecBody = "spec_body" CheckSteps = "steps" CheckGrounds = "grounds" )
Check names, in the fixed order Ready always reports them.
const ( StartCheckOpenQuestions = "open_questions" StartCheckClaimSections = "claim_sections" StartCheckHold = "hold" StartCheckBlocked = "blocked" StartCheckSteps = "steps" )
The record-only pre-start check names, in the order StartChecksIn reports them.
const BlockedByKey = "blocked_by"
BlockedByKey is the frontmatter key a record names the records it waits on under.
const BuildsOnKey = "builds_on"
BuildsOnKey is the frontmatter key of the softer dependency edge: cheaper or better if the other intent exists first. BlockedByKey is the hard one.
const BundleKey = "bundle"
BundleKey is the frontmatter key a bundle-member names its bundle under.
const CaptureSeedNote = captureSeedOpening + " " + seedNoteTail
CaptureSeedNote is the placeholder a quoted-text capture once minted, whole: recognized on the drafts that carry it, written by no route today.
const ConsistencyScopeCorpus = "corpus"
ConsistencyScopeCorpus is the scope of a bare run: the whole corpus.
const ConsistencyType = "abcd/intent-consistency-findings/v1"
ConsistencyType is the only _type the consistency ingest accepts.
const GroundsHeading = grounds.Heading
GroundsHeading is the section an intent record carries its grounds under — core/grounds's one spelling, so the intent half and the ledger half name the same section.
const HeldKey = "held"
HeldKey is the frontmatter key the hold lives under, spelled once.
const IntentsRelDir = ".abcd/development/intents"
IntentsRelDir is the intent-store root, relative to the repo worktree.
const KindSuperseded = "superseded"
KindSuperseded is the reclassify target that retires a record to superseded/. It is a target, never a persisted kind: the retired record keeps the kind it had, and records it again as kind_at_supersession.
const NullityToken = "None stated."
NullityToken is the one exact spelling that records a claim as considered and declined: alone on its line under the section heading, byte for byte. Prose that merely contains the word "none" is a stated claim — the whole point of an exact token is that a gate can tell a declined claim from a discussed one.
const PartPoints = 100
PartPoints is the most one part of the readiness score carries.
const PickFalsifier = "" /* 127-byte string literal not displayed */
PickFalsifier is the lane outcome that shows a pick wrong, as the reason states it: an outcome the lane's state file can meet.
const PickRule = "" /* 127-byte string literal not displayed */
PickRule is the rule that places the winner, as the reason states it.
const PlanningBriefsRelDir = ".abcd/.work.local/scratch/planning-briefs"
PlanningBriefsRelDir is where a planning brief lives: the local tier, beside the briefs a session writes by hand (commands/intent.md, Autonomous runs).
const ReclassificationHistoryKey = "reclassification_history"
ReclassificationHistoryKey is the append-only kind-change log.
const RelatedIssuesKey = "related_issues"
RelatedIssuesKey is the frontmatter key of the intent half of the promote join (itd-4 AC3): the ledger records the intent graduated from.
const RetiredRelatedIssuesKey = "promoted_from"
RetiredRelatedIssuesKey is the key the promote back-edge was written under before itd-4 AC3 renamed it. Nothing writes it and the reader does not read it: a record still carrying it is migrated by `abcd capture migrate --apply`, and a write that meets it refuses rather than leaving both spellings of one join on a record.
const ReviewsShelfRelDir = ".abcd/work/reviews"
ReviewsShelfRelDir is the reviews shelf the report is filed on — the committed working tier's `reviews/` directory, under its charter.
const RunPickMarker = "picked by run "
RunPickMarker opens the text of every grounds entry a run's pick writes: "picked by run <run-id> on <date>". The token vocabulary is closed, so the marker lives in the text after the `pursued:` token (the intent's open question keeps the token's shape open).
const VerdictType = "abcd/intent-fidelity-verdict/v1"
VerdictType is the only _type the ingest accepts.
const WhyThisMattersPrompt = "> _Why this matters to the user — replace before planning._"
WhyThisMattersPrompt is what `## Why This Matters` carries on a draft whose route seeded no body for it — the quoted-text route, whose text is the press release and is not pasted here a second time. Same register as the claim prompts below: a contract the human replaces, not content anybody wrote. No gate reads the section today, so nothing needs to tell the prompt from prose; a reader that comes to need that compares against this constant, for the reason IsClaimPrompt gives.
Variables ¶
var Buckets = []string{BucketDrafts, BucketPlanned, BucketShipped, BucketDisciplines, BucketSuperseded}
Buckets is the fixed lifecycle order used for loading and rendering.
var BundledReadinessWeights = ReadinessWeights{Criteria: 1, TestPath: 1, Footprint: 1}
BundledReadinessWeights are the equal weights the score is declared with (decision 7).
var ConsistencyClasses = []string{
"terminology_drift",
"premise_contradiction",
"scope_leakage",
"sequencing_impossibility",
"naming_conflict",
}
ConsistencyClasses is the closed set of judgement classes, in report order.
var ErrNoProseCitationGate = errors.New("no prose-citation gate is registered")
ErrNoProseCitationGate is UnresolvedProseCitations' answer when no front door registered the gate: the question cannot be answered, and a caller refuses rather than writes (fail closed).
var ErrRetiredField = fmt.Errorf("intent: the record carries a retired back-link field")
ErrRetiredField reports a record that still carries a retired back-link key, so a write would leave both spellings of one join side by side.
var PrepassAnswers = []string{"keep-both", "bundle", "supersede", "refine"}
PrepassAnswers are the four standard answers an overlap is asked with (decision 2), in the order the brief lists them.
Functions ¶
func CheckOwedCap ¶ added in v0.11.1
CheckOwedCap refuses a negative cap (0 is no cap). A front door calls it before any costlier work, such as the history walk that supplies ShippedOn.
func DuplicateConditionIDs ¶ added in v0.7.0
func DuplicateConditionIDs(conds []ScopeCondition) []string
DuplicateConditionIDs returns the identity markers carried by more than one condition, in first-seen order. Two conditions sharing an identity is a fault the gate names rather than repairs: a disposition keyed on a duplicated marker would attach to whichever bullet the reader happened to reach first.
func IDOlder ¶ added in v0.12.0
IDOlder reports whether intent id a is older than b: an ordinal id predates every timestamp id (adr-45), and ids of one kind order by their number.
func IsClaimPrompt ¶ added in v0.7.0
IsClaimPrompt reports whether a claim section's body is still one of the prompts above, rather than something somebody wrote.
It lives HERE, with the templates, for the reason IsSeedNote does: a consumer comparing against its own copy of the sentence would keep matching the old wording the moment these are reworded, and the symptom would be an unanswered prompt reported to a reader as a recorded claim.
func IsSeedNote ¶
IsSeedNote reports whether a press-release body is still one of the templates this package writes, rather than something somebody wrote.
It lives HERE, with the templates, because it is the only place that can stay true: a consumer comparing against its own copy of the sentence would keep matching the old wording the moment these are reworded, and the symptom would be a placeholder quoted at a reader as a testimonial. Both minted forms are covered and nothing else is — the promotion form's source id is the only part allowed to vary, and a body with a single written sentence in it fails.
The caller passes the body already reduced to its words: quote markers, emphasis markers and whitespace runs removed.
func MalformedMarkerOrdinals ¶ added in v0.7.0
func MalformedMarkerOrdinals(conds []ScopeCondition) []int
MalformedMarkerOrdinals returns the 1-based positions of bullets carrying a near-miss of an identity marker.
func MultiplyMarkedConditions ¶ added in v0.7.0
func MultiplyMarkedConditions(conds []ScopeCondition) []int
MultiplyMarkedConditions returns the 1-based positions of bullets carrying more than one identity.
func ParseGrounds ¶ added in v0.7.0
ParseGrounds reads an intent record's recorded grounds, in the order they were written — core/grounds's section reader, held to the SUBSTANCE FLOOR.
The floor applies on this side and not on the ledger's: the value it judges was written by this package's own writer, which validated it, whereas a wontfix stamps its grounds from a reason whose contract is merely non-empty. The readiness gate CLAIMS the floor, so a reader that did not apply it would let `- pursued: yes` satisfy a check that was then enforcing only a colon (iss-2608300930057882).
It reads the record BODY, which is the scope the writer appends into. Asked of the whole file it would match a frontmatter `# Grounds` comment — a legal YAML comment the block parser skips and an ATX heading pattern matches — as the section, and report an empty pseudo-section about a record whose body carries its entries (iss-2608301805069999). Callers pass the whole record: a bare body passes through grounds.Body whole only while it does not open with a thematic break, which Body reads as a frontmatter opener.
func PickEntryText ¶ added in v0.12.0
PickEntryText is the reason the pick writes onto the chosen intent: the marker, every candidate with its score, the rule, the runner-up and why it lost, and the falsifier.
func PickLess ¶ added in v0.12.0
func PickLess(a, b PickCandidate) bool
PickLess is the pick order, the one statement of it: the readiest first, the oldest among equals.
func PickOrder ¶ added in v0.12.0
func PickOrder(c []PickCandidate)
PickOrder sorts candidates into the pick order, in place.
func PrepareGrounds ¶ added in v0.12.0
PrepareGrounds is the entry RecordGrounds appends for g in repoRoot: the text redacted, then re-validated on the redacted text, the token against the closed set and the text against the substance floor. A redaction that emptied the text, or a token no caller checked, is refused here with nothing written. It also returns how many spans the redactor rewrote.
It is exported so a caller that must recognise a record this writer produced can rebuild the exact entry (grounds.AppendToRecord over the record it was appended to) and compare bytes, rather than judge the result by its shape.
func ReEmitCommand ¶ added in v0.11.1
ReEmitCommand is the command that re-emits a shipped intent's review request.
func ReadConsistencyFindings ¶ added in v0.11.1
ReadConsistencyFindings reads a findings file the way the Role 1 ingest reads a verdict (guarded, capped), for a front door that reports from the payload.
func ReadPrepassFindings ¶ added in v0.12.0
ReadPrepassFindings reads the host's findings file for WritePrepassBrief: a regular file, never a symlink or a device, within the findings cap. The bytes stay untrusted until WritePrepassBrief validates them.
func ReadVerdict ¶ added in v0.11.0
ReadVerdict reads a verdict file the way IngestVerdict does (guarded, capped), for a front door that needs the payload itself as well as its ingest.
func SetAuditLedger ¶ added in v0.13.0
func SetAuditLedger(file func(AuditOwed) (AuditFiling, error), clear func(AuditCleared) error)
SetAuditLedger registers the ledger's filer and resolver for owed audit checks, for the package that owns the ledger to call once, from init. In a binary that links no ledger both stay nil: the flag is still written, and says that no issue carries it.
func SetLedgerLock ¶ added in v0.11.1
SetLedgerLock registers the issue ledger's lock, for the package that owns it to call once, from init.
func SetProseCitationGate ¶ added in v0.11.1
func SetProseCitationGate(fn func(repoRoot, rel, text string) ([]UnresolvedCitation, error))
SetProseCitationGate registers the prose-citation gate every host-prose ingest asks before it writes: the verdict ingest here, and the consistency and reading ingests in the ledger through UnresolvedProseCitations. One registration serves them all, so a front door cannot arm one and miss another. Pass an adapter over lint.UnresolvedProseCitationsInRecord.
func SupersessionChainOf ¶ added in v0.12.0
func SupersessionChainOf(repoRoot string, links []ChainLink, id string) (chain []string, endID, endBucket, problem string, err error)
SupersessionChainOf follows id along `superseded_by` through links to the record the chain ends at, through the same walk the build's blocked check uses (followBlocker), so every reader of a supersession chain resolves it one way. chain names every record visited, id first. endID and endBucket name the record the chain ends at: an intent and the bucket it sits in, or a decision and its status, which is `accepted` (ruling CF1 of 2026-09-30: an accepted decision settles the chain). problem is non-empty when the chain cannot be finished (a loop, a record links does not hold, a superseded record naming no successor, a successor naming neither an intent nor a decision, or a decision this checkout does not hold or has not accepted), and then endID and endBucket are empty. repoRoot is the checkout whose decision store a chain ending at a decision is read from; err is a fault in reading that store, an ADR id two files claim included.
record-lint's stale_edge rule takes it through lint.SetSupersessionChain to name the live successor of a superseded record an intent's builds_on or blocked_by still names.
func Targets ¶ added in v0.12.0
func Targets(repoRoot string) ([]launch.TargetedIntent, error)
Targets lists every planned intent carrying a target, sorted by id number: the list `launch --dry-run` and the cut report (criterion 2). A value that is not a legal target is listed with the reason, never dropped — the report says what the record says, and the record lint is what refuses it.
func UnmarkedConditionOrdinals ¶ added in v0.7.0
func UnmarkedConditionOrdinals(conds []ScopeCondition) []int
UnmarkedConditionOrdinals returns the 1-based positions of the conditions no `Plan` run has stamped yet.
func Validate ¶
Validate enforces the id regex — the fail-closed guard Load runs before trusting a record's id in a filesystem path.
func WithLedgerThenMintLock ¶ added in v0.11.1
func WithLedgerThenMintLock(repoRoot string, ledger func(func() error) error, fn func() error) error
WithLedgerThenMintLock runs fn holding the record stores' three locks in their one total order: the issue ledger's lock (taken through ledger), then the intent store's, then the spec store's (spec.WithStoreLock's). Every path holding more than one of them takes them in that order and none takes a later one and then an earlier one: this package never takes the ledger lock inside its own (it cannot import the ledger's package; capture registers the lock, SetLedgerLock), plan mints its spec inside the intent lock, and the spec package imports neither of the others. It is the one acquisition for a writer of records in more than one store — a link repoint (intent's repointUnderLock, capture's repointMovedIssue), capture's migration of the promote join's back-edge, a lifeboat embark — and a writer of specs outside the spec package takes the spec lock through it (iss-2609262218309668).
It never holds an earlier lock while it WAITS for a later one (iss-2609262218059995). Waiting inside the hold chained two budgets: an intent hold of five seconds made a third process's ledger writer fail with contention while this caller waited on. So each attempt asks for the intent lock and then the spec lock only briefly (pairIntentTry each), and on contention for either lets every lock it holds go, rests, and tries again, until mintLockTimeout; past it the call returns an error naming the lock it last found busy and fn has not run. fn runs exactly once, with every lock held.
A store with no directory contributes no lock, as WithMintLock and spec.WithStoreLock take none there: taking one would plant the store, and with no store there is no record in it to race. With neither the intent nor the spec store, fn runs under the ledger lock alone.
None of the three is reentrant, so fn must not call a writer that takes any of them.
func WithMintLock ¶ added in v0.11.1
WithMintLock runs fn while holding the intent store's lock — the one withIntentMintLock takes, not a second one — for a caller OUTSIDE this package that rewrites intent records and no ledger record. A caller that rewrites both — the link repoint after a ledger record moves, capture's migration of the promote join's back-edge (iss-2609261254247117, iss-2609261941039204) — takes WithLedgerThenMintLock instead, which never holds the ledger lock while it waits for this one. Every intent writer here reads and writes under this lock, so a caller writing an intent record without it can erase an edit landing between its read and its write.
A tree with no intent store runs fn WITHOUT the lock: taking it creates the store, and a verb that writes no intent must not plant an empty one — the verdict ingest makes the same refusal without the lock for the same reason. With no store there is no intent record for fn to race.
It is NOT reentrant — an flock blocks a second acquisition in the same process until the timeout — so a caller must not hold it across any exported verb of this package that writes, every one of which takes it internally.
Lock order: the capture ledger lock, THEN this one, THEN the spec store's (spec.WithStoreLock). capture takes this lock inside its ledger lock, plan mints its spec inside this one, and nothing may take them the other way round. This package cannot take the ledger lock at all (capture imports it, so it cannot import capture), and the spec package imports neither, which is what keeps the order one-way inside the core.
Types ¶
type AuditCleared ¶ added in v0.13.0
AuditCleared asks the ledger to resolve the open issues carrying the owed check of a receipt a passing audit judged: IssueID, the one the flag names (empty when the receipt carries no flag), and every other open carrier.
type AuditEmitOptions ¶ added in v0.11.0
type AuditEmitOptions struct {
// RoutingSection is the request block's routing section
// (itd-2609170822093401): the tier and fan-out bound the host is asked to
// run the auditor at, rendered by the front door from the resolved route.
// It lands after the provenance block, outside the hashed prompt, so the
// verdict's prompt_hash does not move with the machine's routing.
RoutingSection string
}
AuditEmitOptions carries what a front door adds to an emitted request.
type AuditEmitResult ¶
type AuditEmitResult struct {
ReceiptID string `json:"receipt_id"`
IntentID string `json:"intent_id"`
Status string `json:"status"` // owed | already_owed | check_owed | already_ingested | already_dead_letter
RequestPath string `json:"request_path,omitempty"`
RequestWritten bool `json:"request_written"`
}
AuditEmitResult reports one emit (OWED stub + request file).
Status names the receipt's state and RequestWritten names the act, because the two differ: an emit on a receipt already OWED rewrites its request, and reported only already_owed, which a caller read as "nothing happened" (iss-2609190337598356). RequestPath is the request this emit wrote, so a terminal receipt — whose emit writes nothing — names none.
func ReEmitAudit ¶
func ReEmitAudit(repoRoot, intentID string) (AuditEmitResult, error)
ReEmitAudit handles the manual `abcd intent audit <itd-N>` verb for a shipped intent. It resolves the intent, refuses one not in shipped/, and delegates to emitAuditForIntent. Behaviour depends on the intent's current review state: an OWED receipt (or none) (re-)parks the OWED stub and rewrites its ephemeral request; a TERMINAL receipt is not re-reviewed — an already-INGESTED or already-DEAD_LETTER receipt returns that status unchanged (re-reviewing would discard the recorded audit), so the caller learns the review is already resolved rather than silently receiving a fresh stub.
func ReEmitAuditWith ¶ added in v0.11.0
func ReEmitAuditWith(repoRoot, intentID string, opts AuditEmitOptions) (AuditEmitResult, error)
ReEmitAuditWith is ReEmitAudit with what the front door adds to the request.
type AuditFiling ¶ added in v0.13.0
AuditFiling is the ledger's answer to an owed check: the issue carrying it, and whether that issue was already open (linked) rather than filed now.
type AuditOwed ¶ added in v0.13.0
type AuditOwed struct {
RepoRoot string
IntentID string
IntentPath string // repo-relative, slash-separated
ReceiptID string
Criteria []OwedCriterion
// Failed is true when a criterion is NOT_MET, false when every owed one is
// INCONCLUSIVE.
Failed bool
}
AuditOwed is the check an after-merge audit leaves owed on a shipped intent.
type BundleMemberClose ¶ added in v0.11.1
type BundleMemberClose struct {
Intent Intent `json:"intent"`
Moved bool `json:"moved"`
From string `json:"from"`
To string `json:"to"`
ReceiptID string `json:"receipt_id,omitempty"`
ReceiptStatus string `json:"receipt_status,omitempty"`
AuditEmitError string `json:"audit_emit_error,omitempty"`
}
BundleMemberClose is one member of a bundle as its shared spec's close left it: where it was and is, whether this call moved it, and the fidelity-review receipt its ship parked — review runs per member, against the one delivery.
type BundleOptions ¶ added in v0.11.1
type BundleOptions struct {
// Bundle is the bundle's name, required: the person planning names it
// (decision 2). It becomes every member's `bundle:` value and the shared
// spec's slug.
Bundle string
// ProductionMode is the disclosure the minted spec carries, as on Plan.
ProductionMode string
// Impact is the judgement stamped onto EVERY member, under the rules Plan
// applies to one; empty stamps nothing.
Impact string
}
BundleOptions parameterises PlanBundle.
type BundleResult ¶ added in v0.11.1
type BundleResult struct {
Bundle string `json:"bundle"`
Spec spec.Spec `json:"spec"`
Members []PlanResult `json:"members"`
Relinked []relink.Rewrite `json:"relinked,omitempty"`
RelinkError string `json:"relink_error,omitempty"`
}
BundleResult reports a completed PlanBundle: the bundle's name, the ONE spec minted for it, and each member as Plan would report it.
func PlanBundle ¶ added in v0.11.1
func PlanBundle(repoRoot string, ids []string, opts BundleOptions) (BundleResult, error)
PlanBundle plans several drafts as one bundle (criterion 1): it refuses a member naming another in `blocked_by`, naming the edge, mints ONE spec whose `intents:` lists every member, stamps `kind: bundle-member` and the bundle's name on each, stamps their scope conditions, links each `spec_id` to the shared spec, and moves them all drafts/ → planned/ together. Any refusal leaves every member where it was and nothing minted.
type ChainLink ¶ added in v0.12.0
type ChainLink = struct{ ID, Bucket, SupersededBy string }
ChainLink is one intent as a supersession chain reads it: its id, the bucket it sits in, and the successor its `superseded_by` names. It is an alias of an unnamed struct so a package this one cannot import (core/lint, whose tests import this package) can name the identical type and take SupersessionChainOf as a registered function without an adapter.
type ClaimState ¶ added in v0.7.0
type ClaimState string
ClaimState is the byte state of a claim section — the three states the gradient refuses to collapse, plus the ordinary one. An ABSENT section is a claim not carried; an EMPTY section is a gate fault; the NULLITY token is a claim considered and declined.
const ( ClaimAbsent ClaimState = "absent" ClaimEmpty ClaimState = "empty" ClaimNullity ClaimState = "nullity" ClaimStated ClaimState = "stated" )
type Claims ¶ added in v0.7.0
type Claims struct {
Mechanism ClaimState `json:"mechanism"`
MechanismPrompt bool `json:"mechanism_prompt"`
ConditionsState ClaimState `json:"conditions_state"`
ConditionsPrompt bool `json:"conditions_prompt"`
Conditions []ScopeCondition `json:"conditions"` // populated only when stated
// ConditionsFenced and ConditionsDuplicated are structural faults in the
// `## Scope Conditions` section that make its bullets unsafe to WRITE: a
// fenced example a stamp could land inside, and a second heading that makes
// "the section" ambiguous. Both are reported by the gate and refused by the
// stamp, so the remedy the gate names is never a command that cannot run.
ConditionsFenced bool `json:"conditions_fenced,omitempty"`
ConditionsCommented bool `json:"conditions_commented,omitempty"`
ConditionsDuplicated bool `json:"conditions_duplicated,omitempty"`
}
Claims is what one intent record says about its mechanism and context claims.
The two Prompt flags mark a section still holding the create-path scaffold: bytes that ASK for a claim. They read as ClaimStated because that is what they are on disk, and the flag is what stops a gate reporting an unanswered question as an answer (iss-2608300210588414).
type ConditionRequest ¶ added in v0.11.0
type ConditionRequest struct {
IntentID string
ConditionID string
Disposition string
Narrowing string
OccasionedBy string
Grounds string
// Date is the block's date, YYYY-MM-DD; empty means today in UTC.
Date string
}
ConditionRequest is one write of the condition verb.
type ConditionResult ¶ added in v0.11.0
type ConditionResult struct {
IntentID string `json:"intent_id"`
ConditionID string `json:"condition_id"`
Disposition string `json:"disposition"`
Narrowing string `json:"narrowing,omitempty"`
OccasionedBy string `json:"occasioned_by"`
Grounds string `json:"grounds"`
Date string `json:"date"`
Path string `json:"path"`
Standing []StandingEntry `json:"standing"`
OccasionCitation *OccasionCitation `json:"occasion_citation,omitempty"`
Redacted int `json:"redacted,omitempty"`
}
ConditionResult is the outcome of one write.
func DispositionCondition ¶ added in v0.11.0
func DispositionCondition(repoRoot string, req ConditionRequest) (ConditionResult, error)
DispositionCondition writes one condition disposition against a shipped intent. Every refusal happens before anything is written, in the order the spec states: the intent's id, presence and bucket; the condition's identity, presence and uniqueness; the value; the grounds; the narrowing; the occasion.
type ConditionStandingView ¶ added in v0.11.0
type ConditionStandingView struct {
IntentID string `json:"intent_id"`
Path string `json:"path"`
Dispositions []condition.Disposition `json:"dispositions"`
Standing []StandingEntry `json:"standing"`
}
ConditionStandingView is the read form: every disposition the record carries, in document order, and each condition's standing.
func ConditionStanding ¶ added in v0.11.0
func ConditionStanding(repoRoot, intentID string) (ConditionStandingView, error)
ConditionStanding reads an intent's condition dispositions. It writes nothing and refuses no bucket: reading a record is not dispositioning it.
type ConsistencyCheck ¶ added in v0.11.1
type ConsistencyCheck func(f ConsistencyFinding, reportRel string) error
ConsistencyCheck holds one finding, as the filer would write it, to the record gate the filed record must pass, and refuses it with nothing written. It is asked of EVERY finding before the first is filed, so a refusal leaves the ledger as it was. The filer's caller supplies it beside the filer, because only the ledger knows the text a finding is filed as (iss-2609261835118276).
type ConsistencyEmitOptions ¶ added in v0.11.1
type ConsistencyEmitOptions struct {
// RoutingSection is the request's routing section, as Role 1's request
// carries it: after the provenance block, outside the hashed prompt.
RoutingSection string
}
ConsistencyEmitOptions carries what a front door adds to the request.
type ConsistencyEmitResult ¶ added in v0.11.1
type ConsistencyEmitResult struct {
Status string `json:"status"` // issued
ReceiptID string `json:"receipt_id"`
Scope string `json:"scope"`
RequestPath string `json:"request_path"`
CorpusPath string `json:"corpus_path"`
ReviewOfCommit string `json:"review_of_commit"`
CorpusDigest string `json:"corpus_digest"`
Documents int `json:"documents"`
BriefDocuments int `json:"brief_documents"`
IntentDocuments int `json:"intent_documents"`
// Dirty is true when a corpus document differs from ReviewOfCommit: the pass
// reads the working tree, so the report is marked rather than refused
// (itd-28's dirty-tree policy for a review pin). DirtyPaths names them.
Dirty bool `json:"dirty"`
DirtyPaths []string `json:"dirty_paths"`
}
ConsistencyEmitResult reports one emit.
func EmitConsistency ¶ added in v0.11.1
func EmitConsistency(repoRoot, intentID string, opts ConsistencyEmitOptions) (ConsistencyEmitResult, error)
EmitConsistency assembles the corpus for one scope — the whole corpus when intentID is empty, else that intent against it — and writes the request and the assembled input under the local tier. It writes nothing else.
type ConsistencyEnd ¶ added in v0.11.1
type ConsistencyEnd struct {
Path string `json:"path"`
Line int `json:"line"`
Quote string `json:"quote"`
IntentID string `json:"intent_id,omitempty"`
}
ConsistencyEnd is one located end of a finding. Quote is the reviewer's quotation made single-line and inert; Line is where the binary found it.
type ConsistencyFiler ¶ added in v0.11.1
type ConsistencyFiler func(f ConsistencyFinding, reportRel string) (ConsistencyFiling, error)
ConsistencyFiler files one finding in the ledger, or names the open record that already holds it. reportRel is the report the finding is evidenced by. The intent store cannot reach the ledger's core (the ledger reads intents), so the caller supplies it.
type ConsistencyFiling ¶ added in v0.11.1
type ConsistencyFiling struct {
IssueID string `json:"issue_id"`
Linked bool `json:"linked"`
// Match is the filing-time match's outcome (itd-2609212137116617) on a
// record the ledger filed, when the ingest asked for one.
Match *match.Outcome `json:"match,omitempty"`
}
ConsistencyFiling is the ledger's answer for one finding: the record filed for it, or the open record that already held it (Linked).
type ConsistencyFinding ¶ added in v0.11.1
type ConsistencyFinding struct {
Number int `json:"number"`
Class string `json:"class"`
Severity string `json:"severity"`
Summary string `json:"summary"`
Explanation string `json:"explanation"`
Ends [2]ConsistencyEnd `json:"ends"`
}
ConsistencyFinding is one validated finding. Every text field is the reviewer's prose made single-line and inert (oneLine); it is not yet redacted — each writer redacts on its own write.
func (ConsistencyFinding) ClassLabel ¶ added in v0.11.1
func (f ConsistencyFinding) ClassLabel() string
ClassLabel is the class as prose.
func (ConsistencyFinding) IntentIDs ¶ added in v0.11.1
func (f ConsistencyFinding) IntentIDs() []string
IntentIDs is the intents the finding's ends sit in, deduplicated, in end order.
type ConsistencyIngestRequest ¶ added in v0.11.1
type ConsistencyIngestRequest struct {
RepoRoot string
Payload []byte
// Date is the report's date, YYYY-MM-DD; empty is today in UTC.
Date string
File ConsistencyFiler
Check ConsistencyCheck
}
ConsistencyIngestRequest is one ingest.
type ConsistencyIngestResult ¶ added in v0.11.1
type ConsistencyIngestResult struct {
Status string `json:"status"` // ingested | noop
ReceiptID string `json:"receipt_id"`
Scope string `json:"scope"`
ReportPath string `json:"report_path"`
ReviewOfCommit string `json:"review_of_commit"`
Dirty bool `json:"dirty"`
Findings int `json:"findings"`
Filed []string `json:"filed"`
Linked []string `json:"linked"`
Rows []ConsistencyRow `json:"rows"`
}
ConsistencyIngestResult reports one ingest.
func IngestConsistency ¶ added in v0.11.1
func IngestConsistency(req ConsistencyIngestRequest) (ConsistencyIngestResult, error)
IngestConsistency validates the payload against the issued request and the corpus as it stands, then files the findings and writes the report. Nothing is written unless the whole payload validates. A payload already ingested — the same receipt and the same bytes, found on the shelf — is a noop naming the report that holds it.
type ConsistencyRow ¶ added in v0.11.1
type ConsistencyRow struct {
ConsistencyFinding
IssueID string `json:"issue_id"`
Linked bool `json:"linked"`
Match *match.Outcome `json:"match,omitempty"`
}
ConsistencyRow is one finding as the report and the result carry it.
type Corpus ¶
type Corpus struct {
Intents []Intent `json:"intents"`
}
Corpus is the in-memory set of intent records discovered across every bucket.
func Load ¶
Load discovers intent files across every lifecycle bucket, parses their frontmatter, and returns the in-memory Corpus. A missing intents/ directory (or a missing individual bucket) yields no records for it (soft, mirroring spec.Load). A present-but-malformed intent file — one whose frontmatter lacks a well-formed id — is a hard, loud error.
func (Corpus) Lookup ¶
Lookup returns the intent with the given id; ok is false when absent.
Matching is CANONICAL (recordid.SameID) after an exact hit fails, the same two-pass shape spec.Store.Lookup uses and for the same reason: record-lint resolves an intent handle on its number with its leading zeros trimmed, so a link written `itd-007` is green and names itd-7. A literal-only compare here made this verb refuse — "itd-007 not found in any bucket" — a record the lint says exists, which left the spec carrying that spelling permanently unclosable and its intent permanently unlinkable. An exact match still wins when the corpus holds one, so a caller naming a record precisely gets that record.
type Created ¶ added in v0.11.1
Created is a quoted-text create's result: the draft, and the match's outcome when one was asked for.
func CreateFromTextMatched ¶ added in v0.11.1
func CreateFromTextMatched(repoRoot, text string, opts TextOptions, m *Matcher) (Created, error)
CreateFromTextMatched is CreateFromText with the filing-time match: the new draft's title and press release are compared with the candidates under the mint lock, and each likely double is written onto the draft as a `duplicates:` or `refines:` link. The match never refuses the create: a candidate set that cannot be read is reported on the outcome and the draft is written unlinked. A nil matcher is CreateFromText exactly.
type DeliveryAudit ¶ added in v0.12.0
type DeliveryAudit struct {
// ReceiptID is the receipt the close parks for the same record.
ReceiptID string
IntentID string
// ShippedPath is where the close moves the intent, the path the prompt names.
ShippedPath string
// Specs are the specs the delivery realises once the closing spec is closed.
Specs []string
// Criteria is the number of Acceptance Criteria the verdict judges.
Criteria int
// RubricHash and PromptHash are the provenance the auditor echoes.
RubricHash, PromptHash string
// contains filtered or unexported fields
}
DeliveryAudit is one fidelity request issued before the close.
func ComposeDeliveryAudit ¶ added in v0.12.0
func ComposeDeliveryAudit(intentID, plannedRel, content string, realised []string) (DeliveryAudit, error)
ComposeDeliveryAudit composes the fidelity request for intentID before its close. plannedRel is the intent's repository-relative path in planned/ and content its bytes as the delivery leaves them; realised are the specs closed once the delivery's last open spec closes, in the order the spec store lists them. The intent's id and spec_id are read from content, as the close reads them: the receipt is keyed on those. An intent whose content names another id, a spec_id that is not one, an intent outside planned/, one with no criteria to judge, and an empty realised list are refused.
func (DeliveryAudit) Check ¶ added in v0.12.0
func (a DeliveryAudit) Check(raw []byte) (DeliveryVerdict, error)
Check validates a verdict the auditor returned against this request, as the ingest does: the strict schema, every criterion judged once with cited evidence, every scope condition disposed, and the two policy hashes the ones this request issued. It returns what the loop records.
func (DeliveryAudit) Request ¶ added in v0.12.0
func (a DeliveryAudit) Request(delivered string) string
Request renders the request the auditor is handed: the prompt the close's emit composes, its Provenance block, and the delivered range, which the prompt leaves to the host to supply.
type DeliveryVerdict ¶ added in v0.12.0
type DeliveryVerdict struct {
// Rollup is the verdict's acceptance rollup.
Rollup map[string]int
// Worst is the worst acceptance verdict any criterion carries, in the order
// NOT_MET, INCONCLUSIVE, MET_WITH_CONCERNS, MET.
Worst string
// NotMet and Inconclusive name the criteria carrying those verdicts.
NotMet, Inconclusive []string
}
DeliveryVerdict is what the loop reads from a verdict the audit accepted.
type DraftOptions ¶
type DraftOptions struct {
Slug string
Title string
PressRelease string
SeedBody string
Impact string
RelatedIssue string
// Origin is the draft's arrival path (itd-178), PARSED rather than named: a
// caller declaring the reading kind hands over the run and the item in the
// same field, so the pointer cannot be forgotten at a call site. It is DERIVED
// from which command ran, never carried as free text — the zero value means
// the default (text written directly, not derived), the issue route of capture.Promote
// passes extracted-from-record, and its reading route passes
// contributed-by-reading with the pair it read out of the readings store.
Origin provenance.Origin
// ProductionMode is how the seed text was produced. Empty takes
// provenance.DefaultMode, so a draft written through a command carries the key
// whatever the caller says.
ProductionMode string
// Match, when non-nil, matches the draft's title and press release (or the
// matcher's own Text) against the record under the mint lock and writes
// each likely double as a typed link (match.go). The issue promote route
// passes none; the reading-item route passes one on request (ruling DQ2b),
// comparing the item's finding.
Match *Matcher
}
DraftOptions parameterises CreateDraft: the explicit slug and title, the prose that seeds the two narrative sections, an optional impact judgement, and — on the promote path (spc-24) — the record the draft graduated from, written as the related_issues back-edge (an iss-N, or the rdi-N of a dispositioned reading item).
The two prose members are per-section, and each is optional on its own: PressRelease seeds `## Press Release` as prose (the quoted-text route, whose text is the press release) and, when empty, that section takes the route's seed note; SeedBody seeds `## Why This Matters` (the promote route's by-id pointer) and, when empty, that section takes WhyThisMattersPrompt. A draft with neither carries no prose at all and is refused.
type DrainStep ¶ added in v0.11.1
type DrainStep struct {
ReviewQueue
Next *AuditEmitResult `json:"next,omitempty"`
}
DrainStep is one step of the drain: the queue, and the request for the first entry that could be emitted, re-emitted so a host can hand it to the auditor. Next is nil when nothing is owed, or when no listed entry could be emitted (each then carries its EmitError).
func NextOwedAudit ¶ added in v0.11.1
func NextOwedAudit(repoRoot string, max int, shippedOn ShippedOn, opts AuditEmitOptions) (DrainStep, error)
NextOwedAudit is OwedQueue plus the single audit's own emit on the head of the queue: the head's request is (re-)written and named, and a markerless head has its receipt minted, which the queue then reports. Only one entry is emitted; nothing runs a reviewer. The next step, after the host ingests that entry's verdict, finds the queue one shorter. A head whose emit fails keeps its error on its own entry and the next entry is tried, so a record that needs a hand fix stays listed without blocking the ones behind it. opts is what the front door adds to the request, as it adds it to a single audit's (the routing section).
type EdgeRequest ¶ added in v0.13.3
type EdgeRequest struct {
ID string // the subject itd-N, in any bucket
BlockedBy []string // itd-N ids to append to blocked_by
Unblock []string // itd-N ids to remove from blocked_by
BuildsOn []string // itd-N ids to append to builds_on
DropBuildsOn []string // itd-N ids to remove from builds_on
}
EdgeRequest edits one intent's dependency edges. At least one of the four lists must be non-empty.
type EdgeResult ¶ added in v0.13.3
type EdgeResult struct {
IntentID string `json:"intent_id"`
Path string `json:"path"`
Bucket string `json:"bucket"`
BlockedBy []string `json:"blocked_by"`
BuildsOn []string `json:"builds_on"`
}
EdgeResult is the outcome of a successful Edge: the subject, its repo-relative path and bucket, and both lists AS WRITTEN (an empty list comes back empty, never null).
func Edge ¶ added in v0.13.3
func Edge(repoRoot string, req EdgeRequest) (EdgeResult, error)
Edge adds to, or removes from, one intent's blocked_by and builds_on lists. The read, the re-check of every removal against the bytes on disk and the write are one critical section under the store's lock, as every other intent write is.
type GroundsResult ¶ added in v0.7.0
type GroundsResult struct {
IntentID string `json:"intent_id"`
Path string `json:"path"` // repo-relative intent path
Token string `json:"token"` // pursued | deferred | declined
Text string `json:"text"` // the text as WRITTEN (post-redaction)
Entries int `json:"entries"`
Redacted int `json:"redacted,omitempty"`
}
GroundsResult is the outcome of one RecordGrounds call. Redacted counts the spans the redactor rewrote before the write, so a surface can SAY the text was altered: rewriting somebody's reasoning in silence is worse than not recording it.
func RecordGrounds ¶ added in v0.7.0
func RecordGrounds(repoRoot, intentID string, g grounds.Grounds) (GroundsResult, error)
RecordGrounds appends one grounds entry to an intent record, creating the `## Grounds` section when it is absent.
Recording is APPEND-ONLY: a second gate decision adds an entry beside the first rather than replacing it, because the earlier conjecture is precisely what a later reader checks the outcome against. Rewriting it would leave the record saying only what was believed last.
The text is redacted BEFORE it is validated, never after, so no rewritten span can reach a field the validator has already passed; the redactor is the same fail-closed one the quoted-text create path uses, because a grounds text is durable committed prose. The write goes through writeIntentFile, the package's one intent-record writer.
type HoldResult ¶ added in v0.10.0
type HoldResult struct {
IntentID string `json:"intent_id"`
Path string `json:"path"` // repo-relative intent path
Bucket string `json:"bucket"` // drafts | planned
Reason string `json:"reason"`
Redacted int `json:"redacted,omitempty"`
}
HoldResult reports a completed Hold. Reason is the text as WRITTEN (post-redaction) and Redacted counts the spans the redactor rewrote before the write, so a surface can say the text was altered rather than rewriting somebody's reason in silence.
func Hold ¶ added in v0.10.0
func Hold(repoRoot, intentID, reason string) (HoldResult, error)
Hold writes `held: "<reason>"` onto a draft or planned intent.
The reason is required, non-empty after trimming, and single-line (no control character of any kind: a newline inside the quotes is a second frontmatter line to the same-line scanner every reader uses). It is redacted BEFORE it is validated, never after, through the same fail-closed redactor the quoted-text create path uses, because a hold reason is durable committed prose. A record already held is refused naming the standing reason — an updated reason is `unhold` then `hold`, so the lift is a visible act rather than a silent overwrite. A terminal record (shipped/, superseded/, disciplines/) is refused: a hold on a record nothing will plan is meaningless, and writing one would make the key stop meaning anything.
The read, the re-check and the write are ONE critical section under the store's advisory lock, as stampPlanned's stamp is: two sessions holding the same record would otherwise each write the file they read, and the later write would silently replace the earlier reason — the overwrite the already-held refusal exists to prevent.
type IngestVerdictResult ¶
type IngestVerdictResult struct {
Status string `json:"status"` // ingested | dead_letter | noop
ReceiptID string `json:"receipt_id"`
IntentID string `json:"intent_id"`
Criteria int `json:"criteria"`
Met int `json:"met"`
MetWithConcern int `json:"met_with_concerns"`
NotMet int `json:"not_met"`
Inconclusive int `json:"inconclusive"`
Conditions int `json:"conditions"`
Survived int `json:"survived"`
Narrowed int `json:"narrowed"`
Falsified int `json:"falsified"`
Untested int `json:"untested"`
DeadLetterPath string `json:"dead_letter_path,omitempty"`
Reason string `json:"reason,omitempty"`
// Replaced is set when the ingest replaced a verdict already ingested for
// the receipt: a re-ingest whose payload renders differently from the block
// on the record. A re-ingest that renders identically is a noop.
Replaced bool `json:"replaced,omitempty"`
// ReadingOccasionedStanding is every condition-block disposition the fold
// still reports as standing after this write: the verdict did not override
// it, because its rationale did not name the block's occasion
// (spc-2609020626046252). An auditor who meant to override one names its
// occasion in the rationale and ingests again for the same receipt: a
// payload that renders differently replaces the ingested block (Replaced).
ReadingOccasionedStanding []condition.Disposition `json:"reading_occasioned_standing,omitempty"`
// AuditOwed names each criterion the verdict judged NOT_MET or
// INCONCLUSIVE ("ac-2 NOT_MET"), the check the flag leaves owed on the
// shipped intent (ruling DQ1c); empty on a passing verdict.
AuditOwed []string `json:"audit_owed,omitempty"`
// OwedIssue is the issue carrying that check, and OwedIssueLinked is true
// when it was already open rather than filed by this ingest.
OwedIssue string `json:"owed_issue,omitempty"`
OwedIssueLinked bool `json:"owed_issue_linked,omitempty"`
// FlagCleared is the issue a passing re-audit resolved as it cleared the
// flag.
FlagCleared string `json:"flag_cleared,omitempty"`
}
IngestVerdictResult reports one verdict ingest.
func IngestVerdict ¶
func IngestVerdict(repoRoot, verdictPath string) (IngestVerdictResult, error)
IngestVerdict reads an untrusted intent-fidelity verdict JSON and applies it to the committed record, FAIL-CLOSED at every step:
- malformed/oversize/unreadable payload with no resolvable receipt -> reject;
- receipt matching no parked marker (unsolicited) -> reject;
- already INGESTED for this receipt -> no-op when the payload renders to the block on the record, a replacement in place when it renders differently, and a refusal with nothing written when it does not validate (see reingestVerdict);
- schema/semantic validation failure on a resolvable receipt -> DEAD_LETTER (marker + INCONCLUSIVE criteria + retained raw payload), never partial;
- a rendered block citing a record id the repository's record gate refuses -> reject, nothing written (checkReviewCitations);
- otherwise -> INGESTED (OWED stub replaced by the rendered verdict).
func IngestVerdictBytes ¶ added in v0.11.0
func IngestVerdictBytes(repoRoot string, raw []byte) (IngestVerdictResult, error)
IngestVerdictBytes is IngestVerdict over a payload a front door has already read through ReadVerdict. The front door reads the verdict once and hands the same bytes to the ingest and to whatever else it reports from the payload (the receipt's model_reported), so the two can never describe different reads of a file that changed between them.
func (IngestVerdictResult) MarshalJSON ¶ added in v0.11.1
func (r IngestVerdictResult) MarshalJSON() ([]byte, error)
MarshalJSON writes the result the way its outcome reads (iss-2609190337545165). One struct serves every status, so its counters are zero-valued members on a quarantine and a noop, and a reader took a dead letter's "criteria: 0" beside the conditions it recorded untested for a rollup. The JSON therefore states what the ingest recorded — `verdict` (ingested), `quarantine` (dead_letter) or `nothing` (noop) — and carries the rollup only beside a recorded verdict, where a zero is a count. A quarantine states the one split it did record, every scope condition untested, under a name of its own.
type Intent ¶
type Intent struct {
ID string `json:"id"` // itd-N
Slug string `json:"slug"` // kebab-case
Kind string `json:"kind"` // standalone | bundle-member | discipline | null
SpecID string `json:"spec_id"` // spc-N, the derived link (may be null)
Bucket string `json:"bucket"` // lifecycle directory (directory-as-truth)
Path string `json:"path"` // repo-relative markdown path
// RelatedIssues are the ledger records this intent graduated from — an iss-N,
// or the rdi-N of a dispositioned reading item — the intent half of the
// two-sided promote join (itd-4 AC3; the record half is the source's
// `related_intents`). The first entry is the record the intent was promoted
// from; a later one was linked beside it. Parsed leniently: absent on every
// record that graduated from nothing.
RelatedIssues []string `json:"related_issues,omitempty"`
// Held is the reason the record is held — the value `abcd intent hold`
// wrote — and empty when it is not. HeldMalformed reports a `held:` key
// present in a shape no verb writes (blank, null, a list, a map, a block
// scalar): the loader stays lenient so one hand edit cannot fail-close the
// whole corpus, and record-lint is what names the line. See hold.go for the
// trust boundary between the two.
Held string `json:"held,omitempty"`
HeldMalformed bool `json:"held_malformed,omitempty"`
// Bundle is the bundle a bundle-member names in its `bundle:` field, and
// empty when the record names none (itd-34).
Bundle string `json:"bundle,omitempty"`
// TargetRelease is the release the intent must land by, as its
// `target_release:` carries it, and empty when it names none
// (itd-2609212103572513).
TargetRelease string `json:"target_release,omitempty"`
// SupersededBy is the successor a superseded record names in its
// `superseded_by:`, an intent (itd-M) or a decision (adr-N), and empty
// when it names none. The build's blocked check follows it (ruling BZ2 of
// 2026-09-29).
SupersededBy string `json:"superseded_by,omitempty"`
}
Intent is one intent record. Bucket is the directory it was found in; Path is repo-relative (never an absolute local path).
func AddRelatedIssue ¶ added in v0.10.0
AddRelatedIssue appends source to an existing intent's `related_issues`, in any bucket. It is the intent half of link mode: `capture promote <iss-N|rdi-N> --intent <itd-N>` stamps the source's `related_intents` and this writes the edge pointing back, so the join reads from both ends.
It writes that one key and NOTHING else. It never reads or rewrites `origin` or `production_mode`, which is what "the origin is unchanged" rests on: an origin is stamped at mint and never rewritten, so a draft filed from quoted text and linked to a reading item stays researcher-authored and says so.
A list already naming source is a no-op that leaves the record byte-identical. A list naming OTHER records keeps them, in order, and appends source: an intent occasioned by several items is promoted from the first and joined to the rest (itd-2609020625400169, first scope condition), so nothing is overwritten.
func CreateDraft ¶
func CreateDraft(repoRoot string, opts DraftOptions) (Intent, error)
CreateDraft is the one canonical draft-mint primitive: both the quoted-text create (CreateFromText) and the capture-promote path (capture.Promote, which supplies an explicit slug and seed body) route through it, so a draft can never be minted outside the store mint lock. It validates every option at the boundary — the slug becomes a filename — mints a native timestamp-numeric itd id through the shared recordid seam, and atomically writes drafts/itd-N-<slug>.md. On any refusal nothing is written.
func CreateDraftMatched ¶ added in v0.12.0
CreateDraftMatched is CreateDraft returning the filing-time match's outcome too, nil when opts asked for none.
func CreateFromText ¶
func CreateFromText(repoRoot, text string, opts TextOptions) (Intent, error)
CreateFromText files a new draft intent seeded from free-form text, mirroring the capture engine's create shape: it derives a filename-safe slug, mints a native timestamp-numeric itd id under the store mint lock, and atomically writes drafts/itd-N-<slug>.md with the canonical draft frontmatter set and a minimal, honest body skeleton. Empty/whitespace text is refused and nothing is written.
The text IS the press release: the whole of it seeds `## Press Release` as prose, and the H1 is its first sentence (deriveTitle), or opts.Title when the caller gives one. Neither is pasted a second time under `## Why This Matters`, which takes a prompt for its own content instead (iss-2609170726360399: a paragraph as a heading is unreadable, the slug truncates it, and a draft whose one press-release-first section is a placeholder reads as malformed on sight).
The seeded record is lint-valid (intent_lifecycle accepts a draft whose kind is null and whose spec_id is null) and passes Validate; a human expands it, then `abcd intent plan` schedules it. This is the quoted-text create path itd-46 delivers — the create half of what spc-6 AC3 (promote) needs.
type IntentListing ¶ added in v0.11.1
type IntentListing struct {
ID string `json:"id"`
Title string `json:"title"`
Bucket string `json:"bucket"`
// ACState is ACStateReal or ACStateSeeded, judged by the bar plan applies.
ACState string `json:"ac_state"`
// Filed is the date a timestamp id encodes (adr-45), as YYYY-MM-DD, and
// null for an ordinal id, which encodes none: the view reads no git history.
Filed *string `json:"filed"`
}
IntentListing is one intent as the status view lists it.
func Listing ¶ added in v0.12.0
func Listing(repoRoot string, it Intent) (IntentListing, error)
Listing is one intent's row of the status listing, read alone: a caller that needs the titles of a few intents it already holds reads them here rather than through Status, which also judges every shipped intent's review.
type LinkResult ¶
LinkResult reports a completed Link: the updated intent and the spec it now declares.
func Link ¶
func Link(repoRoot, intentID, specID string) (LinkResult, error)
Link retroactively writes the derived spec_id link on an existing planned intent for an existing spec. It validates both ids, that the intent is in planned/, and that the spec exists AND already declares this intent (the reciprocal intent: itd-N side); a spec that realises a different intent is a mismatch and fails closed rather than forging a one-sided link.
type LinkedPair ¶
LinkedPair is one intent↔spec link in the lifecycle summary.
type MatchText ¶ added in v0.11.1
MatchText is one intent's comparable text: its H1 and its press release. A press release that is still the seed note a create wrote is left out, since every promoted draft carries the same words there.
func MatchTexts ¶ added in v0.11.1
MatchTexts reads the title and press release of every intent in every bucket. A store no reader can load is an error, which the match reports as its reason for comparing nothing, never as a refusal of the write.
type Matcher ¶ added in v0.11.1
type Matcher struct {
Threshold float64
Candidates func() ([]match.Candidate, error)
// Text, when non-empty, is the text the match compares in place of the
// draft's title and press release: the source record's own words, for a
// draft minted from a record whose title alone is too short to compare
// (a promoted reading item's pattern, ruling DQ2b).
Text string
}
Matcher is a create's request to be matched before it is written: the threshold, and the candidate set, which the caller's reader gathers and the create calls under the store's mint lock.
type OccasionCitation ¶ added in v0.11.0
type OccasionCitation struct {
Occasion string `json:"occasion"`
Cited string `json:"cited"`
Dispositioned string `json:"dispositioned"`
}
OccasionCitation reports a reading item whose constraint_in_play cites a condition identity other than the one dispositioned. It is a report, never a refusal: the item is the reading's word and the mark is the researcher's.
type OwedCriterion ¶ added in v0.13.0
OwedCriterion is one criterion an audit judged NOT_MET or INCONCLUSIVE.
type Pick ¶ added in v0.12.0
type Pick struct {
// Chosen is the candidate taken.
Chosen PickCandidate `json:"chosen"`
// RunnerUp is the next in the pick order; nil when there was one candidate.
RunnerUp *PickCandidate `json:"runner_up"`
// TieBrokenByAge is true when the runner-up scored the same as the chosen
// one, so age placed the winner.
TieBrokenByAge bool `json:"tie_broken_by_age"`
// Candidates are every candidate, in the pick order.
Candidates []PickCandidate `json:"candidates"`
Rule string `json:"rule"`
Falsifier string `json:"falsifier"`
}
Pick is a choice among candidates.
func Choose ¶ added in v0.12.0
func Choose(cands []PickCandidate) (Pick, bool)
Choose orders the candidates and takes the first. ok is false when there is none.
type PickCandidate ¶ added in v0.12.0
type PickCandidate struct {
ID string `json:"id"`
Score ReadinessScore `json:"score"`
}
PickCandidate is one intent the pick may take, with its score.
type PlanOptions ¶ added in v0.10.0
type PlanOptions struct {
// ProductionMode is the disclosure the MINTED SPEC carries (itd-178); it is
// validated by the spec store before the id is minted, and an empty value
// takes the vocabulary's default. It has no bearing on the intent record,
// whose own stamp was written when the draft was created and is never
// rewritten.
ProductionMode string
// Impact is the product-impact judgement to stamp onto the INTENT record
// (`impact: additive|breaking|fix`), the same field the create path writes
// from its own --impact and the close that ships demands. Plan is the verb
// that runs when the judgement is actually made — the planning interview
// settles the impact class — so it is where a draft filed without one gets
// it. It is validated at the bar the create and close paths apply, refused
// when it disagrees with a judgement the record already carries, and
// accepted as a no-op when it agrees; empty leaves the record unjudged.
Impact string
// Target is the release the planned intent must land by, written as
// `target_release:` in the same write that plans the draft
// (itd-2609212103572513). It is validated before anything moves; empty
// writes none.
Target string
}
PlanOptions parameterises Plan. Both fields are optional: the zero value plans a draft exactly as the bare verb does.
type PlanResult ¶
type PlanResult struct {
Intent Intent `json:"intent"`
Spec spec.Spec `json:"spec"`
// ConditionsStamped is how many scope-condition bullets this run gave an
// identity to.
ConditionsStamped int `json:"conditions_stamped"`
// StampOnly reports that this run did the identity step alone, over a record
// already in planned/: no spec was minted and no bucket moved. It is how a
// condition written after planning reaches the mint, which is what makes the
// readiness gate's remedy a command that works.
StampOnly bool `json:"stamp_only"`
// LinkedInPlace reports that this run minted (or reused) the spec for a
// record already in planned/ whose spec_id was null, and linked it without
// moving the record (iss-2609211738504433).
LinkedInPlace bool `json:"linked_in_place"`
// ImpactStamped is the impact judgement this run wrote onto the record, and
// empty when it wrote none — because no --impact was supplied, or because the
// record already carried the same value.
ImpactStamped string `json:"impact_stamped"`
// TargetStamped is the target release this run wrote onto the record
// (PlanOptions.Target), and empty when it wrote none.
TargetStamped string `json:"target_stamped,omitempty"`
// Relinked and RelinkError report the repoint of links that named the
// draft's old path, as ReconcileResult's do for a close.
Relinked []relink.Rewrite `json:"relinked,omitempty"`
RelinkError string `json:"relink_error,omitempty"`
}
PlanResult reports a completed Plan: the updated planned intent and the spec minted to realise it.
func Plan ¶
func Plan(repoRoot, intentID string, opts PlanOptions) (PlanResult, error)
Plan is the load-bearing verb. For a draft intent carrying a non-empty `## Acceptance Criteria` section, it mints a native spec, writes the intent's derived side of the bidirectional link (spec_id + a default kind), and moves the intent drafts/ → planned/.
It is fail-closed: every intermediate on-disk state satisfies the intent_lifecycle record-lint rule, so a failure at any step leaves a consistent, lint-valid record rather than a half-written link. The order — create spec, set kind while still a draft, move to planned, then write spec_id — is chosen so that (kind=standalone, spec_id=null) is the only transient frontmatter, and that shape is valid in BOTH drafts and planned.
opts carries the two optional judgements the verb takes: the production mode the MINTED SPEC carries, and the impact the INTENT record is stamped with (see PlanOptions).
type PrepassBriefResult ¶ added in v0.12.0
type PrepassBriefResult struct {
Intent string `json:"intent"`
BriefPath string `json:"brief_path"`
Conflicts int `json:"conflicts"`
Overlaps int `json:"overlaps"`
Unanchored int `json:"unanchored"`
// Demoted counts the conflicts and overlaps the host returned that could
// not be anchored, and so are among the Unanchored questions.
Demoted int `json:"demoted"`
Warnings []string `json:"warnings"`
}
PrepassBriefResult reports one written brief.
func WritePrepassBrief ¶ added in v0.12.0
func WritePrepassBrief(repoRoot, intentID string, raw []byte) (PrepassBriefResult, error)
WritePrepassBrief validates the host's findings for the draft intentID against the input as it stands now and writes the planning brief. A payload that does not validate is refused with nothing written; a brief at the path that the pre-pass did not write is never replaced.
type PrepassIndexEntry ¶ added in v0.12.0
type PrepassIndexEntry struct {
ID string `json:"id"`
Title string `json:"title"`
Shelf string `json:"shelf"`
}
PrepassIndexEntry is one line of the sibling index.
type PrepassInput ¶ added in v0.12.0
type PrepassInput struct {
Type string `json:"_type"`
Intent string `json:"intent"`
DraftPath string `json:"draft_path"`
Draft string `json:"draft"`
Invariants []PrepassInvariant `json:"invariants"`
Principles []PrepassPrinciple `json:"principles"`
Index []PrepassIndexEntry `json:"index"`
Answers []string `json:"answers"`
Rules []string `json:"rules"`
Warnings []string `json:"warnings"`
Digest string `json:"input_digest"`
}
PrepassInput is everything the host's pass reads, and nothing else.
func AssemblePrepass ¶ added in v0.12.0
func AssemblePrepass(repoRoot, intentID string) (PrepassInput, error)
AssemblePrepass reads the four inputs for the draft intentID. It refuses an id that is malformed, absent, or not on the drafts shelf; a missing invariants file or principles directory degrades the pass with a warning instead. It writes nothing.
type PrepassInvariant ¶ added in v0.12.0
type PrepassInvariant struct {
Number int `json:"number"`
Title string `json:"title"`
Text string `json:"text"`
}
PrepassInvariant is one numbered invariant, as the brief's register states it.
type PrepassPrinciple ¶ added in v0.12.0
type PrepassPrinciple struct {
Path string `json:"path"`
Title string `json:"title"`
Rule string `json:"rule"`
}
PrepassPrinciple is one principle: its path, its title and its rule paragraph (the stance, without the argument for it).
type QueuedReview ¶ added in v0.11.1
type QueuedReview struct {
ReviewEntry
Shipped string `json:"shipped,omitempty"`
ShippedState string `json:"shipped_state"`
// EmitError is why the drain step could not emit this entry's request (a
// malformed spec_id, an unreadable file); the step moves on to the next
// entry, so one bad record never blocks the drain. "" when it was not
// tried or was emitted.
EmitError string `json:"emit_error,omitempty"`
}
QueuedReview is one owed review in the drain queue: the reader's entry plus the day its intent shipped ("" when there is none to give) and which of the three facts about that day holds (ShippedState).
type ReadinessPart ¶ added in v0.12.0
ReadinessPart is one part of the score: its points and what earned them.
type ReadinessScore ¶ added in v0.12.0
type ReadinessScore struct {
Criteria ReadinessPart `json:"criteria"`
TestPath ReadinessPart `json:"test_path"`
Footprint ReadinessPart `json:"footprint"`
// Total is the weighted sum of the three parts; it orders the pick.
Total int `json:"total"`
// NoFootprint is true when the spec carries no `## Footprint` section, so
// its test-path and footprint parts read zero.
NoFootprint bool `json:"no_footprint"`
}
ReadinessScore is an intent's readiness, computed from its record and its spec's.
func ReadinessIn ¶ added in v0.12.0
func ReadinessIn(repoRoot string, store spec.Store, it Intent, specID string) (ReadinessScore, error)
ReadinessIn scores an intent the caller has already looked up, from its record and the record of specID read through a spec store the caller has already loaded. It is the one read both orderings score through, the build's pick and the status board's "next up", so the two cannot score an intent differently.
type ReadinessWeights ¶ added in v0.12.0
type ReadinessWeights struct {
Criteria int `json:"criteria"`
TestPath int `json:"test_path"`
Footprint int `json:"footprint"`
}
ReadinessWeights are the weights of the score's three parts.
type ReadyCheck ¶
type ReadyCheck struct {
Name string `json:"name"` // bucket | acceptance_criteria | mechanism_claim | scope_conditions | spec_link | spec_body | steps | grounds
OK bool `json:"ok"`
// Advisory marks a check that REPORTS and never gates: its verdict and its
// remedy are shown, and Ready ignores it. The two claim checks and the
// grounds check are advisory until the rethink of the reading work settles
// what a human is asked for at this gate (iss-2609091009111294), and the
// steps check is advisory by design (a spec with no steps is one step); the
// four structural checks are not.
Advisory bool `json:"advisory,omitempty"`
Detail string `json:"detail"` // why it passed or failed
Remedy string `json:"remedy,omitempty"` // the exact next command/action when !OK
}
ReadyCheck is one finding of the implement-readiness gate.
type ReadyResult ¶
type ReadyResult struct {
IntentID string `json:"intent_id"`
Path string `json:"path"` // repo-relative intent path
Bucket string `json:"bucket"` // directory-as-truth state
// SpecID is the spec this gate judged: the intent's own spec_id, or — when
// the intent owns more than one spec and the spec_id names a closed one — the
// open spec that realises the remainder (adr-2609151513118583).
SpecID string `json:"spec_id"`
Ready bool `json:"ready"`
Checks []ReadyCheck `json:"checks"` // always exactly 8, fixed order
// Conditions is the record's scope conditions with their minted identities —
// the observable surface the identity criteria assert against. Empty for a
// record whose conditions are absent, or recorded as the nullity token.
Conditions []ScopeCondition `json:"conditions"`
}
ReadyResult is the structured readiness verdict for one intent: may this intent be implemented now? Every check is always evaluated and reported, so a surface presents the full picture rather than the first failure.
func Ready ¶
func Ready(repoRoot, intentID string) (ReadyResult, error)
Ready reports whether an intent is ready to implement: planned (directory-as-truth), carrying enumerable Acceptance Criteria, linked bidirectionally to a spec, and that spec's body written past its minted stub. It is a read-only reporter — the machine-checkable form of the run protocol's "is this item ready?" question — and never mutates the store. The mechanism, scope-condition and grounds checks are evaluated and reported beside the four structural ones but are advisory: a failing one never withholds readiness.
"Not ready" is a result, never an error: error is reserved for structural faults (malformed id, unknown intent, unreadable record, store load failure), so a surface can map result vs error to distinct exit codes.
func ReadyIn ¶ added in v0.12.0
ReadyIn is Ready for an intent the caller has already looked up, judged against a spec store the caller has already loaded: the same eight checks and the same verdict, without reloading either store. A caller judging every planned intent (the Now / Next / Later block) loads each store once rather than once per intent. store must be spec.Load's for repoRoot.
type ReclassifyRequest ¶ added in v0.11.1
type ReclassifyRequest struct {
// Kind is the target: standalone, bundle-member, superseded (or discipline,
// which is refused with its remedy).
Kind string
// Bundle names the bundle a bundle-member target joins; required there and
// refused anywhere else.
Bundle string
// By is the successor of a superseded target, an intent (itd-M) or an ADR
// (adr-N); required there and refused anywhere else.
By string
// Reason is why, one line, redacted before it is written; required for a
// supersession and optional for a kind change.
Reason string
// Date is the history entry's date, YYYY-MM-DD; empty means today (UTC).
Date string
}
ReclassifyRequest parameterises Reclassify.
type ReclassifyResult ¶ added in v0.11.1
type ReclassifyResult struct {
IntentID string `json:"intent_id"`
FromKind string `json:"from_kind"`
ToKind string `json:"to_kind"`
Bucket string `json:"bucket"`
Path string `json:"path"`
// Moved names every record this call moved, and Written every record it
// wrote (the moved record at its new path, the successor, the survivor).
Moved []PathMove `json:"moved"`
Written []string `json:"written"`
// Successor is the record a supersession named, and Survivor the member it
// left alone in its bundle (empty when it left none, or several).
Successor string `json:"successor,omitempty"`
Survivor string `json:"survivor,omitempty"`
// OpenSpecs names the open specs that still name a record this call
// superseded: a fact to act on, not a refusal.
OpenSpecs []string `json:"open_specs,omitempty"`
Reason string `json:"reason,omitempty"`
Redacted int `json:"redacted,omitempty"`
Relinked []relink.Rewrite `json:"relinked,omitempty"`
RelinkError string `json:"relink_error,omitempty"`
}
ReclassifyResult reports a completed Reclassify.
func Reclassify ¶ added in v0.11.1
func Reclassify(repoRoot, intentID string, req ReclassifyRequest) (ReclassifyResult, error)
Reclassify changes one intent's kind, or retires it as superseded, in one write (criteria 3 and 4). See the file comment for what each shape writes and what it refuses.
type ReconcileResult ¶
type ReconcileResult struct {
Spec spec.Spec `json:"spec"`
Intent Intent `json:"intent"`
IntentMoved bool `json:"intent_moved"`
From string `json:"from"`
To string `json:"to"`
// OpenSpecs names the specs that still realise the intent after this close,
// in store order. Empty is the ordinary case and the one that ships: the
// intent moves planned/ -> shipped/ on the close after which no open spec
// names it (adr-2609151513118583). A non-empty list is the visible reason the
// intent did NOT move, and the surface prints it.
OpenSpecs []string `json:"open_specs,omitempty"`
// Remainder is the follow-on spec this close minted for the part of the
// intent the closed spec did not deliver (the zero value when none was
// asked for). It is attached to the same intent and lands in open/.
Remainder spec.Spec `json:"remainder,omitzero"`
// RemainderMinted says whether THIS invocation wrote that remainder. The mint
// is idempotent — a retry after a failure downstream of it reuses the spec the
// previous attempt left behind — so without this the surface reports a record
// it did not write as one it just wrote.
RemainderMinted bool `json:"remainder_minted,omitempty"`
// RemainderSteps are the steps of the closing spec that were not marked
// landed, which this close carried into the remainder it minted, in order
// and renumbered from one (itd-2609212103565953). Empty when the closing
// spec lists no steps, when every step it lists has landed, and when the
// remainder was reused rather than minted: a reused spec is left as found.
RemainderSteps []spec.Step `json:"remainder_steps,omitempty"`
// NeedsRewritten names each carried step's `- needs:` line the remainder
// rewrote against its own numbering, before and after.
NeedsRewritten []spec.NeedsRewrite `json:"needs_rewritten,omitempty"`
// ReceiptID is the deterministic fidelity-review receipt parked in the
// shipped intent's Audit Notes (empty if the emit failed).
ReceiptID string `json:"receipt_id,omitempty"`
// ReceiptStatus says what the emit did: "owed" on the close that actually
// parked a new OWED stub, and "already_owed"/"already_ingested"/
// "already_dead_letter" when the intent had shipped before and the receipt
// was already there. A close is idempotent, so the same receipt id comes back
// on every re-run; without this the surface announced a fresh review on each
// one, which reads as a new obligation the operator has to discharge.
ReceiptStatus string `json:"receipt_status,omitempty"`
// AuditEmitError is a NON-FATAL report of a failed review emit. The review is
// report-only, so the intent still ships; the surface prints this loudly.
AuditEmitError string `json:"audit_emit_error,omitempty"`
// Relinked lists every relative markdown link this close repointed because
// it named the old path of a record the close moved — the spec leaving
// open/, the intent leaving planned/ (iss-2609091732329046). Empty when no
// link named either.
Relinked []relink.Rewrite `json:"relinked,omitempty"`
// RelinkError is a NON-FATAL report of a repoint that failed part-way: the
// records have moved and the close stands, so the surface prints it loudly
// and a re-run of the close repoints the links other files still hold. The
// moved records' own links it leaves as written, for links_resolve to name.
RelinkError string `json:"relink_error,omitempty"`
// Members is every member a bundle's shared spec shipped (or found already
// shipped), in the order the spec lists them, and Skipped the members it
// passed over — superseded out of the bundle, or naming another — (itd-34).
// Both are empty on one intent's close; on a bundle's, Intent and the fields
// beside it describe the first member.
Members []BundleMemberClose `json:"members,omitempty"`
Skipped []string `json:"skipped,omitempty"`
}
ReconcileResult reports a completed Reconcile (the deterministic half of `abcd spec close`): the closed spec, the linked intent in its post-reconcile state, whether the intent moved this call (false on an idempotent re-run), and the intent's bucket transition (From → To).
func Reconcile ¶
func Reconcile(repoRoot, specID, impact string, remainder RemainderRequest) (ReconcileResult, error)
Reconcile is the deterministic half of `abcd spec close`: it closes a spec and, when that close was the intent's last open spec, ships the intent — so one command marks the spec done AND moves the intent exactly when the capability is whole.
An intent owns one or more specs (adr-2609151513118583, invariant 17). A spec that delivers only part of an intent is closed on its own terms while another spec still names the intent, and the intent stays in planned/; the close after which no open spec names it is the one that ships it. More than one spec naming one intent is the normal state, not an ambiguity — the question that decides the move is "does this intent have an open spec left?".
Ordering is intent-first, spec-last on the close that ships, so a partial failure is recoverable by re-running: the intent moves planned/ → shipped/ before spec.Close runs, so a failure at the move leaves the spec OPEN (retry-safe), never a closed spec with a still-planned intent. A remainder is minted before either, so a failure there moves nothing at all. It is idempotent: an already-shipped intent is not re-moved, and a re-run on an already-closed spec is a clean no-op/complete rather than an error.
It fails closed with NO partial move when: the spec has no/empty intent link; the named intent does not exist; the intent's spec_id names no spec that realises it (bidirectional drift); the intent is in an unexpected bucket (e.g. still in drafts — it was never planned); an impact is supplied at a close that ships nothing; a remainder is asked for on an already-shipped intent or an already-closed spec (neither has a delivery boundary left to split); or the intent would enter shipped/ without the impact judgement that bucket requires (see resolveShipImpact). Every id is validated against the ^spc-/^itd- regexes before any path is built. The intent's `## Audit Notes` are left untouched (the fidelity audit is a later phase; the intent ships with them empty).
impact is the judgement `abcd spec close --impact` carries: empty means "the record already carries its own", and a value is stamped onto a record that has none. It is never a silent override — see resolveShipImpact — and it is accepted only at the close that ships, because that is the only close that writes it.
remainder asks this close to mint the follow-on spec for what the closing spec did not deliver (see RemainderRequest); the zero value asks for none.
type RemainderRequest ¶ added in v0.9.0
type RemainderRequest struct {
// Slug is the kebab-case slug of the spec to mint. Empty means no remainder.
Slug string
// ProductionMode is the disclosure mode stamped on the minted spec; empty
// takes the vocabulary's default (provenance.DefaultMode).
ProductionMode string
}
RemainderRequest asks a close to mint a follow-on spec for the part of the intent the closing spec did not deliver, attached to that same intent. The zero value asks for none, which is the ordinary close.
It is how the honest path is taken in one operation: the visible state after a partial delivery is "spec closed X, spec open Y, intent still planned", and minting Y by hand afterwards is the step that gets forgotten (adr-2609151513118583).
type ReviewEntry ¶ added in v0.11.1
type ReviewEntry struct {
IntentID string `json:"intent_id"`
// State is OWED, INGESTED, DEAD_LETTER, or none.
State string `json:"state"`
// ReceiptID is the receipt the first marker names; empty for none, where the
// re-emit mints one.
ReceiptID string `json:"receipt_id,omitempty"`
// Reason is the dead-letter reason the quarantine block recorded, and empty in
// every other state. The block also names where the raw payload is retained,
// under the gitignored local tier; that path is never carried here.
Reason string `json:"reason,omitempty"`
// ReEmit is the command that (re-)emits the review request, set only where the
// review is owed: on a terminal receipt the re-emit changes nothing.
ReEmit string `json:"re_emit,omitempty"`
// AuditOwed is true on an INGESTED receipt whose verdict left a check owed
// (ruling DQ1c): its review is done, its check is not, and its re-emit
// rewrites the request for the re-run. AuditOwedIssue is the issue the
// flag names as carrying it, empty when none does.
AuditOwed bool `json:"audit_owed,omitempty"`
AuditOwedIssue string `json:"audit_owed_issue,omitempty"`
}
ReviewEntry is one shipped intent's fidelity-review state.
func ReviewOf ¶ added in v0.11.1
func ReviewOf(repoRoot string, it Intent) (ReviewEntry, error)
ReviewOf reads one intent's review state from its record. The caller decides whether the intent's bucket owes a review; this reads the marker whatever the bucket.
func (ReviewEntry) IsOwed ¶ added in v0.11.1
func (e ReviewEntry) IsOwed() bool
IsOwed reports whether the entry is in the owed set: OWED plus none. A dead-lettered review is unreviewed but not owed; it is reported apart.
type ReviewListing ¶ added in v0.11.1
type ReviewListing struct {
Entries []ReviewEntry `json:"entries"`
Owed int `json:"owed"`
DeadLettered int `json:"dead_lettered"`
Ingested int `json:"ingested"`
// AuditOwed counts the ingested receipts whose verdict left a check owed;
// they are counted in Ingested too.
AuditOwed int `json:"audit_owed"`
}
ReviewListing is every shipped intent's review state, in corpus load order, with the totals by state. Owed counts OWED plus none.
func Reviews ¶ added in v0.11.1
func Reviews(repoRoot string) (ReviewListing, error)
Reviews reads the review marker of every intent in shipped/. It never writes.
type ReviewQueue ¶ added in v0.11.1
type ReviewQueue struct {
Queue []QueuedReview `json:"queue"`
Owed int `json:"owed"`
Max int `json:"max"`
Remaining int `json:"remaining"`
}
ReviewQueue is the owed reviews, oldest shipped first, capped at Max. Owed is the whole owed total before the cap; Remaining is how many the cap left out. Max 0 is no cap.
func OwedQueue ¶ added in v0.11.1
func OwedQueue(repoRoot string, max int, shippedOn ShippedOn) (ReviewQueue, error)
OwedQueue orders the owed fidelity reviews oldest shipped first and caps the list at max (0: no cap; negative: refused). shippedOn may be nil, which leaves every day unknown and the queue in mint order. It never writes.
type ScopeCondition ¶ added in v0.7.0
type ScopeCondition struct {
Ordinal int `json:"ordinal"` // 1-based position in the section
ID string `json:"id"` // cond-<16 digits>, empty when unmarked
Text string `json:"text"` // the bullet's prose, marker removed
// ExtraIDs holds every well-formed marker after the first. A bullet carrying
// two identities is an ambiguity the gate names: a disposition keyed on one of
// them would attach to whichever the reader reached first.
ExtraIDs []string `json:"extra_ids,omitempty"`
// MalformedMarker reports a `<!-- cond:` in the bullet that is not a
// well-formed identity. Left unread it is silent prose, so the stamp glues a
// real marker beside it and the bullet ends up carrying two things that look
// like identities to a human and one to the machine.
MalformedMarker bool `json:"malformed_marker,omitempty"`
}
ScopeCondition is one recorded context claim: the condition's prose and the minted identity that outlives a rewrite of it. ID is empty for a bullet no `Plan` run has stamped yet — an unstamped condition is a gate finding, never silently repaired at read time.
type ShippedOn ¶ added in v0.11.1
ShippedOn reports the day (YYYY-MM-DD) the intent file at a repo-relative path entered the bucket it sits in, or "" when it is not known.
type StandingEntry ¶ added in v0.11.0
type StandingEntry struct {
ConditionID string `json:"condition_id"`
Disposition string `json:"disposition"`
Source string `json:"source,omitempty"`
Occasion string `json:"occasion,omitempty"`
Date string `json:"date,omitempty"`
}
StandingEntry is one condition's standing disposition and the block it came from. Source is empty, and Disposition `untested`, for a condition no block names.
type StartChecks ¶ added in v0.12.0
type StartChecks struct {
Rows []StartRow
// Steps are the spec steps a run would build, in order: the unlanded ones,
// or the one implicit step of a spec that lists none. Empty when the steps
// check fails.
Steps []spec.Step
}
StartChecks is every record-only pre-start check for one READY intent.
func StartChecksIn ¶ added in v0.12.0
func StartChecksIn(repoRoot string, corpus Corpus, store spec.Store, r ReadyResult) (StartChecks, error)
StartChecksIn runs the record-only pre-start checks for the intent r judges, against the corpus and the spec store the caller has already loaded for repoRoot. r is the readiness gate's result for that intent. It writes nothing; an error is a fault in reading the checkout, and a record that may not start is a result whose OK is false.
func (StartChecks) OK ¶ added in v0.12.0
func (s StartChecks) OK() bool
OK reports whether every row passes.
type StatusView ¶
type StatusView struct {
Buckets map[string]int `json:"buckets"`
SpecsOpen int `json:"specs_open"`
SpecsClosed int `json:"specs_closed"`
Linked []LinkedPair `json:"linked"`
ReviewsOwed int `json:"reviews_owed"`
// Intents lists every intent, one entry each, ordered by bucket then id
// (iss-242): what a planning sweep asks of each record without opening it.
Intents []IntentListing `json:"intents"`
}
StatusView is the read-only lifecycle summary: intent counts by bucket, spec counts by status, the linked intent↔spec pairs, and the count of shipped intents whose fidelity review is owed (the Reviews listing's owed total).
func Status ¶
func Status(repoRoot string) (StatusView, error)
Status builds the read-only lifecycle summary: intent counts by bucket, spec counts by status, the owed fidelity reviews, and the intent↔spec links (every intent whose spec_id is non-null). Linked pairs are ordered by the corpus load order (bucket, then directory), which is deterministic.
type TargetResult ¶ added in v0.12.0
type TargetResult struct {
IntentID string `json:"intent_id"`
Path string `json:"path"`
Target string `json:"target_release"`
Previous string `json:"previous,omitempty"`
Written bool `json:"written"`
}
TargetResult reports a completed Target. Previous is the target the record carried before, empty when it carried none; Written is false when the record already carried this target, so nothing was written.
func Target ¶ added in v0.12.0
func Target(repoRoot, intentID, version string) (TargetResult, error)
Target writes `target_release: <version>` onto a planned intent: `next`, or a release tag vX.Y.Z. A second target replaces the first, and the result names the one it replaced; the same target again writes nothing. A draft is refused naming `intent plan --target`, and a shipped, superseded or discipline record is refused, each with nothing written.
The read, the re-check and the write are one critical section under the store's advisory lock, as every other intent write is, so a concurrent write to the same record is never overwritten from stale bytes.
type TargetRewrite ¶ added in v0.12.0
TargetRewrite is one intent record a cut rewrites as it moves a missed target to `next`: the record's repo-relative path and its bytes before and after, so the cut writes it with its other writes and restores it on their undo.
func PlanTargetMoves ¶ added in v0.12.0
func PlanTargetMoves(repoRoot string, moves []launch.TargetMove) ([]TargetRewrite, error)
PlanTargetMoves reads each record a cut passes (launch.MissedTargets) and returns the rewrite that moves its target to `next` (criterion 3, the product thinker's ruling BS1 of 2026-09-29). It writes nothing. A move whose target is already `next` needs no rewrite and yields none: it still names the following release after the cut.
Every move is checked against the record as it is on disk, and one that no longer matches — a record not in planned/ under that path, or a target that moved since the cut read it — refuses the whole plan: the cut is not written from a stale read. The caller holds the store's lock (WithMintLock) across this read and its writes, as every other intent writer does.
type TextOptions ¶ added in v0.10.0
TextOptions parameterizes CreateFromText beyond the text itself: an explicit H1 title in place of the derived first sentence, the optional impact judgement and the production mode. Impact and mode are stamped as CreateDraft validates them; the title is validated and redacted by CreateFromText.
TitleSet says the caller gave a title at all, so that an explicit empty one (`--title ""`) is refused like a blank one rather than read as no title: the surface passes whether the flag was set, and a non-empty Title counts as set on its own.
type UnholdResult ¶ added in v0.10.0
type UnholdResult struct {
IntentID string `json:"intent_id"`
Path string `json:"path"`
Bucket string `json:"bucket"`
Reason string `json:"reason"`
}
UnholdResult reports a completed Unhold. Reason is the hold it lifted, so the surface can say what was standing.
func Unhold ¶ added in v0.10.0
func Unhold(repoRoot, intentID string) (UnholdResult, error)
Unhold removes the `held:` line from a draft or planned intent. A record not held is refused — there is nothing to lift, and a no-op that reports success would let a caller believe it changed a state it did not. A record whose `held` value is in a shape the verb never writes is refused too, naming the line as a hand repair: the verb only removes what the verb could have written, so its write stays a single-line delete with no guess about which following lines were part of somebody's block. And a removal that changes nothing — a `held` line the reader accepts but the writer's key pattern does not match — is refused the same way, never reported as a lift.
type UnresolvedCitation ¶ added in v0.11.1
UnresolvedCitation is one record id a fragment cites that the repository's record gate refuses, at its 1-based line in the fragment.
func UnresolvedProseCitations ¶ added in v0.11.1
func UnresolvedProseCitations(repoRoot, rel, text string) ([]UnresolvedCitation, error)
UnresolvedProseCitations asks the registered gate which record ids text cites that the repository's record-lint would refuse in the record at rel — the same question the verdict ingest asks, for a writer outside this package that copies host-delegated prose into a lint-bound record (iss-2609261835118276). With no gate registered it returns ErrNoProseCitationGate.