Skip to content
Closed
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/804141bc65d66fdb0a422a9c7c545a71180bffb1/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/d4c1a8ecc53f1bf82d3974f533b02eb0b9f93221/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
25 changes: 14 additions & 11 deletions UPSTREAM.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
},
"files": {
".gitignore": "a7079e923a776f14f1bb3a6aa0a11a133a8e1dfb35af020f327623357b7e3957",
"CONTRIBUTING.md": "c7f3497bbe061860f2ecec201bbb9526f045585371c5f62db2b1b5ce17362bc5",
"CONTRIBUTING.md": "204670c493aed31a5a0932d1075e2982bdf6e318c33d372b7dd2c28c424f3a31",
"README.md": "3ce3e95e511089b44e946a44b8d5f4f81d019ece5336db65b2cab1f9dc4d4dad",
"assets/boatstack-journey.svg": "e465befc50c8ce30f3e07e8fd97012931beeb053392c8fbf38ad645023b3cc63",
"assets/boatstack-mark.svg": "be1f984da1bfa69fa5d1f986d8343d21f7e20921b71db888c928b4d2e54b09b5",
"assets/boatstack-portability.svg": "66dfdfa85db857b3bd18b32047a6975f1fbbfc4dc091158e8277193f9969a346",
"boatstack/AGENTS.md": "39574398c3c3f82c22077299b45fd46a587926e1c9a35b66998c65ab8a756554",
"boatstack/AGENTS.md": "bc76221e1fe90a91afbacd7c6bc9b41a70e6c10fc128c275a6a0b9bc094d9506",
"boatstack/BUG-worktree-delivery-state.md": "02469cf51c3849dad5743783e248e5c04583e4240507fbef0e3f890cd6a95724",
"boatstack/SKILL.md": "b393fe00f23f701e1310d7c1006f339935d3082c7adb9b35c038a3ad1bcc459e",
"boatstack/agents/gemini.yaml": "cbf43b387399e456fa6178f86d83e6e35567e6142ff800f8de6ffca306fa963e",
Expand Down Expand Up @@ -50,13 +50,14 @@
"boatstack/config_documentation_test.go": "0632366edc5e88145bb080083ea03c6515da07b0162ce404d63e51bb5bc0774e",
"boatstack/decision.go": "257ca328da6ae19ab252f10ee5d06bd7daf49dd8141d083ab1b32f106ea7a94c",
"boatstack/decision_test.go": "1a92ff832610f9559bd47ccac7fc1755a8b4f8261c35bc72a092830dff05f7c0",
"boatstack/delivery.go": "9bdcfecae7564c34a0d374ddbba4f237b241085db5c9d5bda6c4afb801055b35",
"boatstack/delivery_boundary_conformance_test.go": "800cd722d8d2a696a0529e8343d3523e453bb052f0917c8a2cad2990296ac1b3",
"boatstack/delivery.go": "86150374b14982b1e589714d6ef6230348ef57b6824d4e1552b72289c044af4b",
"boatstack/delivery_boundary_conformance_test.go": "c374eddf49b4597db78c0621f65f87199de9a26f1872ed88d9d790edf21fe3f2",
"boatstack/delivery_migrate.go": "7566e49f9c1838d4d563866e941c7aacd61ac918c9e886222282398d287ca780",
"boatstack/delivery_migrate_conformance_test.go": "b8ba53681e1d0361ac62b06586c62b7763d55a65b5427976b5289e1fb1503bdc",
"boatstack/delivery_reactivation_test.go": "573a2dba0034bc4290478414e3bdd8670b06a326128eb0295d77e748ecc8689e",
"boatstack/delivery_test.go": "45c48ff7581c911bcaf821c3e4241d4ae2a9bb4aa682485cc58b6ad8fe1c85bf",
"boatstack/deliverycontrol_parity_test.go": "f8662cfc35043395a0e1eef8a87051c2120752f38b09c56b78f82896008f1b65",
"boatstack/docs/control-law-scoping.md": "0ae984821248eabda8c0eeaf201b367991e6742984e7c718df20ecc24caee475",
"boatstack/evidence.go": "497a31e6ff632cb1d7c3adfc9f269af3f6aa84e948dd5d417c162767542a27df",
"boatstack/export.go": "b3e28b571024b1b7a97b28f226f7c89734c95dcf1854c3d1a6dc672f34d4ded7",
"boatstack/export_test.go": "dce5aa3ab5499c82d05859cf86b46dfcee308482491366d83e10ca3fb8605bb6",
Expand All @@ -77,7 +78,7 @@
"boatstack/flow_trace_test.go": "99f89a831e904f6a8ef710b6977ed3a808ce1c7ddfaba457b292d84f2ddca51b",
"boatstack/go.mod": "6086ef1b2a83f5696190dca692c653925f27b61f652f659fd3fca43ed54a1641",
"boatstack/go.sum": "26c315c867b11b886f3c9402fce7f341f6a9115a5d61f54afbb5e1b1fb5f6017",
"boatstack/hooks.go": "c8606417aec79fdcf84b3420758e7d491c3757e7b3117c7fc8df2bf4b0733e03",
"boatstack/hooks.go": "bed08eeaf80cc6c953c49463ffd5a6cf7bad595e8551e8d41c0a1580803c03cf",
"boatstack/hooks_hydrate_test.go": "7beeb26b2b1398741e8a28963a9686e974047016cc736f233024004add1afc32",
"boatstack/hooks_test.go": "fb75e3aabf2204871b3e6d16de98d26fb33b0ec19e41aae761cf1f34397c31f4",
"boatstack/hydrate_runtime_test.go": "dbd5eae2ba85701e4af0430ba3a0d70ea98e028b66992bd4fc05f3f582398627",
Expand Down Expand Up @@ -130,7 +131,7 @@
"boatstack/provision_test.go": "214e9edb991a66d5bbb696a7c1b63876d2f799f2cab4e3f40785f4e8f1eac57b",
"boatstack/publication_ignored_repro_test.go": "b6f3aeb8ba22949ff9af7ac5afe8fb828385d9708d5d5893ef41f33a3de873e1",
"boatstack/published_slice_routing_test.go": "ea7e7351018bc13dcd31c4b96f50f8bc230e8a1dbf7806fba32a12ae58923e7e",
"boatstack/recovery.go": "7c06cdb52a31125cf3b944c304eca1df2273edc763242112527798bfb114874f",
"boatstack/recovery.go": "e45b3b3c2cda85c2b887fae32ee46ad205f1f3f707e6dc7b46f68ef6746ab5aa",
"boatstack/recovery_test.go": "29490e7477ba602491330036a491289dd9117b99ff862f66dae421ba17e04c9f",
"boatstack/reexec.go": "fed55416479d7bd3e0c3637057ffe8eb58a032f93fc358f76df906ab7acc677b",
"boatstack/reexec_unix.go": "ff86157a9aa20c82a56fcd859b70669b7eacf4e0a9f61a4546ef33808437939e",
Expand Down Expand Up @@ -176,10 +177,10 @@
"docs/benchmark-corpus-audit.md": "f2d206fe8579a514f9da82b2c96c19b343ac004be67617e1bd34f0f8e0e5e6c6",
"docs/benchmark-submission-audit.md": "9518abdd17690729c6423f87cab20418ed47b0915b5faa44b9ef975e9e9c3b79",
"docs/configuration.md": "df054f49d532c8b1b7d94184810d1b3b5bf18cdc30eb985b4b6d0639162e341a",
"docs/evidence-engineered-coding.md": "8c9ac13f925f67db325db9163a1384aa01591d17af82e47e7447922585f62e16",
"docs/evidence-engineered-coding.md": "4cbdb995edc8e285b4312ab9815808d886097f6d179de26602def7d142830deb",
"docs/generated-files.md": "437791765b0a4015032ae21d1a6618563cad92b7402819e4f963bf5ae16284a3",
"docs/getting-started.md": "1dd4f4e2e636cc5adfc2f79939629701e171087c3d5e558cf919548b9224adfd",
"docs/public-claims.json": "16d2327409e3bdaefab872aa46abeb88328a8ec263302ed0a4d5d48df431a392",
"docs/public-claims.json": "2d9f5e9e4fed027683d2a46e3f90e7f44d9bc5e878de977b79cd558c3e095b06",
"docs/public-surface.md": "713f7a050b5f339cf948299103ef3800417dccfecf2cc1a4166397ea6f978907",
"docs/research-and-design.md": "8d78678108f0a6c924e1ff9b32c0f81aae9d1f779e0082843b6f99ad993ae2b6",
"docs/safety.md": "7b9b5c515d36e683767ec8d3d9d6d119ac93650b2f629d351deadd4c600ed6a6",
Expand All @@ -193,7 +194,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": "59951495e1695536035673cac5c799a174d43ac1091e455d4cf4a0e29c9f9763",
"labs/diagram-json/plan.lock.json": "a567d42bdc7341fcd8e81c44c786e2be5b794de9f04e6e6f02620e4259e882b8",
"labs/diagram-json/plan.md": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51",
"labs/diagram-json/questions.md": "74733b015002c8a6777c558e7e997fa48c94850b9bd39054fe9366c97ecf728d",
"labs/diagram-json/request.md": "0808fc41c36779c404f4a3a121167da6e76cac56df526e70f9ed6d3e0d4c02ed",
Expand Down Expand Up @@ -292,15 +293,17 @@
"release-notes/2026-07-25-per-worktree-operation-ledger.md": "d0d4495fe4406cf67e7475032e3fc9eb74ed02a3e68f4928c086b5518422b46c",
"release-notes/2026-07-25-published-slice-correction-routing.md": "129cdd62c80c8b93060726027d68ba3abdb0bca1a1ce9e64d6053271af3fd082",
"release-notes/2026-07-25-readme-simplified-technical-english.md": "c362f46702c38dda6b0301d05b95a067da617d170ddfcca22fd7eb9f6e2c1881",
"release-notes/2026-07-25-recovery-tolerates-unrelated-stale-delivery.md": "70bf76d00d19455712d8e38c602b066982511aec89fed9aea2c196345ad783cb",
"release-notes/2026-07-25-release-notes-simplified-technical-english.md": "70b273cbeb5ee46c49c10541540f31e8ca67a71102acfd47ae15451856c651db",
"release-notes/2026-07-25-root-cause-operation.md": "5bf1f082e9123c5a7bcc8bc01b12e97b24b5ae15958577b4ff2a358994fca891",
"release-notes/2026-07-25-runtime-simplified-technical-english.md": "917fbfb51ae56e5c6e0d9c705b84da492ef3b8f8782ea4b62635814259f11bb4",
"release-notes/2026-07-25-update-publish-guard-unblock.md": "adf06ee02b8d3c995525bb9673c2f1fea66a147df8751d65885ced83da0e96e2"
"release-notes/2026-07-25-update-publish-guard-unblock.md": "adf06ee02b8d3c995525bb9673c2f1fea66a147df8751d65885ced83da0e96e2",
"release-notes/2026-07-26-guard-etxtbsy-retry.md": "4238591804be62f8b9a76dd5cda18923af60ef67932d40d70cab0d815124bcaf"
},
"generator": "operatorstack/intelligence-flow:boatstack-distribution",
"schema_version": 1,
"source": {
"commit": "804141bc65d66fdb0a422a9c7c545a71180bffb1",
"commit": "d4c1a8ecc53f1bf82d3974f533b02eb0b9f93221",
"path": "labs/12-product-engineering-loop",
"repository": "operatorstack/intelligence-flow"
}
Expand Down
7 changes: 7 additions & 0 deletions boatstack/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ change. Ask whether to (1) expand the current delivery, (2) split the shared
boundary into a prerequisite delivery, or (3) apply bounded local containment
and record the remaining risk.

State the law over the invariant and its failure class — never scoped to the one
call site where you found the bug. A single shared resource is usually crossed by
several boundaries; enumerate them all and extend the existing law to cover them
rather than minting a near-duplicate for the second one. See
[docs/control-law-scoping.md](docs/control-law-scoping.md) for the method and a
worked example.

### 3. Add boundary-conformance tests

Tests must prove the control law, not merely exercise the implementation. Add
Expand Down
2 changes: 1 addition & 1 deletion boatstack/delivery.go
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ func RecordChangeObservation(options ChangeObservationOptions) (ChangeObservatio
if len(state.Slices) > 0 {
observation.SliceID = state.Slices[len(state.Slices)-1].ID
}
states, statesErr := allManagedDeliveryStates(repo)
states, _, statesErr := allManagedDeliveryStates(repo)
if statesErr != nil {
return ChangeObservation{}, DeliveryState{}, statesErr
}
Expand Down
92 changes: 88 additions & 4 deletions boatstack/delivery_boundary_conformance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (
//
// control-law: stale-delivery-cannot-block-unrelated-feature
// A stale/invalid delivery in the shared store must never escalate into a
// repo-wide INVALID_STATE that blocks resolution of an unrelated new feature.
// The ignored-deliveries filter is applied at the read-only ResolveNext
// boundary BEFORE invalidity becomes fatal; the mutation boundary
// (ActiveManagedDeliveries) stays fail-closed.
// repo-wide block on resolution of an unrelated delivery. This holds at EVERY
// read-only resolution boundary — ResolveNext (new work) and ResolveRecovery
// (recovering an existing delivery) alike: each partitions the store instead
// of failing closed and applies the ignored-deliveries filter BEFORE
// invalidity becomes fatal, blocking only on a still-unignored invalid
// delivery. The mutation boundary (ActiveManagedDeliveries) stays fail-closed.
//
// control-law: discard-preserves-published-authority
// A delivery bearing published authority (any slice with a recorded PRState)
Expand Down Expand Up @@ -142,6 +144,88 @@ func TestResolveNextLeavesInvalidStateUntouched(t *testing.T) {
}
}

// The same law holds at the OTHER read-only resolution boundary: ResolveRecovery.
// ResolveNext resolves new work; ResolveRecovery resolves an existing delivery
// that hit a problem. Both scan the shared store, so both must tolerate an
// unrelated stale delivery. These tests are the recovery-boundary twins of the
// ResolveNext cases above — the defect that motivated generalizing the law was
// that recovery had none of them and fell through to a repo-wide block.

// Positive + bypass conformance: an IGNORED invalid delivery no longer poisons
// recovery of an unrelated healthy delivery on the current branch. The ignore
// filter runs before invalidity can become fatal, so recovery selects and routes
// the real target instead of blocking on abandoned state.
func TestResolveRecoveryIgnoredInvalidDeliveryDoesNotBlockHealthyBranch(t *testing.T) {
repo := nextTestRepo(t)
branch, _ := gitCommand(repo, "branch", "--show-current")

writeNextDelivery(t, repo, "healthy-feature", "BUILD", 0)
updateRecoveryDelivery(t, repo, "healthy-feature", branch, "", "")

writeInvalidDelivery(t, repo, "stale-one")
if _, err := IgnoreDelivery(repo, "stale-one"); err != nil {
t.Fatal(err)
}

status, err := ResolveRecovery(RecoveryStatusOptions{Repo: repo, Message: "the test failed", SourceStage: "ci"})
if err != nil {
t.Fatal(err)
}
if status.VerificationStatus != "VERIFIED" || status.Feature != "healthy-feature" ||
status.Lifecycle != "ACTIVE" || status.NextOperation != "repair_active" {
t.Fatalf("ignored invalid delivery poisoned recovery of an unrelated healthy branch: %#v", status)
}
}

// Negative + relation conformance: a still-unignored invalid delivery does block
// recovery, but the block names exactly the offending delivery and routes to the
// discard-delivery remedy — request -> boundary -> decision.
func TestResolveRecoveryUnignoredInvalidDeliveryBlocksWithDiscardRemedy(t *testing.T) {
repo := nextTestRepo(t)
branch, _ := gitCommand(repo, "branch", "--show-current")
writeNextDelivery(t, repo, "healthy-feature", "BUILD", 0)
updateRecoveryDelivery(t, repo, "healthy-feature", branch, "", "")
writeInvalidDelivery(t, repo, "stale-one")

status, err := ResolveRecovery(RecoveryStatusOptions{Repo: repo, Message: "the test failed", SourceStage: "ci"})
if err != nil {
t.Fatal(err)
}
if status.VerificationStatus != "BLOCKED" || status.NextOperation != "discard-delivery" {
t.Fatalf("unignored invalid delivery did not block with discard remedy: %#v", status)
}
found := false
for _, slug := range status.Blockers {
if slug == "stale-one" {
found = true
}
}
if !found {
t.Fatalf("block did not name the offending delivery: %#v", status.Blockers)
}
}

// Failure-state conformance: ResolveRecovery is read-only. A blocking decision on
// an invalid delivery must leave the offending state file byte-for-byte unchanged.
func TestResolveRecoveryLeavesInvalidStateUntouched(t *testing.T) {
repo := nextTestRepo(t)
statePath := writeInvalidDelivery(t, repo, "stale-one")
before, err := os.ReadFile(statePath)
if err != nil {
t.Fatal(err)
}
if _, err := ResolveRecovery(RecoveryStatusOptions{Repo: repo, Message: "boom", SourceStage: "ci"}); err != nil {
t.Fatal(err)
}
after, err := os.ReadFile(statePath)
if err != nil {
t.Fatal(err)
}
if string(before) != string(after) {
t.Fatalf("read-only recovery mutated the invalid state file\nbefore=%s\nafter=%s", before, after)
}
}

// ---- control-law: discard-preserves-published-authority ----

// Positive + relation conformance: an unpublished delivery is discardable; the
Expand Down
Loading
Loading