A-97: the seat mesh-builds accepts the build step and says its end

The accepted kind step (body StepRun: the pipeline, the branch, the source revision and each entry by module and
fingerprint) and the event step-ended.<node> (StepEnded: the same entries, the outcome), as mesh-wants has for its
composition step.

Builds: design 47 G A-97 (§230, §194, §473)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 00:04:41 +01:00
parent 84bcdb687a
commit 009c4c6e73
6 changed files with 384 additions and 1 deletions
+92 -1
View File
@@ -12,9 +12,14 @@ const (
VerbReserved = "reserved"
)
const (
AcceptStep = "step"
)
const (
EventPackagePublished = "package-published"
EventToolchainAgreed = "toolchain-agreed"
EventStepEnded = "step-ended"
)
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
@@ -24,7 +29,7 @@ func Verbs() []string {
// Events is every event published on the seat's subject: a new copy each call.
func Events() []string {
return []string{EventPackagePublished, EventToolchainAgreed}
return []string{EventPackagePublished, EventToolchainAgreed, EventStepEnded}
}
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
@@ -45,6 +50,26 @@ const StreamToolchainAgreed = "SEAT_mesh-builds_toolchain-agreed"
// SubjectToolchainAgreed is where toolchain-agreed is published.
const SubjectToolchainAgreed = "mesh.seat.mesh-builds.event.toolchain-agreed"
// StreamStepEnded is the stream step-ended is kept in.
const StreamStepEnded = "SEAT_mesh-builds_step-ended"
// SubjectStepEnded is where step-ended is published, on mesh.seat.mesh-builds.event.step-ended.<node>.
func SubjectStepEnded(node string) string { return "mesh.seat.mesh-builds.event.step-ended." + node }
// SubjectStepEndedAll hears every step-ended.
const SubjectStepEndedAll = "mesh.seat.mesh-builds.event.step-ended.*"
// AcceptSubjectStep is where step is sent.
const AcceptSubjectStep = "mesh.seat.mesh-builds.accept.step"
// BuildEntry is one entry a candidate's build builds: a module at the fingerprint of its build inputs, and the commit that fingerprint was read at.
type BuildEntry struct {
Commit string `json:"commit,omitempty"`
// the module's build-input fingerprint at the commit
Fingerprint string `json:"fingerprint"`
Module string `json:"module"`
}
// Dependency is a dependency the build resolved: one item of kind 1 for each resolved package and one of kind 3 for the toolchain, named by the build module in its step key and carried by the builder (design 47 G 374, 673).
type Dependency struct {
// the digest of the part, as the build used it
@@ -77,6 +102,66 @@ type PackagePublished struct {
Version string `json:"version"`
}
// StepEnded is the build step's end, said by the builder on the node it runs on (design 47 G A-97; ADR 0666 section 21): the runner takes it only from that holder's account, naming the ask and each entry's fingerprint it was asked at.
type StepEnded struct {
// for a rerun, the caller the runtime proved
By string `json:"by,omitempty"`
Ended *time.Time `json:"ended,omitempty"`
// each entry the step built, by module and fingerprint
Entries []BuildEntry `json:"entries"`
// the ask's id
ID string `json:"id"`
// the node the builder ran it on, as in the subject
Node string `json:"node"`
// never passed for a build that built nothing
Outcome string `json:"outcome"`
Pipeline string `json:"pipeline"`
Rerun bool `json:"rerun,omitempty"`
// why it failed or could not run: the entry not built, the part not heard
Said string `json:"said,omitempty"`
Started *time.Time `json:"started,omitempty"`
Step string `json:"step"`
// the layout version
V int64 `json:"v,omitempty"`
}
// The values of StepEnded's outcome.
const (
StepEndedOutcomePassed = "passed"
StepEndedOutcomeFailed = "failed"
StepEndedOutcomeNothingToRun = "nothing to run"
StepEndedOutcomeCouldNotRun = "could not run"
)
// The values of StepEnded's step.
const (
StepEndedStepBuild = "build"
)
// StepRun is the build step of one module's pipeline (design 47 G section 230, A-97): the runner asks the seat mesh-builds to build a candidate's entries, each named by its fingerprint, and the builder, the one entry point for a candidate, takes the ask and answers the step's end.
type StepRun struct {
// the watched branch, or the branch of a run on request
Branch string `json:"branch"`
// for a rerun, the caller the runtime proved
By string `json:"by,omitempty"`
// each entry the candidate's build builds, by module and fingerprint
Entries []BuildEntry `json:"entries"`
// the runner's pipeline key, <branch>.<revision>.<module>, a branch escaped as a key's name is
Pipeline string `json:"pipeline"`
// the step is asked again by the runner's rerun
Rerun bool `json:"rerun,omitempty"`
// the catalog graph revision of the branch the pipeline runs for
Revision int64 `json:"revision,omitempty"`
// the source revision of the watched branch; absent for a run on request
Source int64 `json:"source,omitempty"`
Step string `json:"step"`
}
// The values of StepRun's step.
const (
StepRunStepBuild = "build"
)
// ToolchainAgreedDigestsItem is a type of the contract.
type ToolchainAgreedDigestsItem struct {
// the toolchain part's digest
@@ -128,8 +213,14 @@ type ReservedAnswer struct {
Version string `json:"version,omitempty"`
}
// StepAccept is the body of step.
type StepAccept = StepRun
// PackagePublishedEvent is the body of the event package-published.
type PackagePublishedEvent = PackagePublished
// ToolchainAgreedEvent is the body of the event toolchain-agreed.
type ToolchainAgreedEvent = ToolchainAgreed
// StepEndedEvent is the body of the event step-ended.
type StepEndedEvent = StepEnded
@@ -21,6 +21,10 @@ func TestFixtures(t *testing.T) {
decode: func(b []byte) (any, error) { var v PackagePublishedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-builds\\.event\\.package-published$")},
{name: "a toolchain agreed", subject: "mesh.seat.mesh-builds.event.toolchain-agreed", given: "{\"at\":\"2026-11-10T10:00:00Z\",\"digests\":[{\"digest\":\"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"nodes\":[\"ace\",\"g14\"]},{\"digest\":\"sha256:fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210\",\"nodes\":[\"novox\"]}],\"language\":\"go\",\"v\":1,\"version\":\"1.26.0\"}", wire: "{\"at\":\"2026-11-10T10:00:00Z\",\"digests\":[{\"digest\":\"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"nodes\":[\"ace\",\"g14\"]},{\"digest\":\"sha256:fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210\",\"nodes\":[\"novox\"]}],\"language\":\"go\",\"v\":1,\"version\":\"1.26.0\"}",
decode: func(b []byte) (any, error) { var v ToolchainAgreedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-builds\\.event\\.toolchain-agreed$")},
{name: "the build step ended passed on the builder's node, each entry named by its fingerprint (A-97)", subject: "mesh.seat.mesh-builds.event.step-ended.novox", given: "{\"ended\":\"2026-11-10T10:01:00Z\",\"entries\":[{\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"module\":\"nats\"}],\"id\":\"m2kx9a1b3c4d6a\",\"node\":\"novox\",\"outcome\":\"passed\",\"pipeline\":\"main.12.nats\",\"started\":\"2026-11-10T10:00:00Z\",\"step\":\"build\",\"v\":1}", wire: "{\"ended\":\"2026-11-10T10:01:00Z\",\"entries\":[{\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"module\":\"nats\"}],\"id\":\"m2kx9a1b3c4d6a\",\"node\":\"novox\",\"outcome\":\"passed\",\"pipeline\":\"main.12.nats\",\"started\":\"2026-11-10T10:00:00Z\",\"step\":\"build\",\"v\":1}",
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-builds\\.event\\.step-ended\\.[^.*> ]+$")},
{name: "the build step failed on its entry", subject: "mesh.seat.mesh-builds.event.step-ended.novox", given: "{\"entries\":[{\"fingerprint\":\"fp-0123\",\"module\":\"nats\"}],\"id\":\"m2kx9a1b3c4d6b\",\"node\":\"novox\",\"outcome\":\"failed\",\"pipeline\":\"main.12.nats\",\"said\":\"nats:program did not build\",\"step\":\"build\",\"v\":1}", wire: "{\"entries\":[{\"fingerprint\":\"fp-0123\",\"module\":\"nats\"}],\"id\":\"m2kx9a1b3c4d6b\",\"node\":\"novox\",\"outcome\":\"failed\",\"pipeline\":\"main.12.nats\",\"said\":\"nats:program did not build\",\"step\":\"build\",\"v\":1}",
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-builds\\.event\\.step-ended\\.[^.*> ]+$")},
{name: "reserved asks a module", subject: "", given: "{\"module\":\"mesh-sdk\"}", wire: "{\"module\":\"mesh-sdk\"}",
decode: func(b []byte) (any, error) { var v ReservedInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "reserved asks a package by its name", subject: "", given: "{\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"version\":\"0.1.15\"}", wire: "{\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"version\":\"0.1.15\"}",
@@ -53,5 +53,47 @@
],
"at": "2026-11-10T10:00:00Z"
}
},
{
"name": "the build step ended passed on the builder's node, each entry named by its fingerprint (A-97)",
"of": "event:step-ended",
"subject": "mesh.seat.mesh-builds.event.step-ended.novox",
"given": {
"v": 1,
"id": "m2kx9a1b3c4d6a",
"pipeline": "main.12.nats",
"step": "build",
"node": "novox",
"outcome": "passed",
"entries": [
{
"module": "nats",
"commit": "0123456789abcdef0123456789abcdef01234567",
"fingerprint": "fp-0123"
}
],
"started": "2026-11-10T10:00:00Z",
"ended": "2026-11-10T10:01:00Z"
}
},
{
"name": "the build step failed on its entry",
"of": "event:step-ended",
"subject": "mesh.seat.mesh-builds.event.step-ended.novox",
"given": {
"v": 1,
"id": "m2kx9a1b3c4d6b",
"pipeline": "main.12.nats",
"step": "build",
"node": "novox",
"outcome": "failed",
"entries": [
{
"module": "nats",
"fingerprint": "fp-0123"
}
],
"said": "nats:program did not build"
}
}
]
+162
View File
@@ -78,6 +78,17 @@
}
}
],
"accepts": [
{
"name": "step",
"body": {
"$ref": "#/defs/StepRun"
},
"callers": [
"seat:mesh-pipeline-runner"
]
}
],
"events": [
{
"name": "package-published",
@@ -98,6 +109,17 @@
"body": {
"$ref": "#/defs/ToolchainAgreed"
}
},
{
"name": "step-ended",
"subject": "step-ended.<node>",
"body": {
"$ref": "#/defs/StepEnded"
},
"keep": {
"max-age-days": 90,
"max-mib": 32
}
}
],
"family": {
@@ -240,6 +262,146 @@
"digests"
],
"description": "the agreed toolchain of a language changed (design 47 G section 376, ADR 0584 section 1)"
},
"StepRun": {
"type": "object",
"description": "the build step of one module's pipeline (design 47 G section 230, A-97): the runner asks the seat mesh-builds to build a candidate's entries, each named by its fingerprint, and the builder, the one entry point for a candidate, takes the ask and answers the step's end",
"properties": {
"pipeline": {
"type": "string",
"description": "the runner's pipeline key, <branch>.<revision>.<module>, a branch escaped as a key's name is"
},
"step": {
"type": "string",
"enum": [
"build"
]
},
"branch": {
"type": "string",
"description": "the watched branch, or the branch of a run on request"
},
"revision": {
"type": "integer",
"description": "the catalog graph revision of the branch the pipeline runs for"
},
"source": {
"type": "integer",
"description": "the source revision of the watched branch; absent for a run on request"
},
"rerun": {
"type": "boolean",
"description": "the step is asked again by the runner's rerun"
},
"by": {
"type": "string",
"description": "for a rerun, the caller the runtime proved"
},
"entries": {
"type": "array",
"items": {
"$ref": "#/defs/BuildEntry"
},
"description": "each entry the candidate's build builds, by module and fingerprint"
}
},
"required": [
"pipeline",
"step",
"branch",
"entries"
]
},
"StepEnded": {
"type": "object",
"description": "the build step's end, said by the builder on the node it runs on (design 47 G A-97; ADR 0666 section 21): the runner takes it only from that holder's account, naming the ask and each entry's fingerprint it was asked at",
"properties": {
"v": {
"type": "integer",
"description": "the layout version"
},
"id": {
"type": "string",
"description": "the ask's id"
},
"pipeline": {
"type": "string"
},
"step": {
"type": "string",
"enum": [
"build"
]
},
"node": {
"type": "string",
"description": "the node the builder ran it on, as in the subject"
},
"outcome": {
"type": "string",
"enum": [
"passed",
"failed",
"nothing to run",
"could not run"
],
"description": "never passed for a build that built nothing"
},
"said": {
"type": "string",
"description": "why it failed or could not run: the entry not built, the part not heard"
},
"rerun": {
"type": "boolean"
},
"by": {
"type": "string",
"description": "for a rerun, the caller the runtime proved"
},
"started": {
"type": "string",
"format": "date-time"
},
"ended": {
"type": "string",
"format": "date-time"
},
"entries": {
"type": "array",
"items": {
"$ref": "#/defs/BuildEntry"
},
"description": "each entry the step built, by module and fingerprint"
}
},
"required": [
"id",
"pipeline",
"step",
"node",
"outcome",
"entries"
]
},
"BuildEntry": {
"type": "object",
"description": "one entry a candidate's build builds: a module at the fingerprint of its build inputs, and the commit that fingerprint was read at",
"properties": {
"module": {
"type": "string"
},
"commit": {
"type": "string"
},
"fingerprint": {
"type": "string",
"description": "the module's build-input fingerprint at the commit"
}
},
"required": [
"module",
"fingerprint"
]
}
}
}
+69
View File
@@ -8,9 +8,14 @@ export declare const verbs: {
readonly reserved: "reserved";
};
export declare const accepts: {
readonly step: "step";
};
export declare const events: {
readonly packagePublished: "package-published";
readonly toolchainAgreed: "toolchain-agreed";
readonly stepEnded: "step-ended";
};
export declare const family: { readonly name: "mesh-builds.events"; readonly maxAgeDays: 7 };
@@ -23,6 +28,22 @@ export declare const toolchainAgreedStream: "SEAT_mesh-builds_toolchain-agreed";
export declare const toolchainAgreedSubject: "mesh.seat.mesh-builds.event.toolchain-agreed";
export declare const stepEndedStream: "SEAT_mesh-builds_step-ended";
export declare function stepEndedSubject(node: string): string;
export declare const stepEndedSubjectAll: "mesh.seat.mesh-builds.event.step-ended.*";
export declare const stepSubject: "mesh.seat.mesh-builds.accept.step";
/** one entry a candidate's build builds: a module at the fingerprint of its build inputs, and the commit that fingerprint was read at */
export interface BuildEntry {
"commit"?: string;
/** the module's build-input fingerprint at the commit */
"fingerprint": string;
"module": string;
}
/** a dependency the build resolved: one item of kind 1 for each resolved package and one of kind 3 for the toolchain, named by the build module in its step key and carried by the builder (design 47 G 374, 673) */
export interface Dependency {
/** the digest of the part, as the build used it */
@@ -55,6 +76,48 @@ export interface PackagePublished {
"version": string;
}
/** the build step's end, said by the builder on the node it runs on (design 47 G A-97; ADR 0666 section 21): the runner takes it only from that holder's account, naming the ask and each entry's fingerprint it was asked at */
export interface StepEnded {
/** for a rerun, the caller the runtime proved */
"by"?: string;
"ended"?: string;
/** each entry the step built, by module and fingerprint */
"entries": BuildEntry[];
/** the ask's id */
"id": string;
/** the node the builder ran it on, as in the subject */
"node": string;
/** never passed for a build that built nothing */
"outcome": "passed" | "failed" | "nothing to run" | "could not run";
"pipeline": string;
"rerun"?: boolean;
/** why it failed or could not run: the entry not built, the part not heard */
"said"?: string;
"started"?: string;
"step": "build";
/** the layout version */
"v"?: number;
}
/** the build step of one module's pipeline (design 47 G section 230, A-97): the runner asks the seat mesh-builds to build a candidate's entries, each named by its fingerprint, and the builder, the one entry point for a candidate, takes the ask and answers the step's end */
export interface StepRun {
/** the watched branch, or the branch of a run on request */
"branch": string;
/** for a rerun, the caller the runtime proved */
"by"?: string;
/** each entry the candidate's build builds, by module and fingerprint */
"entries": BuildEntry[];
/** the runner's pipeline key, <branch>.<revision>.<module>, a branch escaped as a key's name is */
"pipeline": string;
/** the step is asked again by the runner's rerun */
"rerun"?: boolean;
/** the catalog graph revision of the branch the pipeline runs for */
"revision"?: number;
/** the source revision of the watched branch; absent for a run on request */
"source"?: number;
"step": "build";
}
export interface ToolchainAgreedDigestsItem {
/** the toolchain part's digest */
"digest": string;
@@ -104,9 +167,15 @@ export interface ReservedAnswer {
"version"?: string;
}
/** is the body of step. */
export type StepAccept = StepRun;
/** is the body of the event package-published. */
export type PackagePublishedEvent = PackagePublished;
/** is the body of the event toolchain-agreed. */
export type ToolchainAgreedEvent = ToolchainAgreed;
/** is the body of the event step-ended. */
export type StepEndedEvent = StepEnded;
+15
View File
@@ -8,9 +8,14 @@ export const verbs = {
reserved: "reserved",
};
export const accepts = {
step: "step",
};
export const events = {
packagePublished: "package-published",
toolchainAgreed: "toolchain-agreed",
stepEnded: "step-ended",
};
export const family = { name: "mesh-builds.events", maxAgeDays: 7 };
@@ -23,3 +28,13 @@ export const toolchainAgreedStream = "SEAT_mesh-builds_toolchain-agreed";
export const toolchainAgreedSubject = "mesh.seat.mesh-builds.event.toolchain-agreed";
export const stepEndedStream = "SEAT_mesh-builds_step-ended";
export function stepEndedSubject(node) {
return "mesh.seat.mesh-builds.event.step-ended." + node;
}
export const stepEndedSubjectAll = "mesh.seat.mesh-builds.event.step-ended.*";
export const stepSubject = "mesh.seat.mesh-builds.accept.step";