worklang

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package worklang is the bounded reader for a `.work` plan (docs/SPEC-WORKLANG.md).

A plan is data the kernel reads, never a program it evaluates. The reader is the restricted-Lisp grammar the rest of this repo already uses for work files: a sequence of s-expressions made only of lists, keywords, strings, integers and bare symbols, with every evaluation form refused at the boundary and three bounds enforced as the reader runs -- bytes, nesting depth and node count. Nothing read here is ever evaluated: there is no eval, and a dispatch macro is a refusal with the byte offset that owes it, not a form the reader will run.

Every refusal is a *Refusal carrying exit code 2, so the command that opens a plan can print one line and stop, never a partial parse.

Index

Constants

View Source
const CardFile = "card"

CardFile is the name of the file one card is written to inside its directory.

View Source
const StateLive = "live"

StateLive is the state a taken unit is in while its attempt runs.

Variables

This section is empty.

Functions

func CanonicalOutcome

func CanonicalOutcome(s string) (string, bool)

CanonicalOutcome maps a caller's spelling onto the grammar's outcome. The second result is false for a word that is neither.

func ExpandDir

func ExpandDir(out string, cards []Card) (int, error)

ExpandDir writes one card directory per node under out, and returns the number of cards it wrote. A card already present is left byte-identical, so a re-expansion after one fact changes appends only the new card and mints no id: the directory name is the node's own stable id.

func ExpandDirWithSink

func ExpandDirWithSink(out string, cards []Card, sink ExpandSink) (int, error)

ExpandDirWithSink is ExpandDir with an optional event sink. If sink is not nil, one cut event is emitted per card actually written (existing cards are skipped and emit no event).

func FindCycles

func FindCycles(order []string, needs map[string][]string) [][]string

FindCycles names every `:needs` cycle in a graph, each once. refuseCycle stops at the first because a plan with one is refused whole; a checker prints them all, and both walk this one function so the two can never find different cycles.

The walk is the same three-colour depth-first search: grey is the path being walked and a grey node reached again closes a cycle. The cycle is normalised to start at its smallest member so that the same loop entered by two of its members is reported once.

func KnownKinds

func KnownKinds() []string

KnownKinds returns the kind names the plan grammar admits, in spec order.

func KnownOutcomes

func KnownOutcomes() []string

KnownOutcomes returns the attempt outcomes the grammar admits.

func KnownStates

func KnownStates() []string

KnownStates returns the unit states the grammar admits, in spec order.

func OutcomeSpellings

func OutcomeSpellings() []string

OutcomeSpellings is every word --outcome accepts, in a stable order, for the refusal that names them.

func ParseStamp

func ParseStamp(s string) (time.Time, error)

ParseStamp reads any of those spellings as UTC and refuses anything else naming what it accepts, rather than falling back to a clock.

func RenderCard

func RenderCard(c Card) []byte

RenderCard renders one card deterministically: the same Card always produces the same bytes, so a re-expansion is byte-identical.

func Requests

func Requests(units []Unit) []jobs.Request

Requests builds the admission requests for units in written order, so a pass over a set is deterministic in the order the author wrote it.

func StateAfter

func StateAfter(outcome string) string

StateAfter is the state an outcome leaves the unit in: A4's machine in one function, so the CLI and any later caller cannot disagree about it.

Types

type Affinity

type Affinity struct {
	Bench string
	Route string
	Model string
}

Affinity names which bench kind may pull the card and which route serves it. Model is the route's model class when the plan pins it; an empty Model means the class is a projection from facts this slice does not read.

type Attempt

type Attempt struct {
	// Offset and End are the record's own byte range, so a verb that CLOSES an
	// open attempt rewrites that record and nothing else.
	Offset   int
	End      int
	N        int64
	Rung     string
	Owner    string
	Started  string
	Outcome  string
	HasProof bool
	Proof    Form
}

Attempt is one record of one try at a unit: which rung ran it, who owned it, when it started, how it ended, and whether it proved termination.

type Blocker

type Blocker struct {
	Need     string
	State    string
	Resolver string
}

Blocker is the exact reason a node cannot proceed: the first unmet need, its state, and the join that resolves it.

type Branch

type Branch struct {
	Name  string
	Base  string
	Green bool
}

Branch is one fact the (:fold :over (:branches ...)) sweep reads: the name of the sibling branch, the base it branched off, and whether it merged and went green. The fold is green-only by default -- a non-green sibling is excluded by the `:over` selector before it reaches the inputs list, so the caller does not check green here.

type Budget

type Budget struct {
	Minutes int64
	Tokens  int64
	Floor   string
}

Budget is one card's wall: its minutes, its token ceiling and the lowest model class admitted.

type Card

type Card struct {
	Node     string
	Kind     string
	Repo     string
	Base     string
	Needs    []string
	Blocks   []string
	Ready    bool
	Inputs   []string
	Result   string
	Branch   string
	Green    []string
	Budget   Budget
	Affinity Affinity
}

Card is one expanded node: everything the bench needs, and nothing the plan did not write. Its fields are the spec's output contract, budget and affinity, plus the needs/blocks edges the graph derived.

func ExpandPlan

func ExpandPlan(plan *Plan, facts ...*Facts) ([]Card, error)

ExpandPlan validates a hand-written plan, expands any (:derive) or (:fold) the plan carries into the same Card pass as the hand-written (:node)s, builds the needs/blocks graph, and returns one Card per node in plan order. It refuses an absent need or a cycle (through the job graph's rules 2 and 3), a `:derive` or `:fold` with no Facts source, an unknown `:from` or `:over` selector key, a duplicate branch name, and a budget below its floor -- always before a card is written.

The variadic Facts parameter is additive: callers without a fact source pass none, and a plan that never names `:derive` or `:fold` is unchanged; callers that do sweep projects pass `ExpandPlan(plan, &worklang.Facts{...})`. `:facts` remains unadmitted -- the rule form is a later slice, not this one.

func ExpandPlanWithSink

func ExpandPlanWithSink(plan *Plan, sink ExpandSink, facts ...*Facts) ([]Card, error)

ExpandPlanWithSink is ExpandPlan with an optional event sink. If sink is not nil, one derive event is emitted per card in plan order, derived and folded cards included.

type Collection

type Collection struct {
	Name           string
	Under          string
	MembersUnknown bool
	Revision       string
	Members        []string
}

Collection is one output collection: named before the run, its members known only after it.

func (*Collection) BindMembers

func (c *Collection) BindMembers(revision string, members []string)

BindMembers records the unit's revision and the member paths at harvest time, clearing the unknown-before-run flag.

type Counts

type Counts struct {
	Units   int
	Ready   int
	Blocked int
	Done    int
	Owned   int
}

Counts is the one summary line's arithmetic: units = ready + blocked + done. owned is the units that name an owner, the ones the machinery routes to a friend rather than cutting as a card.

type Criterion

type Criterion struct {
	ID        string
	Kind      string
	Subject   string
	Predicate string
}

Criterion is one acceptance criterion, in the schema the work spec fixes.

type CutEvent

type CutEvent struct {
	Card          string
	PoolCandidate string
	Template      string
	Route         string
}

CutEvent is one cut event from the expander: a card cut, carrying its card id, pool candidate, template and route.

type DeriveEvent

type DeriveEvent struct {
	Node   string
	Parent string
	Rule   string
}

DeriveEvent is one derive event from the expander: a node derived from the graph, carrying its node id, parent and rule.

type ExpandSink

type ExpandSink interface {
	Derive(DeriveEvent)
	Cut(CutEvent)
}

ExpandSink receives derive and cut events from the expander. A nil sink discards all events.

type Facts

type Facts struct {
	Issues   []Issue
	Branches []Branch
}

Facts is the closed fact source the expander reads: pinned issues and pinned branches. The plan only carries the *shape* of a selector; the data the selector filters lives here, passed in by the caller. A nil Facts means a hand-written plan only, and a plan naming `:derive` or `:fold` in that case is a refusal naming the missing source, never a silent empty sweep.

type Finding

type Finding struct {
	Rule string
	Unit string
	// Key and Value are the rule's own field -- need=<id>, owner=<name>,
	// cycle=<a->b->a> -- kept apart so the printing site escapes the value on
	// its own and a unit id with a space can never break the line.
	Key    string
	Value  string
	Remedy string
	Offset int
}

Finding is one thing wrong with the CONTENT of a work set: the rule that caught it, the unit that owes it, and the fields the line prints. Rule is the word the SET line carries, so a caller greps one word rather than a sentence.

func (Finding) Detail

func (f Finding) Detail() string

Detail is the rule's field as one `name=value` token, for a caller that wants the line's middle rather than its two halves.

type Form

type Form struct {
	Kind   Kind
	Offset int
	End    int
	List   []Form
	Value  string // keyword name (no colon) or decoded string
	Int    int64
}

Form is one restricted s-expression. Offset is the form's first byte and End the byte just past its last, so a refusal can name where in the file it happened -- and a WRITER can replace exactly the bytes one form occupies and leave every other byte of the file alone.

func Read

func Read(file string, data []byte, limits Limits) (Form, error)

Read reads exactly one restricted form from data, holding it to limits and refusing anything else. The file name is carried into every refusal.

func (Form) Bytes

func (f Form) Bytes(data []byte) []byte

Bytes returns the form's own bytes out of the source it was read from. It is the writer's half of Offset/End: the one place a caller turns a form back into the text the author wrote, rather than re-rendering it and losing the spacing, the order and the comments beside it.

func (Form) IsKeyword

func (f Form) IsKeyword(name string) bool

IsKeyword reports whether f is the keyword named name (without the colon).

func (Form) Text

func (f Form) Text() string

Text returns the decoded string of a String form, or "".

type Graph

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

Graph is the closed needs/blocks graph a plan declares. order is seed order, the order every node is admitted in; needs is the forward edge and blocks the inverse, both derived from the same declarations so the graph never holds one without the other.

func (*Graph) Blocks

func (g *Graph) Blocks(id string) []string

Blocks returns the reverse edges of id in seed order. It is the inverse of Needs, written by the same insert.

func (*Graph) Edges

func (g *Graph) Edges() int

Edges returns the number of forward needs edges.

func (*Graph) Len

func (g *Graph) Len() int

Len is the number of nodes. Edges is the number of needs edges.

func (*Graph) Needs

func (g *Graph) Needs(id string) []string

Needs returns the forward edges of id in seed order: the nodes it cannot begin before.

func (*Graph) NeedsBroken

func (g *Graph) NeedsBroken(id string) bool

NeedsBroken reports whether a reverted need has left id flagged needs-broken.

func (*Graph) Order

func (g *Graph) Order() []string

Order returns every node id in seed order: admission, not readiness.

func (*Graph) Ready

func (g *Graph) Ready(id string) (bool, *Blocker)

Ready reports whether id may proceed, and when it may not, the exact blocker and its resolver. A node is ready only when every need is terminal accepted and no reverted need has left it needs-broken. A node already terminal accepted has nothing left to begin.

func (*Graph) ReadySet

func (g *Graph) ReadySet() []string

ReadySet is the mechanical ready set: the nodes with no unmet need, in seed order. The graph's own nodes are all admitted; only the ready ones are pullable.

func (*Graph) Revert

func (g *Graph) Revert(id string)

Revert records that id was reverted after landing, flagging each dependent needs-broken rather than leaving it silently ready.

func (*Graph) Settle

func (g *Graph) Settle(id string)

Settle records that id merged and went green: terminal accepted.

type Issue

type Issue struct {
	Number   int64
	Slug     string
	URL      string
	Repo     string
	Label    string
	State    string
	HasPR    bool
	PRNumber int64
}

Issue is one fact the (:derive :from (:issues ...)) sweep reads: the stable identifiers the kernel already uses, the slug the branch rule substitutes, the URL the issue input names, and the :pr fact -- bool for presence, number for the PR it points at -- that decides ink-or-out. An issue missing any of these is refused by the caller before it reaches the sweep, so the sweep never picks half-formed ones.

type Kind

type Kind int

Kind is the type of one restricted form.

const (
	// List is a parenthesized form, possibly empty.
	List Kind = iota
	// Keyword is a `:name` token, stored without the leading colon.
	Keyword
	// String is a double-quoted string, stored decoded.
	String
	// Integer is a decimal integer.
	Integer
	// Symbol is a bare token such as go-fix or false, stored verbatim. The
	// plan language uses symbols for values that are not text (kind names,
	// booleans), and they are data: nothing here is ever evaluated.
	Symbol
)

type Limits

type Limits struct {
	MaxBytes int
	MaxDepth int
	MaxNodes int
}

Limits is the three bounds every plan read is held to. A zero value is not usable; build one with DefaultLimits or set all three. The names are the flag names the caller exposes: --max-bytes, --max-depth, --max-nodes.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits is the shape the kernel's intake limits already carry (lisp/nova-work/src/control.lisp): 64 KiB, depth 64, 4096 nodes.

type NewAttempt

type NewAttempt struct {
	Rung    string
	Owner   string
	Started string
	Outcome string
	Proof   Proof
	Usage   string
	PR      int
}

NewAttempt is one attempt to record: who ran it, when it started, how it ended, the proof that it ended, and the two optional pointers a run leaves behind -- the usage TSV it spent into and the PR it produced.

type Node

type Node struct {
	Fields  map[string]Form
	Unknown map[string]Form
}

Node is one `:node` of a plan: its known Fields and the unknown keys beside them, preserved so a later slice can read what this one ignores.

func (Node) ID

func (n Node) ID() string

ID is the node's :id string, or "" when it carries none.

func (Node) Kind

func (n Node) Kind() string

Kind is the node's :kind name, or "" when it carries none.

type Options

type Options struct {
	// Minds is the set of known owners, folded to lower case. Nil means no --minds
	// was given and no owner is checked.
	Minds map[string]bool
	// MindsFile is what Minds was read from, named in the finding so the remedy
	// says which file to add the row to.
	MindsFile string
	// Lanes is the set of known lane names. Nil means no --lanes was given and no
	// lane is checked.
	Lanes     map[string]bool
	LanesFile string
	// Done names units that are done regardless of what the file says, for a
	// caller that holds the settled facts the document does not.
	Done map[string]bool
	// Evidence is the verdict of a unit's evaluated :acceptance (set check
	// --evaluate, #2664). A unit it names is done exactly when the value is true
	// -- the criteria replace what :done and :status say -- and a unit it does
	// not name keeps the document's own word. Done still adds on top.
	Evidence map[string]bool
}

Options is what Check needs from outside the language. Every one of them is optional, and an absent one turns its rule OFF rather than inventing a default: there is no default minds file, no default lanes file and no discovery.

type Plan

type Plan struct {
	File    string
	Version int64
	Goal    *Form
	Nodes   []Node
	Unknown []Form
}

Plan is the reader's view of a `.work` file. Unknown forms at the top level are preserved and ignored.

func ParsePlan

func ParsePlan(file string, data []byte, limits Limits) (*Plan, error)

ParsePlan reads a plan and validates the fields the reader owns: every `:kind` must be a known kind, refused naming the field otherwise. An unknown key beside it is preserved in Node.Unknown and otherwise ignored.

func (*Plan) Graph

func (p *Plan) Graph() (*Graph, error)

Graph closes the plan's nodes into a needs/blocks graph. It refuses a duplicate or empty id, an absent need naming the field and the id, and a `:needs` cycle by validator rule 3, before returning: a refused graph is never published.

type Proof

type Proof struct {
	Kind  string
	Value string
}

Proof is the termination proof A3 requires of every outcome but `uncertain`: what kind of evidence it is and the evidence itself. A3's own examples are an exit, a merge, a reaped pid and a fencing token; what a caller has at hand is a path, a sha or a url, and the kind is READ off the value rather than asked for twice.

func ProofOf

func ProofOf(value string) (Proof, bool)

ProofOf reads a --proof value and says what kind of evidence it is. A url is one with a scheme, a sha is 7 to 64 hex digits, and everything else is a path: three readings of the same bytes, none of them a guess a caller has to confirm. An empty value is no proof at all.

type Recorded

type Recorded struct {
	N      int64
	Closed bool
	State  string
	// Rung, Owner and Started are the record AS WRITTEN. They are not always
	// what the caller passed: closing an open attempt keeps the rung, the owner
	// and the instant the try actually began, because that is when it began.
	Rung    string
	Owner   string
	Started string
	Outcome string
	Bytes   []byte
}

Recorded is what one edit did: the record as written, whether it closed an attempt that was already open rather than appending a new one, and the state the unit now carries.

func Record

func Record(file string, data []byte, unitID string, a NewAttempt, limits Limits) ([]byte, Recorded, error)

Record appends -- or closes -- one attempt on one unit of a work set, editing the file's bytes in place.

It CLOSES rather than appends when the unit's last attempt is still open: an `:outcome :uncertain` with no `:proof`, taken by this same owner. An open attempt taken by a DIFFERENT owner is refused, never appended beside: its state and reservation stand until its own owner closes it. A try that started and then ended is ONE try (A3: "an attempt is a record, not a counter"), so the record that was opened by `next --take` is the record that is finished here, keeping its `:n`, its `:rung` and its `:started`. Any other last attempt is a new try and a new record.

Every refusal is made BEFORE a byte is written, so a file is never left half edited: an outcome that is not one of A3's, an outcome other than `uncertain` with no proof, a unit the set does not hold, a unit already in a terminal state and an open attempt owned by another mind are all refused with the document untouched.

func Take

func Take(file string, data []byte, unitID string, a NewAttempt, limits Limits) ([]byte, Recorded, error)

Take opens an attempt: the record `next --take` writes when a mind picks a unit up. It is an ordinary attempt with the only outcome an unfinished try can honestly carry -- `uncertain`, which owes no proof precisely because nothing has terminated -- and it moves the unit to `live`, so the lane and the writes it holds are charged to it from this instant.

type Refusal

type Refusal struct {
	File   string
	Reason string
}

Refusal is a plan this reader would not read. It is always exit 2: the tool could not run, and the input is refused whole rather than truncated.

func (*Refusal) Error

func (r *Refusal) Error() string

func (*Refusal) ExitCode

func (r *Refusal) ExitCode() int

ExitCode is 2 for every refusal, the code docs/nova-lessons.md fixes for a plan that could not be read.

type Status

type Status struct {
	Merged   bool
	Green    bool
	Reverted bool
}

Status is the settled facts the journal holds for one node. A node is terminal accepted when it has merged and gone green and has not been reverted.

func (Status) State

func (s Status) State() string

State names a status for a row: open, merged, reverted, or accepted. A merged node that is not yet green is the blocker nova-sprint card harvest settles.

type Tool

type Tool struct {
	Verb string
	At   string
	Key  string
}

Tool is one verb a unit needs installed, at a version, with an optional semantic key: when the key moves the unit's outputs are invalid even though the version did not change.

type Unit

type Unit struct {
	ID string
	// Offset and End are the unit form's own byte range in the file it was read
	// from. They are what lets a writer edit ONE unit in place and leave every
	// other byte of the document exactly as its author wrote it.
	Offset  int
	End     int
	Fields  map[string]Form
	Unknown map[string]Form
	// contains filtered or unexported fields
}

Unit is one (unit "<id>" ...) form: its stable id, its known Fields and the unknown keys beside them, preserved so a later slice reads what this one ignores.

func (Unit) Acceptance

func (u Unit) Acceptance() []Criterion

Acceptance returns the unit's acceptance criteria in A14's schema: the evidence that closes the unit. A criterion written as a bare sentence is not one of these -- it is in AcceptanceText -- because a sentence names no predicate a tool can read.

func (Unit) AcceptanceText

func (u Unit) AcceptanceText() []string

AcceptanceText is the unit's acceptance in the older spelling: the sentences a person wrote, which is what the sets on the bench carry and what an ask note renders. A criterion written as a (:id ...) form contributes its subject, so one caller reads both spellings and there is no second reader of the key.

func (Unit) Attempts

func (u Unit) Attempts() []Attempt

Attempts returns the unit's attempt records in source order.

func (Unit) Branch

func (u Unit) Branch() string

Branch is the :branch a unit's owner replies on. It is read HERE with every other key because there is ONE reader of this form: a key only the ask side read would be the second reader of the work set growing back.

func (Unit) Bytes

func (u Unit) Bytes(data []byte) []byte

Bytes returns the unit's own bytes out of the document it was read from: the form exactly as its author wrote it, spacing and comments included. It is what makes "every unit but the edited one is byte-identical" a reading rather than an assertion.

func (Unit) Collections

func (u Unit) Collections() []Collection

Collections returns the unit's output collections.

func (Unit) Deadline

func (u Unit) Deadline() string

Deadline is the :deadline text EXACTLY as written. It is parsed by the caller, because an unreadable deadline is a finding about one unit and never a refusal of the whole set.

func (Unit) Done

func (u Unit) Done() bool

Done reports the :done key, read as true for the handful of spellings a person writes for yes. Anything else is false: a :done nobody can read is not a unit this tool will call finished.

func (Unit) Keys

func (u Unit) Keys() []string

Keys returns every key the unit's form carried, known or not, in written order. It is what lets a later slice read what this one ignores without a second reader over the same bytes.

func (Unit) Lane

func (u Unit) Lane() string

Lane returns the unit's lane -- the resource of capacity 1 over the area of the tree queue/control/lanes.tsv names -- from :resources first and the plain :lane key second, so the set as written today keeps working.

func (Unit) Needs

func (u Unit) Needs() []string

Needs returns the ids this unit waits on, in source order.

func (Unit) Owner

func (u Unit) Owner() string

Owner returns the mind that owns the unit: a friend, a child rung, a swarm, or "all". One spelling, so `nova-work ask` and the pull worker read the same form.

func (Unit) PR

func (u Unit) PR() string

PR is the :pr a unit's work lands as, as written -- `1412`, `#1412` or `"1412"`. It is read HERE with Branch and Lane, for the reason Branch names: there is ONE reader of this form, and a key only the token ledger read would be the second reader of the work set growing back. An Integer form carries no Value, so the number is rendered from Int.

func (Unit) Request

func (u Unit) Request() jobs.Request

Request is the unit's admission request: the vector it consumes and the paths it writes. The lane comes from :resources first and the plain :lane key second -- the reader already reconciles the two (A6) -- and it enters the vector as a NAMED dimension, because `lane:docs` and `lane:merge` do not contend and one opaque "lane" count could not tell them apart.

func (Unit) Resources

func (u Unit) Resources() map[string]int64

Resources returns the unit's resource vector as counts. A lane is capacity 1 under the key "lane"; a named scarce class is "class:<name>"; every other entry keeps its own name. Admission against these counts is the kernel's, not this reader's.

func (Unit) State

func (u Unit) State() string

State returns the unit's state, "open" when it carries none. `uncertain` is a state of its own.

func (Unit) Status

func (u Unit) Status() string

Status is the :status value as written -- "closed", :review -- one of the three ways a unit says it has finished. The other two are :done and the caller's own list.

func (Unit) Title

func (u Unit) Title() string

Title is the unit's display text. The id is never it: A2 keeps the two apart so a rename is a new id carrying :was, not a re-pointed one.

func (Unit) Tools

func (u Unit) Tools() []Tool

Tools returns the verbs the unit needs installed, with their versions and optional semantic keys.

func (Unit) Warm

func (u Unit) Warm() WarmState

Warm returns the retained/active split of the unit's warm state.

func (Unit) Writes

func (u Unit) Writes() []string

Writes returns the repo-relative paths the unit edits, in source order.

type WarmState

type WarmState struct {
	Retained []Form
	Active   []Form
}

WarmState is the retained/active split. Retained entries are held between units (a kept worktree, a loaded compiler); active entries are what the unit is using while it runs. They are accounted apart so warmth is never charged as running capacity.

type WorkSet

type WorkSet struct {
	File string
	ID   string
	// Title is the set's :title as written, kept beside Fields because it is
	// the one field of the set a caller prints rather than walks.
	Title   string
	Fields  map[string]Form
	Unknown map[string]Form
	Units   []Unit
	// contains filtered or unexported fields
}

WorkSet is the reader's view of one (work-set ...) file.

func ParseWorkSet

func ParseWorkSet(file string, data []byte, limits Limits) (*WorkSet, error)

ParseWorkSet reads a work set and validates the shapes this reader owns: the top form, every unit's id, and the shape of each amendment key. A refused set is refused whole at exit 2, never half-read. This is A2's door: a unit with no id, an empty id, or an id another unit already carries is refused here.

func ParseWorkSetTolerant

func ParseWorkSetTolerant(file string, data []byte, limits Limits) (*WorkSet, error)

ParseWorkSetTolerant reads the same form but carries a broken IDENTITY through instead of refusing it: a member of :units that is not a unit, an empty id and a repeated id all come back as units at their own byte, for Check to report as findings in one pass. Everything else -- a file that cannot be read, a :units that is not a list, an amendment key whose shape is wrong -- is still a refusal, because those are not findings about one unit.

The two doors exist because the two callers want different answers. A kernel loading a set wants A2 enforced before anything is scheduled; a checker wants every defect of the file in one pass, and a reader that stopped at the first would cost its caller a round trip per defect.

func (*WorkSet) Blocked

func (w *WorkSet) Blocked(extra map[string]bool) map[string]string

Blocked is the complement of Ready among the open units: each one with the first need that is holding it. It is the same walk, so the two can never disagree.

func (*WorkSet) Check

func (w *WorkSet) Check(opts Options) ([]Finding, Counts)

Check validates one work set whole and returns EVERY finding, in the order the rules run and the units are written. It never stops at the first: a checker that did would cost the caller one round trip per defect.

The rules:

  1. every unit has an id, and no two units share one
  2. every :needs names a unit of this set
  3. the :needs edges are acyclic
  4. every :owner is a mind --minds names (off without --minds)
  5. every :lane is a lane --lanes names (off without --lanes)
  6. every :deadline is an instant, in either RFC3339 spelling

A unit is done when it says so (`:done`, or a `:status` of closed, done, landed or merged) or when Options.Done names it. It is ready when it is not done and every need is done; blocked when it is not done and some need is not.

func (*WorkSet) Decided

func (w *WorkSet) Decided(opts Options) map[string]bool

Decided is DoneSet with the evaluated evidence beside it: a unit Evidence names is done when its criteria hold, whatever its :status says; every other unit is done by its own word; opts.Done adds to both.

func (*WorkSet) DoneSet

func (w *WorkSet) DoneSet(extra map[string]bool) map[string]bool

DoneSet is every unit this run counts as finished: what the document says and what the caller's --done adds. It is the only input to readiness, so the ready set is mechanical -- a function of the language and the settled facts, never of anyone's judgment.

func (*WorkSet) LoadCheck

func (w *WorkSet) LoadCheck() error

LoadCheck refuses the work set at load time when any unit carries no :acceptance. This is the kernel's door (behaviour 40): a unit naming no evidence names no finish line, so whether it is done is a judgement rather than evidence. The caller uses the file and limits that produced this set so the refusal carries the right name.

func (*WorkSet) Ready

func (w *WorkSet) Ready(extra map[string]bool) []Unit

Ready is the mechanical ready set: the units that are not done and whose every need is done, in written order. It is what a coordinator asks the language for -- "what can be pulled right now" -- and it is derived, never maintained by hand.

func (*WorkSet) ReadyFrom

func (w *WorkSet) ReadyFrom(done map[string]bool) []Unit

ReadyFrom is Ready over a done set the caller already decided -- Decided's, when the done set came from evidence rather than from the document's words.

func (*WorkSet) Unit

func (w *WorkSet) Unit(id string) (Unit, bool)

Unit returns the unit with this id.

func (*WorkSet) WithoutAcceptance

func (w *WorkSet) WithoutAcceptance() []string

WithoutAcceptance returns the ids of the units that name no evidence. It is the report rule A14 owes: a unit with no acceptance names no finish line, and the real set of 2026-09-17 has not one.

Jump to

Keyboard shortcuts

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