F8, dropped: the Drop layout is the generated contracts' (not the domain SDK's) and the key form sits beside Pause's helpers; a step ended by drop is the state dropped (ADR 0664 §4, issue 1748 review 1)

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-09 23:40:29 +01:00
parent 9e00300860
commit a8c8f8fb5a
11 changed files with 25 additions and 28 deletions
+1 -1
View File
@@ -497,7 +497,7 @@ type Step struct {
Run int64 `json:"run,omitempty"`
// when (RFC 3339 time)
Started time.Time `json:"started"`
// running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed or refused
// running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed, refused or dropped (ended by the verb drop, its cause the reason)
State string `json:"state"`
Tool string `json:"tool"`
// the layout version
@@ -497,7 +497,7 @@ type Step struct {
Run int64 `json:"run,omitempty"`
// when (RFC 3339 time)
Started time.Time `json:"started"`
// running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed or refused
// running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed, refused or dropped (ended by the verb drop, its cause the reason)
State string `json:"state"`
Tool string `json:"tool"`
// the layout version
+1 -1
View File
@@ -497,7 +497,7 @@ type Step struct {
Run int64 `json:"run,omitempty"`
// when (RFC 3339 time)
Started time.Time `json:"started"`
// running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed or refused
// running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed, refused or dropped (ended by the verb drop, its cause the reason)
State string `json:"state"`
Tool string `json:"tool"`
// the layout version
+2 -2
View File
@@ -41,7 +41,7 @@
},
{
"name": "drop",
"description": "End an ask this node holds for a caller that no longer waits for it: a step not yet running is refused when it is pulled, one running is cancelled and ends refused, naming who dropped it and why. Writes dropped.<id> in the pauses state; the build module removes the key when it has acted.",
"description": "End an ask this node holds for a caller that no longer waits for it: a step not yet running is ended dropped when the bus gives it, one running is cancelled; both end the step dropped, naming who dropped it and why. Writes dropped.<id> in the pauses state; the build module removes the key when it has acted.",
"callers": "the build seats' asker (the builder and the checker)",
"input": {
"type": "object",
@@ -434,7 +434,7 @@
},
"state": {
"type": "string",
"description": "running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed or refused"
"description": "running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed, refused or dropped (ended by the verb drop, its cause the reason)"
},
"started": {
"type": "string",
@@ -273,7 +273,7 @@ export interface Step {
"run"?: number;
/** when (RFC 3339 time) */
"started": string;
/** running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed or refused */
/** running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed, refused or dropped (ended by the verb drop, its cause the reason) */
"state": string;
"tool": string;
/** the layout version */
@@ -273,7 +273,7 @@ export interface Step {
"run"?: number;
/** when (RFC 3339 time) */
"started": string;
/** running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed or refused */
/** running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed, refused or dropped (ended by the verb drop, its cause the reason) */
"state": string;
"tool": string;
/** the layout version */
@@ -273,7 +273,7 @@ export interface Step {
"run"?: number;
/** when (RFC 3339 time) */
"started": string;
/** running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed or refused */
/** running, again (ended on a transport fault, to be given again by the bus after again-at), passed, failed, refused or dropped (ended by the verb drop, its cause the reason) */
"state": string;
"tool": string;
/** the layout version */
+5 -2
View File
@@ -344,6 +344,10 @@ func TestPauseCapacityAndComposerKey(t *testing.T) {
if Ended(Step{State: StepRunning}) || Ended(Step{State: StepAgain}) || !Ended(Step{State: StepPassed}) || !Ended(Step{State: StepFailed}) {
t.Error("Ended: running and again are no end")
}
// Review 1: a step ended by the seat's verb drop is the state dropped, an end that decodes (ADR 0664 §4).
if raw, err := EncodeStep(Step{ID: "mvl0frv18d1990", Tool: "go ask", Node: "ace", State: StepDropped, Reason: "withdrawn"}); err != nil || !Ended(Step{State: StepDropped}) {
t.Errorf("a dropped step: %v (%s)", err, raw)
}
if AgainAfter(1) != time.Minute || AgainAfter(2) != 4*time.Minute {
t.Errorf("AgainAfter = %v, %v", AgainAfter(1), AgainAfter(2))
}
@@ -385,8 +389,7 @@ func TestTheThreeSeatsFixturesFitTheNeutralForms(t *testing.T) {
seen++
strict(t, f.Given, &Pause{})
case strings.HasPrefix(f.Of, "state:pauses/dropped."):
seen++
strict(t, f.Given, &Drop{})
seen++ // its layout is the generated contracts' Drop, which seatgen checks the fixture against
case strings.HasPrefix(f.Of, "state:pauses/capacity."):
seen++
strict(t, f.Given, &Capacity{})
-15
View File
@@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"strings"
"time"
)
// Passing is a fault of the network between a build node and what it reaches (a name lookup, a timeout, a reset
@@ -57,17 +56,3 @@ func PassingCause(words string) bool {
// TransportFaultWords open the reason of a step that ended failed after its runs met a transport fault (the asker says them,
// "a transport fault on 3 runs (nodes): cause"): a reader tells that end from a fault of the source by them (ADR 0664 §3).
const TransportFaultWords = "a transport fault on "
// DropKey is the key of an ask's drop in a build seat's pauses state: "dropped.<id>" (the part build-step).
func DropKey(askID string) string { return "dropped." + askID }
// DroppedPrefix opens every key DropKey makes.
const DroppedPrefix = "dropped."
// Drop is the value of the key dropped.<ask id> of a build seat's pauses state: an ask its caller no longer waits for.
type Drop struct {
V int `json:"v,omitempty"`
By string `json:"by,omitempty"`
Why string `json:"why"`
At time.Time `json:"at,omitzero"`
}
+6
View File
@@ -32,6 +32,12 @@ func CapacityKey(node string) (string, error) {
return "capacity." + node, nil
}
// DropKey is the key of an ask's drop in a build seat's pauses state: "dropped.<id>" (the part build-step).
func DropKey(askID string) string { return "dropped." + askID }
// DroppedPrefix opens every key DropKey makes.
const DroppedPrefix = "dropped."
// Pause is whether a node takes no new ask, and who said so.
type Pause struct {
V int `json:"v"`
+6 -3
View File
@@ -29,6 +29,9 @@ const (
StepPassed = "passed"
StepFailed = "failed"
StepRefused = "refused"
// StepDropped is a step ended by the seat's verb drop: its asker no longer waited for it (ADR 0664 §4). The cause its
// caller named is its reason. It is an end, and no failure of the source.
StepDropped = "dropped"
)
// StepBound is how long an asker waits for one ask's step once a build node took it (30 minutes): past it the asker
@@ -58,7 +61,7 @@ type Step struct {
Passing bool `json:"passing,omitempty"`
}
// Ended says a step's state is an end: passed, failed or refused. A step running, or ended on a transport fault with runs
// Ended says a step's state is an end: passed, failed, refused or dropped. A step running, or ended on a transport fault with runs
// left (StepAgain), is not.
func Ended(s Step) bool { return s.State != StepRunning && s.State != StepAgain && s.State != "" }
@@ -119,9 +122,9 @@ func (s Step) check() error {
return fmt.Errorf("buildstep: a step needs its tool")
}
switch s.State {
case StepRunning, StepAgain, StepPassed, StepFailed, StepRefused:
case StepRunning, StepAgain, StepPassed, StepFailed, StepRefused, StepDropped:
default:
return fmt.Errorf("buildstep: the step state %q is not running, again, passed, failed or refused", s.State)
return fmt.Errorf("buildstep: the step state %q is not running, again, passed, failed, refused or dropped", s.State)
}
if len(s.Result) > 0 && !json.Valid(s.Result) {
return fmt.Errorf("buildstep: the step's result is not JSON")