Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Contributing

Boatstack is a generated content distribution. Propose changes to workflow semantics, templates, evidence rules, or generated presentation in [Intelligence Flow](https://github.com/operatorstack/intelligence-flow/tree/5850912e0d947f9fbee5048d9ed6a79a5d614ff1/labs/12-product-engineering-loop).
Boatstack is a generated content distribution. Propose changes to workflow semantics, templates, evidence rules, or generated presentation in [Intelligence Flow](https://github.com/operatorstack/intelligence-flow/tree/4db21095ddbad477daa4a04cd4d0827d9ba2fb8b/labs/12-product-engineering-loop).

The Boatstack repository receives product/runtime changes through a generated pull request. Review the PR's `UPSTREAM.json`, tests, adapter diff, and context-size change; do not hand-edit generated output on `main`. `.github/workflows` is the exception: it is Boatstack's executable control plane, excluded from scheduled projection and changed only through a separate manually reviewed Boatstack PR.

Expand Down
27 changes: 15 additions & 12 deletions UPSTREAM.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"files": {
".gitignore": "a7079e923a776f14f1bb3a6aa0a11a133a8e1dfb35af020f327623357b7e3957",
"CONTRIBUTING.md": "16a84cf6a740fe70a920537a534dfb11f00273204d83a9f8f23661dca7d5331d",
"CONTRIBUTING.md": "eac65d45c4f20dd219708eeee3f435d37663fbf23e6383cca97b1324e0d0d781",
"README.md": "3ce3e95e511089b44e946a44b8d5f4f81d019ece5336db65b2cab1f9dc4d4dad",
"assets/boatstack-journey.svg": "e465befc50c8ce30f3e07e8fd97012931beeb053392c8fbf38ad645023b3cc63",
"assets/boatstack-mark.svg": "be1f984da1bfa69fa5d1f986d8343d21f7e20921b71db888c928b4d2e54b09b5",
Expand Down Expand Up @@ -49,16 +49,18 @@
"boatstack/command.go": "4726ac515dedab4947be7eb48f88c6cb8b53d674124504b69f03e6396b080ee8",
"boatstack/command_test.go": "9f707abba3640add81c3e97ba7e72fedbf98f3394b1c060a9ca4b4a28e919968",
"boatstack/compiled_artifact_resolution_test.go": "0748d67643263e698211eb04d46464e1dd3db15d94537f5fd5092b5aa689745b",
"boatstack/config_documentation_test.go": "aaecea04ee178ecf7872fff23a64014a3968dce1e7624e61a9453567e99969a9",
"boatstack/config_documentation_test.go": "cb0ab6f13162909a0a0b60bddc0dfb10b3492639b336a4d2eb36e819c25cc3be",
"boatstack/content_effect_conformance_test.go": "ebf4f6d50af0a76722177c717c79d33921948b9c06bec2e9d062769dce32b8aa",
"boatstack/context.go": "02510af176d2d040c0080086f06d1235e76a1d47fef5176f96f740ad18d27660",
"boatstack/decision.go": "257ca328da6ae19ab252f10ee5d06bd7daf49dd8141d083ab1b32f106ea7a94c",
"boatstack/decision_test.go": "1a92ff832610f9559bd47ccac7fc1755a8b4f8261c35bc72a092830dff05f7c0",
"boatstack/delivery.go": "1cbc917eaa7df569f09c71352db157dff743827d5310dccb63acb7be72ccf9d5",
"boatstack/delivery.go": "a9a266d0413b25276d9bcfeb8328ac3ca4b3020ada1f97912251f68b4a9b5019",
"boatstack/delivery_boundary_conformance_test.go": "53dde765046420b9119e82034d137742e600019938ed908c608f725d8a0c84c6",
"boatstack/delivery_migrate.go": "7566e49f9c1838d4d563866e941c7aacd61ac918c9e886222282398d287ca780",
"boatstack/delivery_migrate_conformance_test.go": "b8ba53681e1d0361ac62b06586c62b7763d55a65b5427976b5289e1fb1503bdc",
"boatstack/delivery_reactivation_test.go": "573a2dba0034bc4290478414e3bdd8670b06a326128eb0295d77e748ecc8689e",
"boatstack/delivery_terminal.go": "56fd8bec4d2c00ee3757bde5999417e5be763ab83cd7b68d4bccbbdea23af26a",
"boatstack/delivery_terminal_conformance_test.go": "50b88bdfb9bdfc9dc50c11f46276e1eb13eed61618e75b4c951dfa2ac6075df0",
"boatstack/delivery_test.go": "45c48ff7581c911bcaf821c3e4241d4ae2a9bb4aa682485cc58b6ad8fe1c85bf",
"boatstack/deliverycontrol_parity_test.go": "f8662cfc35043395a0e1eef8a87051c2120752f38b09c56b78f82896008f1b65",
"boatstack/denial.go": "ce77240edafb2589565ff3821b4e2cf8be3cf5884b1467d606265ca877188d3e",
Expand All @@ -71,11 +73,11 @@
"boatstack/detached_test.go": "6cc70d15baa9a69afacf66ea29ce112efeb166836acb0a52bf9c4bb4c898cee5",
"boatstack/docs/control-law-scoping.md": "0ae984821248eabda8c0eeaf201b367991e6742984e7c718df20ecc24caee475",
"boatstack/evidence.go": "497a31e6ff632cb1d7c3adfc9f269af3f6aa84e948dd5d417c162767542a27df",
"boatstack/export.go": "ca5c284fc658b112f58145e80b2bbfdd33a72b153a97922e2cf8c3f6e98fefb3",
"boatstack/export.go": "12c4e8c3b0692736ead73c613e0410b0ed5f7cb4c6b6a9fcc96e59547f2fe566",
"boatstack/export_test.go": "dce5aa3ab5499c82d05859cf86b46dfcee308482491366d83e10ca3fb8605bb6",
"boatstack/flow_coding.go": "9fa53a0204f98a25f97775c3acf37392a591c14ce850b44aa587b5806e770bb9",
"boatstack/flow_coding_test.go": "dddcd7a85892d4fa10af42739d4c1ff265721b0313e27b6e7a1bbb019d5c3b51",
"boatstack/flow_control.go": "43204356017b837148bd1505779611fbe781097839c2944f41a437a4ec755a82",
"boatstack/flow_control.go": "193dcae29e0611001b33ea23011beeb921d626562a5c733658c585ebca62b7fe",
"boatstack/flow_control_test.go": "02d788c83be55ebd79ffc73875bfd019de45325151eb1f70f506980eb8e77f29",
"boatstack/flow_drive.go": "a501ceda390dfd3605e22cf7ecfa15f9d50240b3fac6ebb2bb2d80c615d0a9fc",
"boatstack/flow_drive_conformance_test.go": "23edea926c271a1f5718fb9dae1da11e4bf03cceb1357290cd61cd8ffb73beda",
Expand Down Expand Up @@ -164,7 +166,7 @@
"boatstack/reexec_unix.go": "ff86157a9aa20c82a56fcd859b70669b7eacf4e0a9f61a4546ef33808437939e",
"boatstack/reexec_windows.go": "f5335c8c28cb4e89048b058b1c4d12f78644f99acb4f6167ff60e622dfb9e742",
"boatstack/references/artifacts.md": "4f27170227ffa2715d632eab4cbdca229726c5fc03c4b0f2af980fa50bb24725",
"boatstack/references/config-schema.md": "eff8586850eca9941df1cea29ac7edb779277ed9bd6d938a1980db5028d28cf4",
"boatstack/references/config-schema.md": "a6860ec7e1cd155e7e36dc465b8cae79de896714751e1c04dfa68d3f5c4a45e0",
"boatstack/references/failure-moves.md": "b65ef72035afa6ad0dce589a0b38f84bc40cde3864c9ecf973f08fc687f001c3",
"boatstack/references/host-hook-contracts.md": "2a89d44d0e418a53f2e3b6300fed957cdf878f45ea97ce24b55b66065f0eaa1d",
"boatstack/references/irreversible-operation-boundary.md": "e0076f0fea3bf729b2e9bdf353eaeaaf7cdafabfaf26b8d9b27287e5414c2441",
Expand All @@ -175,7 +177,7 @@
"boatstack/repair_state_test.go": "f3779ac47c3db3927175a545728d3b2e020dbc85f41394d8235753b52afc3739",
"boatstack/run.go": "3127e8c054b80e41413011e423ba9463fe06a7e6c9e1e71750ab98587871d89d",
"boatstack/run_test.go": "5b291510fa90cefdc26eb89e18a3443385456a6ebc73408325ac1945b7c084d6",
"boatstack/runtime.go": "7dc5d033ec11bbcf9a4561da66d4d6692a071bc79ac2fe4eb66feaced358d3c3",
"boatstack/runtime.go": "368bb43a0e3042bde2d4bab1b62df560a93f5928384c7c8f5e27e8a836628af4",
"boatstack/runtime_cache.go": "e026ffc1906f7e1e98b768bae63e6658164d2826c07169c9121ce0f23c73faf8",
"boatstack/runtime_cache_test.go": "b981467ddc9f0f562da6bff5de7a80a9fe5a433a0317541d1e48df268546ac85",
"boatstack/runtime_provenance_test.go": "1d52f1e6b0691cf4667729cc9b9f3c55c128f0aa3321f3a2843a9aa6fd0e73dc",
Expand Down Expand Up @@ -209,11 +211,11 @@
"docs/account-recovery-walkthrough.md": "676034974594a7d1a559b24dbed31d7ccc429eb81404b203ca07bbdaa19ec3d3",
"docs/benchmark-corpus-audit.md": "f2d206fe8579a514f9da82b2c96c19b343ac004be67617e1bd34f0f8e0e5e6c6",
"docs/benchmark-submission-audit.md": "9518abdd17690729c6423f87cab20418ed47b0915b5faa44b9ef975e9e9c3b79",
"docs/configuration.md": "060775c73431f28bd16066bdf9e0f89034d2855c7ca0f5544f660d24b91211d0",
"docs/evidence-engineered-coding.md": "f0567d8bb1fdf42d78ca7dac28603042a78e2290487a6fce8518ff16b8307fd2",
"docs/configuration.md": "221f979506a3a9de357e5277f1329c345bf175346ec8dfc8fdd1212fb100dea1",
"docs/evidence-engineered-coding.md": "223d5b3a75c66eccbaa8d4a0b755bfb873fe0684106004b841e8319653e702ef",
"docs/generated-files.md": "437791765b0a4015032ae21d1a6618563cad92b7402819e4f963bf5ae16284a3",
"docs/getting-started.md": "51c2823f21e35140d31e6d5083dc4b89fddd24721ac6acc474154a4da53ee9f8",
"docs/public-claims.json": "ff6bba756adaa499998016d2e4986c7fdfe7bc6f5d3ae7531bbc4471a6371a8b",
"docs/public-claims.json": "9f640af54667625048eddfbb78dd63a0c055c556affc4c105d186ca164467e2f",
"docs/public-surface.md": "713f7a050b5f339cf948299103ef3800417dccfecf2cc1a4166397ea6f978907",
"docs/research-and-design.md": "8d78678108f0a6c924e1ff9b32c0f81aae9d1f779e0082843b6f99ad993ae2b6",
"docs/safety.md": "7b9b5c515d36e683767ec8d3d9d6d119ac93650b2f629d351deadd4c600ed6a6",
Expand All @@ -227,7 +229,7 @@
"labs/diagram-json/compiled/evidence.md": "1ba1c989ade070a8ef9a508fbd788d100d7292f2dbacbb2bce895468019f619d",
"labs/diagram-json/compiled/tasks.json": "88f60851abf79d851e9fccc754ff3040034ae595306bc87d64784c19eb403e71",
"labs/diagram-json/compiled/test-matrix.json": "424657ff505768e50fa113801fd8363364a18269d5297480907a993d44063a39",
"labs/diagram-json/plan.lock.json": "73fe60031dd4c5b25b28fbbb29654416359a4040b1d7bf124618aede0e482633",
"labs/diagram-json/plan.lock.json": "030278e2649c63a717a744ef91926fef25fc1648cedef91b4d2edafaf9e8d4ce",
"labs/diagram-json/plan.md": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51",
"labs/diagram-json/questions.md": "74733b015002c8a6777c558e7e997fa48c94850b9bd39054fe9366c97ecf728d",
"labs/diagram-json/request.md": "0808fc41c36779c404f4a3a121167da6e76cac56df526e70f9ed6d3e0d4c02ed",
Expand Down Expand Up @@ -356,6 +358,7 @@
"release-notes/2026-07-27-repeated-denials-escalate.md": "ee54b597e593ce74d8d9acbc67c74d2d8cb972ff206f618e08a047d4d962d7af",
"release-notes/2026-07-27-sandboxed-migration-grading.md": "03cebc372bbdfed37cc70d18f3b6374d1aa5e585bafefa073dbcced58bd0336a",
"release-notes/2026-07-27-state-ownership-map.md": "d032547aafc1a4acbeb520f6cbb59d7757de4f33fe824701d7b5ea8cd8c8b9e7",
"release-notes/2026-07-28-configurable-delivery-terminal.md": "54f42d8ee04f16429f7f6db484c12247a75dd365d9028ec6c24b2efb92f9e700",
"release-notes/2026-07-28-flow-frontier-dashboard.md": "9a768e0fd67f122bc0aef99899314f0a8530189164fb560b536ee03f20e29081",
"release-notes/2026-07-28-flow-watch-loop.md": "54833887e733c65626c3bb7f0e6430eca8028f0fe19aa2cd31cdbdcc6ff4d8ba",
"release-notes/2026-07-28-minimum-app-permissions.md": "9ef97e32e5591966ef34ba23f4e0a7aa14d061a4ac5f72f5f4dcecc9bfb62a89",
Expand All @@ -367,7 +370,7 @@
"generator": "operatorstack/intelligence-flow:boatstack-distribution",
"schema_version": 1,
"source": {
"commit": "5850912e0d947f9fbee5048d9ed6a79a5d614ff1",
"commit": "4db21095ddbad477daa4a04cd4d0827d9ba2fb8b",
"path": "labs/12-product-engineering-loop",
"repository": "operatorstack/intelligence-flow"
}
Expand Down
1 change: 1 addition & 0 deletions boatstack/config_documentation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func TestSerializedConfigurationSurfaceIsDocumentedInternally(t *testing.T) {
func TestPublicConfigurationGuideContainsOnlySupportedUserControls(t *testing.T) {
want := []string{
"adapters",
"delivery.terminal",
"project.commands",
"project.context",
"project.default_branch",
Expand Down
9 changes: 9 additions & 0 deletions boatstack/delivery.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ type DeliveryState struct {
RepairAttempt int `json:"repair_attempt,omitempty"`
SupersededReceipts []string `json:"superseded_receipts,omitempty"`
ParentDelivery string `json:"parent_delivery,omitempty"`
// Goal snapshots the non-default delivery terminal ("merged") this
// delivery was activated under, so a mid-flight config change never
// silently changes an in-progress delivery's goal. Empty means: resolve
// from config at read time (and keeps a default-config state file
// byte-identical to the pre-field format).
// control-law: terminal-goal-defaults-to-published-and-hydrates-from-state-then-config
Goal string `json:"goal,omitempty"`
}

type DeliveryGateReceipt struct {
Expand Down Expand Up @@ -377,6 +384,7 @@ func initializeDeliveryState(repo, feature, planPath, lockPath string) error {
SchemaVersion: deliveryStateSchemaVersion, Feature: feature, PlanLockHash: lockHash,
ActiveIndex: 0, Slices: slices, Mode: "NORMAL",
ParentDelivery: strings.TrimSpace(stringValue(plan["parent_delivery"])),
Goal: deliveryGoalSnapshot(repo),
})
}

Expand Down Expand Up @@ -485,6 +493,7 @@ func reconcileAmendedDeliveryState(existing DeliveryState, newSlices []DeliveryS
Slices: merged,
Mode: "NORMAL",
ParentDelivery: existing.ParentDelivery,
Goal: existing.Goal,
}
}

Expand Down
71 changes: 71 additions & 0 deletions boatstack/delivery_terminal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package boatstack

import "strings"

// The delivery terminal is the standing goal of the flow — the state past
// which nothing more is owed. It resolves in a fixed order: the goal the
// delivery was ACTIVATED under (state.Goal — hysteresis, so a mid-flight
// config change never silently changes an in-progress delivery's goal), then
// the repository config (delivery.terminal), then the published default.
// Every unreadable or invalid input resolves to the narrower published goal:
// a goal is widened only by an explicit, verifiable operator choice.
// control-law: terminal-goal-defaults-to-published-and-hydrates-from-state-then-config
type DeliveryTerminal string

const (
// TerminalPublished — the flow is done when the slice's PR is open.
TerminalPublished DeliveryTerminal = "published"
// TerminalMerged — the flow keeps naming read-only post-publish steps
// until the PR is observed merged.
TerminalMerged DeliveryTerminal = "merged"
)

func normalizeDeliveryTerminal(value string) (DeliveryTerminal, bool) {
switch strings.ToLower(strings.TrimSpace(value)) {
case string(TerminalPublished):
return TerminalPublished, true
case string(TerminalMerged):
return TerminalMerged, true
default:
return "", false
}
}

// configuredDeliveryTerminal reads the repository's standing terminal from
// the project config. Absent, invalid, or unreadable configuration resolves
// to published — never an error, because the terminal is consulted from
// read-only paths that must not gain a new failure mode.
func configuredDeliveryTerminal(repo string) DeliveryTerminal {
config, _, err := LoadConfig(WorkspaceFor(repo).ProjectConfigPath())
if err != nil || config.Delivery == nil {
return TerminalPublished
}
if terminal, ok := normalizeDeliveryTerminal(config.Delivery.Terminal); ok {
return terminal
}
return TerminalPublished
}

// resolveDeliveryTerminal resolves the terminal for one feature: the
// activation snapshot first, then config, then the default.
func resolveDeliveryTerminal(repo, feature string) DeliveryTerminal {
if strings.TrimSpace(feature) != "" {
if state, err := LoadDeliveryState(repo, feature); err == nil {
if terminal, ok := normalizeDeliveryTerminal(state.Goal); ok {
return terminal
}
}
}
return configuredDeliveryTerminal(repo)
}

// deliveryGoalSnapshot is what activation records on the new delivery state.
// Only the non-default goal is snapshotted: a default-config delivery keeps
// an empty Goal, so its persisted state is byte-identical to before this
// field existed.
func deliveryGoalSnapshot(repo string) string {
if configuredDeliveryTerminal(repo) == TerminalMerged {
return string(TerminalMerged)
}
return ""
}
Loading
Loading