Documentation
¶
Overview ¶
Package loopcommand is the /loop façade over the agent loop's done definition. It parses a `tasks.md`-shaped directory into a LoopSpec and returns it as a loop.DoneSet, without changing the turn cycle.
Nothing in the product calls this yet: the client-side recognition of `/loop` is Step 3 of the family's `.i` and has not been built. What ships here is the parser and the dispatch between sources, and the package is honest about that rather than reading as a delivered command.
Spec: docs/specs/architecture/loop-command/202608252000-loop-command.*.spec.md
Index ¶
Constants ¶
const SpecsDirName = "specs"
SpecsDirName is where spec folders are looked for. One place, not a search: walking a repository for anything that resembles a spec finds node_modules.
Variables ¶
This section is empty.
Functions ¶
func Load ¶
Load resolves the source and returns the DoneSet.
Same inputs → same DoneSet. Reading the filesystem is the only I/O.
The verifyCommand is consulted by SourceAuto only when neither done.toml nor a LoopSpec is available. Legacy behaviour preserved.
func SessionConfig ¶
func SessionConfig(opts SessionOptions) (loop.Config, string)
SessionConfig builds the loop.Config a /loop session is born with, and the name that session will carry.
It creates nothing. The name says so, because the name that was here before — NewSession — promised a session, and the `.p` it was written against declared it as `NewSession(ctx, srv, opts) (SessionHandle, error)`: a call that reaches the server, hands back a handle with ID() and SubmitTurn(), and owns the session's lifetime. None of that exists. A constructor that constructs nothing is the kind of name a reader trusts once and then stops trusting the package.
Creating the session, running the turn and closing it afterwards belong to the caller that does not exist yet — Step 3 of the family's `.i`. What can be built without a client is the Config that caller will pass, and this is it.
Types ¶
type Found ¶ added in v0.12.0
type Found struct {
// Path is relative to the workspace, as a person would type it.
Path string
// Criteria is how many the folder declares.
Criteria int
// Unmet is how many did not pass. Zero with Criteria above zero is a spec
// that is done.
Unmet int
Unavailable int
// Err is why the folder could not be read, when it could not.
Err string
}
Found is one spec folder and where it stands.
Where it STANDS, not what it declares. "Pending" cannot be answered by counting checkboxes in a tasks.md — a box is marked by whoever felt like marking it. It is answered by running the folder's own criteria, which is the only definition of done this product recognises.
func Discover ¶ added in v0.12.0
func Discover(ctx context.Context, workspace string, run loop.CriterionRunner, timeout time.Duration) []Found
Discover lists the spec folders under a workspace and runs each one's criteria to find out where it stands.
It runs them. There is no cheaper honest answer to "which specs are pending": the criteria are the definition of done, and anything else — checkbox counts, file timestamps, a marker in the spec — is a second statement of something that can move.
Ordered by folder name, which for a dated spec is chronological. The sequencing is the operator's, and a list that reshuffles between runs is one nobody can act on.
type LoopSpec ¶
type LoopSpec struct {
// Path is the directory containing tasks.md.
Path string
// Criteria are the verifiable conditions of done for this spec.
// Sourced exclusively from tasks.md. Prose is not a source.
Criteria []loop.Criterion
// Protected are paths the agent may not modify silently during execution.
// Sourced from the user (frontmatter or argument), not inferred from
// where the spec lives.
Protected []string
}
LoopSpec is a parsed specification external to the workspace.
The parser is total: it never panics on malformed input and never invents data. What it CANNOT read, it refuses to read — a file with no task list is an error, not an empty DoneSet. The difference matters more than it looks: an empty DoneSet is "nothing to verify", which the agent loop reports as done. Silently turning an unreadable file into "done" is the worst outcome this package can produce, and RN-6 exists to forbid it.
func LoadSpec ¶
LoadSpec reads a LoopSpec from a directory containing tasks.md. Returns an error on malformed input or a missing file.
func LoadSpecWithProtect ¶
LoadSpecWithProtect reads a LoopSpec and adds the argument's globs to any declared in the file.
The two are a union, not a precedence. Protected is a list of paths whose modification gets surfaced, so "the file wins over the argument" would mean an argument could REMOVE a protection the file asked for — the one direction that must never be reachable by accident.
type Options ¶
Options holds the four loop.* settings the daemon reads from configuration.
Typed surface for the `loop.spec_path`, `loop.source`, `loop.protect` and `loop.session_prefix` keys in KnownKeys. Keys are read by name in OptionsFromConfig so the wiring guard sees them in source.
func OptionsFromConfig ¶
OptionsFromConfig builds an Options from the resolved layer values.
The wiring guard looks for the literal key names ("loop.spec_path" etc.) in source; this is the function that reads them. Keeping the literals here is what makes the configuration surface checkable — a key declared in KnownKeys and not referenced by this name in any consumer would fail the wiring test even with this function present.
type SessionOptions ¶
type SessionOptions struct {
Spec LoopSpec
Limits loop.Limits
MaxStall int
DoneTimeout time.Duration
SandboxMode policy.SandboxMode
Workspace string
Model string
SessionPrefix string
}
SessionOptions configures the dedicated session /loop will create.
The session is born with the DoneSet already in place: this is what guarantees the engine sees the same definition of done the user typed /loop for, regardless of any later session-level mutation.
type Source ¶
type Source int
Source declares where a DoneSet comes from.
SourceAuto preserves the historical behaviour: done.toml if it exists, else the legacy verifyCommand. SourceDoneFile forces done.toml. SourceLoopSpec forces a LoopSpec — failure to load is an error, not a fallback to legacy.
func ParseSource ¶
ParseSource turns the textual value (DCODE_LOOP_SOURCE or a flag) into a Source. Anything else is an error — silent defaulting would hide a typo the same way a misread configuration would.