Skip to content

feat(boatstack): delivery.terminal — the standing goal becomes a typed setpoint - #239

Merged
bigboateng merged 3 commits into
mainfrom
boatstack/delivery-terminal
Jul 28, 2026
Merged

feat(boatstack): delivery.terminal — the standing goal becomes a typed setpoint#239
bigboateng merged 3 commits into
mainfrom
boatstack/delivery-terminal

Conversation

@bigboateng

Copy link
Copy Markdown
Contributor

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

  • New optional config block: delivery.terminal: "published" | "merged" (default published), validated fail-closed (validateDeliveryConfig), documented with boatstack-config-field markers in references/config-schema.md and a user-control section in boatstack-distribution/CONFIGURATION.md.
  • Resolution order (single resolver resolveDeliveryTerminal): state → config → default. Activation snapshots the non-default goal onto a new additive DeliveryState.Goal field (omitempty, schema version unchanged, preserved through reconcileAmendedDeliveryState), 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.Terminal surfaces the setpoint; FormatFlowNext renders a Terminal goal: merged line only for the widened goal. The oracle's Goal remains StatePublished — the delivery machine has no modeled transition past publish; Terminal is the setpoint layer above it.
  • No prescription, actor, or gate behavior changes in this slice.

Boundary conformance

  • Boundary: a persisted goal entering the read-only advisory layer.
  • Control law: 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 narrower published.
  • Required evidence: 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 explicit published, across BUILD/TEST_PASSED/REVIEW_PASSED/PUBLISHED), failure-state (pre-field state file loads clean and hydrates from config).
  • Failure behavior: fail-closed to published everywhere; config enum errors block at validation with an exact message.
  • Release condition: full suite green including config_documentation_test.go (schema + public doc markers, supported-user-controls list).

Evidence

  • go build ./... && go vet ./... && go test ./... — pass.
  • Release note added: 2026-07-28-configurable-delivery-terminal.md.

Next slice: post-publish prescriptions + actor typing under terminal: merged (watch → fix → merge, prescribe-only).

…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
Base automatically changed from boatstack/flow-watch to main July 28, 2026 16:38
…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
@bigboateng
bigboateng merged commit 4db2109 into main Jul 28, 2026
12 checks passed
@bigboateng
bigboateng deleted the boatstack/delivery-terminal branch July 28, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant