store, ipc: type the routine status, defend the framing with tests (V-410)

Two review threads from PR 4, and the answer to the third.

The routine status was a bare string with its legal set in a comment.
Nothing caught a typo at compile time, nothing enumerated the set for a
test, and a bad value surfaced as a /routines row that neither accepts nor
dismisses. It is a RoutineStatus now, with the three constants, a
RoutineStatuses slice as the single source of truth, and Valid(). Listing
by an unknown status is refused with ErrRoutineStatus instead of answering
"no rows", which is what a correct query says about an empty table. A
round-trip test moves a routine into each state and reads it back, so a
constant that drifts from the inline SQL fails loudly.

The hand-rolled framing stays, and frame.go now says why: ninety lines,
readable with socat, and every standard replacement brings schema
machinery this boundary does not want. What was wrong was inheriting it
untested. frame_test.go covers the paths a real socket produces and the
round-trip test never does — truncated header, truncated body, one byte
per Read, two frames back to back, and a non-JSON body. Empty input is the
only EOF.

The unanswered question in the same file is answered in place: a routine
object stays a local string, not a Nexus ref, because nothing acts on it.
It is the word he used, replayed back to him, compared only against itself
for the UNIQUE key. Canonical refs arrive if a routine ever drives a Hexis
call, which is V-272.

The mood enum has the same shape and is not done here: it is spelled in
the GBNF grammar, three prompts and the parse, so it is its own change.
This commit is contained in:
2026-08-04 05:39:52 +04:00
parent ff71d981ef
commit 08512ad58b
5 changed files with 254 additions and 10 deletions
+1 -1
View File
@@ -356,7 +356,7 @@ func (a *storeAPI) ListProposedRoutines(ctx context.Context) ([]ProposedRoutine,
Action: r.Action,
Object: r.Object,
IntervalDays: r.IntervalDays,
Status: r.Status,
Status: string(r.Status),
CreatedTs: r.CreatedTs.UnixMilli(),
}
if r.ReminderID != nil {