feat(boatstack): delivery.terminal — the standing goal becomes a typed setpoint - #239
Merged
Conversation
…rontier New `flow watch` verb: re-observe the read-only frontier on an interval and exit on the first row-signature change (position, actor, lifecycle, failing checks), immediately when nothing can move, or at the deadline with a distinct exit code (1) so callers can tell change from timeout. Time flows through injectable seams so the loop is provable without real waiting; a failing gh observation degrades rows to Unknown and the loop stays bounded. The watch observes and exits — it never executes a transition and never writes, pinned across ticks including a terminal MERGED observation. SKILL.md and workflow.md document composing watch with the loop (watch exits → next-status → continue). control-law: watch-observes-and-exits-never-acts Disclosure-Reviewed: reviewed — public-safe only, private facet kept out of this commit
…d setpoint New optional config block delivery.terminal: "published" (default, byte- identical behavior pinned by conformance) or "merged" (the flow's advisors keep reporting the standing goal past publish). Resolution is state-then- config-then-default: activation snapshots the non-default goal onto DeliveryState.Goal (additive omitempty, no schema bump, preserved across plan amendments), so a mid-flight config change never changes an in-progress delivery's goal and a fresh session hydrates the goal from the repository instead of the operator restating it. FlowNext gains a Terminal field; only the widened goal earns a rendered line. The oracle's Goal stays StatePublished — the delivery machine models no transition past publish; Terminal is the operator-facing setpoint layer. Every invalid or unreadable input resolves to the narrower published goal. control-law: terminal-goal-defaults-to-published-and-hydrates-from-state-then-config Disclosure-Reviewed: reviewed — public-safe only, private facet kept out of this commit
…golden test The byte-identical default-vs-published comparison blanks the per-fixture temp repo path out of both captures; on Windows the JSON capture carries the path with escaped backslashes, so the raw-path replacement missed it and the two fixtures diverged on their temp paths alone. Blank both the raw and the JSON-escaped forms. Disclosure-Reviewed: reviewed — public-safe only, private facet kept out of this commit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth slice of the published → merged program (stacked on #238). The root cause of the repeated "make PR, watch CI, merge" prompt is that the operator is the only component holding the goal. This PR makes the goal a typed, persisted setpoint — without changing a single prescription yet.
What changed
delivery.terminal: "published" | "merged"(defaultpublished), validated fail-closed (validateDeliveryConfig), documented withboatstack-config-fieldmarkers inreferences/config-schema.mdand a user-control section inboatstack-distribution/CONFIGURATION.md.resolveDeliveryTerminal): state → config → default. Activation snapshots the non-default goal onto a new additiveDeliveryState.Goalfield (omitempty, schema version unchanged, preserved throughreconcileAmendedDeliveryState), giving hysteresis: a delivery keeps the goal it was started under; a default-config state file stays byte-identical to the pre-field format.FlowNext.Terminalsurfaces the setpoint;FormatFlowNextrenders aTerminal goal: mergedline only for the widened goal. The oracle'sGoalremainsStatePublished— the delivery machine has no modeled transition past publish; Terminal is the setpoint layer above it.Boundary conformance
terminal-goal-defaults-to-published-and-hydrates-from-state-then-config— the default is a hard no-op; the goal widens only by explicit operator choice; invalid/unreadable inputs resolve to the narrowerpublished.delivery_terminal_conformance_test.go— positive (merged config surfaces + renders; activation snapshots non-default only), relation (state.Goal overrides config both directions; invalid snapshot ignored), negative (invalid enum fails validation; empty stays legal), bypass (per-stage byte-identical JSON + rendering between absent block and explicitpublished, across BUILD/TEST_PASSED/REVIEW_PASSED/PUBLISHED), failure-state (pre-field state file loads clean and hydrates from config).publishedeverywhere; config enum errors block at validation with an exact message.config_documentation_test.go(schema + public doc markers, supported-user-controls list).Evidence
go build ./... && go vet ./... && go test ./...— pass.2026-07-28-configurable-delivery-terminal.md.Next slice: post-publish prescriptions + actor typing under
terminal: merged(watch → fix → merge, prescribe-only).