Documentation
¶
Overview ¶
Package mode is abcd's waiting-on state: which of three states the agent loop is parked in. Managed and nobody waiting; parked on the facilitator; parked on the product thinker. The agent writes it when it stops for a verdict, naming whom it is addressing, and the human writes it by hand to say which hat they wear; the status render and the bare board read the same state, so no surface invents its own answer (spc-70, itd-200).
The state lives in the repository's local-ephemeral tier, at `.abcd/.work.local/mode`: one line, one of three words. Three properties are load-bearing.
It is PER-REPOSITORY, because the question it answers is about one checkout's loop, so the root is resolved through gitutil.CheckoutRoot — git's toplevel or one of two refusals, never a guess. A repository-shaped tree git cannot answer for and a directory that is no repository at all are both refusals here; the alternative, falling back to the working directory, is the defect this project has already fixed across six front doors (iss-2609090951291524, iss-2609091707224329).
It is MANAGED-ONLY BY CONSTRUCTION, not by a check. Only a managed repository has the local-ephemeral tier, so the writer requires the tier and creates nothing: a repository abcd does not manage has nowhere for the state to go and the write refuses, without this package ever asking "is this managed?" and without minting an abcd namespace in a tree that never asked for one. A bare `.abcd/` is deliberately not accepted in its place — it is not a managed signal on its own (iss-88), so gating on it would be a second, weaker answer to a question the tier already answers.
ABSENT MEANS MANAGED, which is what lets the reader hold the same property with no check at all: an unmanaged repository has no tier, so it has no file, so it reads as managed — the same answer a managed repository with no parked stop gives, which is the only answer either could sensibly carry.
This package is a library: it returns values and errors, and never prints.
Index ¶
- Constants
- Variables
- func CanSet(repoRoot string) error
- func HasTier(repoRoot string) bool
- func MarkQuestionOpen(repoRoot string, s State) error
- func QuestionOpenForTest(repoRoot string) (bool, error)
- func ResetOnAnswer(repoRoot string) (bool, error)
- func Root(cwd string) (string, error)
- func Set(cwd string, s State) error
- func SetAt(repoRoot string, s State) error
- type State
Constants ¶
const FileRelPath = TierRelPath + "/mode"
FileRelPath is the store itself, repo-relative and slash-separated. One line, one word, gitignored with the rest of the tier and per worktree — so two concurrent sessions on one repository each park their own loop and neither merge-conflicts nor overwrites the other's state.
const QuestionOpenRelPath = TierRelPath + "/question_open"
QuestionOpenRelPath is the marker, repo-relative and slash-separated. Its content is the state the question was admitted under, one word; only its presence is load-bearing.
const StoreName = "the mode store"
StoreName is the noun this store's root refusals are phrased with. It is the store's half of gitutil.CheckoutRoot's contract: the resolution and the refusal policy are shared there, and only the noun is this store's.
const TierRelPath = ".abcd/.work.local"
TierRelPath is the local-ephemeral tier that holds the store, repo-relative and slash-separated (an os.Root path). Its presence is the managed-only property: the writer requires it and never creates it.
Variables ¶
var ( // ErrUnknownState is the closed vocabulary's refusal, raised both by a write // offered a word that is not one of the three and by a read that finds one in // the store. The message names all three, because a refusal that does not say // what would have been accepted makes the caller guess. ErrUnknownState = errors.New("mode: unknown state") // ErrNoLocalTier is the writer's refusal when the local-ephemeral tier is // absent. It is the managed-only property doing its work: there is nowhere in // this tree for the state to live, and creating the tier would mint an abcd // namespace in a repository abcd does not manage. ErrNoLocalTier = errors.New("mode: no local-ephemeral tier") )
Functions ¶
func CanSet ¶ added in v0.11.1
CanSet reports whether SetAt could record a state in repoRoot right now, and why not when it could not. It is the question gate's check before it names `abcd mode` as the remedy for a refused question: a refusal whose remedy cannot run refuses forever (iss-2609260100382261).
It probes what the write needs rather than what the permission bits say: the tier is a real directory (ErrNoLocalTier otherwise, as SetAt refuses), nothing stands at the store's path that the writer's rename cannot replace, and a file can be created in the tier — the atomic writer's first step. The probe file is removed before CanSet returns, so a successful probe leaves the tier as it found it, and a failed create leaves nothing to remove. Creating is the test because it is what fails on a read-only mount, an unwritable directory and a foreign owner alike, where a mode-bit check answers only the second.
A probe the remove could not reach — the tier turned unwritable between the create and the remove, or the process died between them — would otherwise stay in the tier for good, so every call first sweeps the probes an earlier call left behind (iss-2609261403493536). The sweep removes only regular files named exactly as a probe is named, and it is best-effort: a tier it cannot read or clear is the one the create below then reports. Because a concurrent call's sweep can take this call's probe, a probe already gone when this call removes it is not a fault: its create succeeded, which is what was asked.
func HasTier ¶ added in v0.11.1
HasTier reports whether repoRoot holds the local-ephemeral tier as a real directory — the same presence test SetAt applies before it writes. It says the tier is HERE, not that it can be written: a read-only tier passes it. A caller that needs "the verb can set the state here" asks CanSet.
func MarkQuestionOpen ¶ added in v0.11.1
MarkQuestionOpen records that a question was admitted while the loop was parked on s. It refuses, writing nothing, where the tier is absent.
func QuestionOpenForTest ¶ added in v0.12.0
QuestionOpenForTest reports whether a question is marked open in repoRoot. It is a declared test seam: the mode tests read the marker through it, and no production path calls it.
func ResetOnAnswer ¶ added in v0.11.1
ResetOnAnswer is the reset the next human message triggers: when a question is marked open it records Managed, then clears the marker, and reports true. With no question open it changes nothing and reports false.
The state is written before the marker is removed, so a failure between the two leaves the marker in place and the next message retries the reset; the opposite order could clear the marker and leave the badge parked with nothing left to reset it.
That ordering is only safe for a marker the reset CAN remove, so it refuses to act on one it cannot: a directory at the marker's path is nothing the gate wrote (the gate writes one file), and resetting on it would reset a hand-set state on every message that follows while the cause stayed put (iss-2609260100393814). The refusal changes nothing and names the path. A file, a symlink or a FIFO is removed as itself, as before.
func Root ¶
Root answers which checkout's mode store a caller standing in cwd addresses. It holds no resolution of its own: the three-state answer — git's toplevel, a refusal for a repository-shaped tree git cannot answer for, and a refusal outside a repository — is resolved once in gitutil.CheckoutRoot, and all this store supplies is the noun those refusals carry.
Both refusals wrap gitutil.ErrNoCheckoutRoot, so a front door maps one error to one exit code. Neither falls back to the working directory: a mode store minted under a subdirectory would read `managed` against a repository whose loop is parked, and report success while doing it.
func Set ¶
Set records the waiting-on state of the repository containing cwd, resolving the checkout root first. An unknown state is refused before anything is resolved or written.
func SetAt ¶
SetAt records the waiting-on state under an already-resolved checkout root. Use Set where the caller holds only a working directory.
Two refusals, and both happen before any byte is written. An unknown state is refused first, so an invalid word leaves the store exactly as it was rather than truncating it. An absent local-ephemeral tier is refused second, and the tier is never created: it is present only in a repository abcd manages, which is what makes this state managed-only without a separate managed check to drift from. A tier that is a symlink rather than a real directory is refused by the same test.
The write itself is a whole-file replacement through fsutil's atomic writer, so the rename is the commit point and a concurrent reader sees either the old state or the new one, never a half-written word. It needs no lock because it reads nothing first: two writers race to a well-formed file, and the later rename wins.
Types ¶
type State ¶
type State string
State is the waiting-on state. The vocabulary is CLOSED: these three words are the whole of it, on the way in and on the way out, because a badge that can render a fourth thing is a badge whose meaning is set by whoever last wrote the file.
const ( // Managed is abcd present and nobody waiting. It is what an absent store // reads as. Managed State = "managed" // Facilitator is the loop parked on the facilitator — the person at the // terminal running the agents. Facilitator State = "facilitator" // ProductThinker is the loop parked on the product thinker, who answers on a // surface of their own and is exactly the addressee a terminal-only stop // leaves unnotified. ProductThinker State = "product-thinker" )
func ParseState ¶
ParseState reads one of the three words from raw, tolerating the surrounding whitespace a stored line or a shell argument carries — the trailing newline the writer itself emits is the common case. Anything else is refused, and the refusal names the three.
Matching is exact beyond that trim: `Facilitator` and `PRODUCT-THINKER` are refused rather than folded, because the store holds the word the writer wrote and a reader that accepts variants makes the file's contents ambiguous for the next writer.
func Read ¶
Read returns the waiting-on state of the repository containing cwd, resolving the checkout root first. An absent store reads as Managed.
func ReadAt ¶
ReadAt returns the waiting-on state stored under an already-resolved checkout root. Use Read where the caller holds only a working directory.
An absent store — an absent file, an absent tier, an absent `.abcd/` — reads as Managed, which is the whole of the managed-only rule on this side: a repository abcd does not manage has no tier, so it has no file, so it reads as the same "nobody is waiting" a managed repository with no parked stop reads as.
Everything else fails closed. A store that is a symlink, a directory, a device or oversize is an error rather than a fallback to Managed, and so is a word outside the vocabulary: each of those is somebody having written something here, and reporting "nobody is waiting" over it would hide exactly the parked stop this store exists to make visible.
func States ¶
func States() []State
States returns the three legal states in the order they are documented. It is the one enumeration; a surface that offers a choice or a completion reads it from here rather than restating the list.
func (State) Addressee ¶
Addressee names, in plain words, the person whose answer a parked loop is waiting on: "technical facilitator", "product thinker", or "" for Managed, which owes nobody an answer. The role names are the two ruled for the badge (itd-2609212130146198, whose three states read `waiting on the technical facilitator` and `waiting on the product thinker`), and the facilitator is named in full, as the record names the role.
It is the one place the vocabulary is turned into prose. Every front door that names the owed person — the status-line badge, the notice a set prints where the host has no status surface (ac-7) — reads it, through WaitingOn, so no two of them can name two different people.
func (State) WaitingOn ¶ added in v0.11.1
WaitingOn is the phrase a front door shows for a parked state — "waiting on the technical facilitator", "waiting on the product thinker" — or "" for Managed. It is the badge's word for the two role states and the body of the set form's notice, composed here once so the two cannot drift apart.