Skip to content

Commit dd69ee4

Browse files
Sync Boatstack from Intelligence Flow Labs @ 2364eaedaefb (#130)
Co-authored-by: operator-stack-publisher[bot] <operator-stack-publisher[bot]@users.noreply.github.com>
1 parent 15317d5 commit dd69ee4

10 files changed

Lines changed: 252 additions & 23 deletions

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Contributing
44

5-
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/fbb9ecffc548440fadb5d32339115bd20166a29f/labs/12-product-engineering-loop).
5+
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/2364eaedaefbe73d8996108dfac261c38b0ce2b3/labs/12-product-engineering-loop).
66

77
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.
88

UPSTREAM.json

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"files": {
1414
".gitignore": "a7079e923a776f14f1bb3a6aa0a11a133a8e1dfb35af020f327623357b7e3957",
15-
"CONTRIBUTING.md": "e016bb241bf61fdc8e96cac4d1cac7a674c545a2e788abeac663ce4235316c3f",
15+
"CONTRIBUTING.md": "ea4d692766d22427d515ab944c9d692e862d63584c255fdecdb67e063cb32e32",
1616
"README.md": "3ce3e95e511089b44e946a44b8d5f4f81d019ece5336db65b2cab1f9dc4d4dad",
1717
"assets/boatstack-journey.svg": "e465befc50c8ce30f3e07e8fd97012931beeb053392c8fbf38ad645023b3cc63",
1818
"assets/boatstack-mark.svg": "be1f984da1bfa69fa5d1f986d8343d21f7e20921b71db888c928b4d2e54b09b5",
@@ -115,6 +115,8 @@
115115
"boatstack/internal/deliverycontrol/trajectorylog_test.go": "227dd6ed9ce181d517a37b67ef4d64dd93779a533eae804798ab54de35c7f13e",
116116
"boatstack/internal/deliverycontrol/transition.go": "b43abb0e99d29697b27b0bb8ee2e2f5f31f3471a2983f25d18ae3564ee246775",
117117
"boatstack/migrate.go": "eaf589e2b266238068e42c6d78e01dc040266d28e342cb24f09e33e8541749b3",
118+
"boatstack/migrate_effect_grade.go": "bccb58e770001aa9554d8e7f151663d907f152508a61118f56fd90465ba6f32e",
119+
"boatstack/migrate_effect_grade_test.go": "fea1d1057bc6d8eaf015e377864a3adab29ef5731f597fe0a38b96fa80355d14",
118120
"boatstack/migrate_test.go": "9f4bda2fb158c5e54bcc0242dace1da3c1965f9846a213c573956a35b7d1724e",
119121
"boatstack/mutation.go": "59fc9e92105d8ec20f854af9898cde037ab0e3e46c453794838dbfc65fecdd6d",
120122
"boatstack/mutation_test.go": "68d5049c7f96c1ac558e4c781151f67e8deee2f8d6b9bf293b90d44e769ef7c6",
@@ -145,7 +147,7 @@
145147
"boatstack/reexec_unix.go": "ff86157a9aa20c82a56fcd859b70669b7eacf4e0a9f61a4546ef33808437939e",
146148
"boatstack/reexec_windows.go": "f5335c8c28cb4e89048b058b1c4d12f78644f99acb4f6167ff60e622dfb9e742",
147149
"boatstack/references/artifacts.md": "5fa888ac519085d65cee1d04df5902761651bcf2d7af81711fa0f8ecd1fc0f59",
148-
"boatstack/references/config-schema.md": "fa5e09d008101957cf5577e9031de256ab682711c3fa21fa9494cd600ddbd2f0",
150+
"boatstack/references/config-schema.md": "eff8586850eca9941df1cea29ac7edb779277ed9bd6d938a1980db5028d28cf4",
149151
"boatstack/references/failure-moves.md": "b65ef72035afa6ad0dce589a0b38f84bc40cde3864c9ecf973f08fc687f001c3",
150152
"boatstack/references/host-hook-contracts.md": "2a89d44d0e418a53f2e3b6300fed957cdf878f45ea97ce24b55b66065f0eaa1d",
151153
"boatstack/references/irreversible-operation-boundary.md": "e0076f0fea3bf729b2e9bdf353eaeaaf7cdafabfaf26b8d9b27287e5414c2441",
@@ -156,7 +158,7 @@
156158
"boatstack/repair_state_test.go": "f3779ac47c3db3927175a545728d3b2e020dbc85f41394d8235753b52afc3739",
157159
"boatstack/run.go": "3127e8c054b80e41413011e423ba9463fe06a7e6c9e1e71750ab98587871d89d",
158160
"boatstack/run_test.go": "5b291510fa90cefdc26eb89e18a3443385456a6ebc73408325ac1945b7c084d6",
159-
"boatstack/runtime.go": "11f741d45994c336e927121ede2481c94d5c33ed9c3beecf89f760450b304da3",
161+
"boatstack/runtime.go": "7dc5d033ec11bbcf9a4561da66d4d6692a071bc79ac2fe4eb66feaced358d3c3",
160162
"boatstack/runtime_cache.go": "e026ffc1906f7e1e98b768bae63e6658164d2826c07169c9121ce0f23c73faf8",
161163
"boatstack/runtime_cache_test.go": "b981467ddc9f0f562da6bff5de7a80a9fe5a433a0317541d1e48df268546ac85",
162164
"boatstack/runtime_provenance_test.go": "1d52f1e6b0691cf4667729cc9b9f3c55c128f0aa3321f3a2843a9aa6fd0e73dc",
@@ -188,10 +190,10 @@
188190
"docs/benchmark-corpus-audit.md": "f2d206fe8579a514f9da82b2c96c19b343ac004be67617e1bd34f0f8e0e5e6c6",
189191
"docs/benchmark-submission-audit.md": "9518abdd17690729c6423f87cab20418ed47b0915b5faa44b9ef975e9e9c3b79",
190192
"docs/configuration.md": "060775c73431f28bd16066bdf9e0f89034d2855c7ca0f5544f660d24b91211d0",
191-
"docs/evidence-engineered-coding.md": "82b129eeacdcea2ccfca0eeb579c412a0d3938e3413d0f57c7c953f55762f25f",
193+
"docs/evidence-engineered-coding.md": "0083581913336f9612f321997a3e9afb8b7f2549e41d6fbae122920174ae4779",
192194
"docs/generated-files.md": "437791765b0a4015032ae21d1a6618563cad92b7402819e4f963bf5ae16284a3",
193195
"docs/getting-started.md": "51c2823f21e35140d31e6d5083dc4b89fddd24721ac6acc474154a4da53ee9f8",
194-
"docs/public-claims.json": "868026dabc8ae86dec955195d6f78aa45a5d74cb44ece1c873fa29e69ff8c8b1",
196+
"docs/public-claims.json": "aa2ee5ecd6a3f29d5dd3c4e538d29931191606a52fad6433d43184af814334c3",
195197
"docs/public-surface.md": "713f7a050b5f339cf948299103ef3800417dccfecf2cc1a4166397ea6f978907",
196198
"docs/research-and-design.md": "8d78678108f0a6c924e1ff9b32c0f81aae9d1f779e0082843b6f99ad993ae2b6",
197199
"docs/safety.md": "7b9b5c515d36e683767ec8d3d9d6d119ac93650b2f629d351deadd4c600ed6a6",
@@ -205,7 +207,7 @@
205207
"labs/diagram-json/compiled/evidence.md": "1ba1c989ade070a8ef9a508fbd788d100d7292f2dbacbb2bce895468019f619d",
206208
"labs/diagram-json/compiled/tasks.json": "88f60851abf79d851e9fccc754ff3040034ae595306bc87d64784c19eb403e71",
207209
"labs/diagram-json/compiled/test-matrix.json": "424657ff505768e50fa113801fd8363364a18269d5297480907a993d44063a39",
208-
"labs/diagram-json/plan.lock.json": "dc27d78e3c23888025cda5f56ce058e48e009584da08ea111d095b8ce2ccb29b",
210+
"labs/diagram-json/plan.lock.json": "9c2377941197d6bd160f338e0bc55dd74f9118844291b5ee73a17867757a5edb",
209211
"labs/diagram-json/plan.md": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51",
210212
"labs/diagram-json/questions.md": "74733b015002c8a6777c558e7e997fa48c94850b9bd39054fe9366c97ecf728d",
211213
"labs/diagram-json/request.md": "0808fc41c36779c404f4a3a121167da6e76cac56df526e70f9ed6d3e0d4c02ed",
@@ -324,12 +326,13 @@
324326
"release-notes/2026-07-27-discoverable-planning-errors.md": "d8099d1a6cd1805c3fcd446d9fa95739dec93e57ff421ecf4f99562b143c9966",
325327
"release-notes/2026-07-27-guard-dual-reward-corpus.md": "6bec0385c6c553f00517259821e502796ca1b1907aeab718a287560e3e0fa0d6",
326328
"release-notes/2026-07-27-invalid-delivery-block-actionable.md": "8fac8e3921e2285291703efa46e624b72cb5bac1b8492beca4c4b633abb5ba16",
327-
"release-notes/2026-07-27-read-only-inspection-pipelines.md": "0963286371e9a12592915c23a958dd013bf2a35e9fca6921691bc8bb3c3d8dc8"
329+
"release-notes/2026-07-27-read-only-inspection-pipelines.md": "0963286371e9a12592915c23a958dd013bf2a35e9fca6921691bc8bb3c3d8dc8",
330+
"release-notes/2026-07-27-sandboxed-migration-grading.md": "03cebc372bbdfed37cc70d18f3b6374d1aa5e585bafefa073dbcced58bd0336a"
328331
},
329332
"generator": "operatorstack/intelligence-flow:boatstack-distribution",
330333
"schema_version": 1,
331334
"source": {
332-
"commit": "fbb9ecffc548440fadb5d32339115bd20166a29f",
335+
"commit": "2364eaedaefbe73d8996108dfac261c38b0ce2b3",
333336
"path": "labs/12-product-engineering-loop",
334337
"repository": "operatorstack/intelligence-flow"
335338
}

boatstack/migrate_effect_grade.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package boatstack
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"os/exec"
7+
"strings"
8+
)
9+
10+
// MigrationEffectStatus is the verdict of grading a migration by its observed effect.
11+
type MigrationEffectStatus string
12+
13+
const (
14+
// MigrationEffectSkipped means the project declared no migration commands, so no
15+
// effect was executed — a repository without a database is unaffected.
16+
MigrationEffectSkipped MigrationEffectStatus = "SKIPPED"
17+
// MigrationEffectPass means the migration applied and verified against the
18+
// disposable database.
19+
MigrationEffectPass MigrationEffectStatus = "PASS"
20+
// MigrationEffectFail means applying or verifying the migration failed — real
21+
// breakage the static guard cannot see, because a migration is inert as data.
22+
MigrationEffectFail MigrationEffectStatus = "FAIL"
23+
)
24+
25+
// MigrationEffectResult is the outcome of GradeMigrationEffect.
26+
type MigrationEffectResult struct {
27+
Status MigrationEffectStatus
28+
Reason string
29+
}
30+
31+
// GradeMigrationEffect grades a project's migrations by their OBSERVED EFFECT rather
32+
// than by their SQL text. Sandboxed-Effect law: when an effect's safety cannot be
33+
// certified statically, execute it in a disposable environment and read the oracle;
34+
// never approximate it by reading the source. The guard keeps treating a committed
35+
// migration as a data artifact (it is applied later by the controlled pipeline); this
36+
// harness IS that controlled executor for grading purposes.
37+
//
38+
// It runs the project's configured apply_command, then verify_command, via `sh -c`
39+
// with the caller-provided environment (which carries the disposable database
40+
// coordinate, BOATSTACK_MIGRATE_DB). PASS iff both succeed; FAIL if either fails;
41+
// SKIPPED when no apply_command is configured. The caller owns the disposable
42+
// database and its guaranteed teardown (a fresh-per-run service container in CI, or a
43+
// temp file removed by the test) — this function only executes and grades.
44+
func GradeMigrationEffect(repo string, extraEnv []string) (MigrationEffectResult, error) {
45+
config, _, err := LoadConfig(WorkspaceFor(repo).ProjectConfigPath())
46+
if err != nil {
47+
return MigrationEffectResult{}, err
48+
}
49+
apply := strings.TrimSpace(config.Project.Migration.ApplyCommand)
50+
verify := strings.TrimSpace(config.Project.Migration.VerifyCommand)
51+
if apply == "" {
52+
return MigrationEffectResult{Status: MigrationEffectSkipped, Reason: "no migration apply_command is configured; effect grading skipped"}, nil
53+
}
54+
if out, runErr := runMigrationShell(repo, apply, extraEnv); runErr != nil {
55+
return MigrationEffectResult{Status: MigrationEffectFail, Reason: "apply failed: " + firstOutputLine(out)}, nil
56+
}
57+
if verify != "" {
58+
if out, runErr := runMigrationShell(repo, verify, extraEnv); runErr != nil {
59+
return MigrationEffectResult{Status: MigrationEffectFail, Reason: "verify failed: " + firstOutputLine(out)}, nil
60+
}
61+
}
62+
return MigrationEffectResult{Status: MigrationEffectPass, Reason: "migration applied and verified against the disposable database"}, nil
63+
}
64+
65+
// runMigrationShell keeps stdout (authority-bearing) and stderr (diagnostic)
66+
// separate. Grading needs only the exit status and a diagnostic line, so it reports
67+
// stderr, falling back to stdout when a tool writes its error there.
68+
func runMigrationShell(dir, command string, extraEnv []string) (string, error) {
69+
cmd := exec.Command("sh", "-c", command)
70+
cmd.Dir = dir
71+
cmd.Env = append(os.Environ(), extraEnv...)
72+
var stdout, stderr bytes.Buffer
73+
cmd.Stdout = &stdout
74+
cmd.Stderr = &stderr
75+
err := cmd.Run()
76+
diagnostic := strings.TrimSpace(stderr.String())
77+
if diagnostic == "" {
78+
diagnostic = strings.TrimSpace(stdout.String())
79+
}
80+
return diagnostic, err
81+
}
82+
83+
func firstOutputLine(s string) string {
84+
s = strings.TrimSpace(s)
85+
if index := strings.IndexByte(s, '\n'); index >= 0 {
86+
s = s[:index]
87+
}
88+
if s == "" {
89+
return "(no output)"
90+
}
91+
return s
92+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package boatstack
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"path/filepath"
7+
"testing"
8+
)
9+
10+
// migrateGradeRepo builds a repo whose project config declares the given migration
11+
// apply/verify commands. A disposable SQLite database is created under the test's
12+
// temp dir (removed automatically when the test ends — the guaranteed-teardown
13+
// invariant), seeded with one row, and returned as the BOATSTACK_MIGRATE_DB env the
14+
// commands read.
15+
func migrateGradeRepo(t *testing.T, apply, verify string) (repo string, env []string) {
16+
t.Helper()
17+
repo = t.TempDir()
18+
if err := os.MkdirAll(filepath.Join(repo, ".product-loop", "features"), 0o755); err != nil {
19+
t.Fatal(err)
20+
}
21+
config := testConfig()
22+
config.Project.Migration = MigrationConfig{ApplyCommand: apply, VerifyCommand: verify}
23+
value, err := MarshalJSON(config)
24+
if err != nil {
25+
t.Fatal(err)
26+
}
27+
if err := os.WriteFile(filepath.Join(repo, ".product-loop", "project.json"), value, 0o644); err != nil {
28+
t.Fatal(err)
29+
}
30+
db := filepath.Join(t.TempDir(), "disposable.sqlite")
31+
if out, seedErr := exec.Command("sqlite3", db, "CREATE TABLE accounts(id INTEGER); INSERT INTO accounts VALUES (1);").CombinedOutput(); seedErr != nil {
32+
t.Fatalf("seed disposable db: %v: %s", seedErr, out)
33+
}
34+
return repo, []string{"BOATSTACK_MIGRATE_DB=" + db}
35+
}
36+
37+
// Sandboxed-Effect law: a migration's safety is graded by EXECUTING it against a
38+
// fresh, disposable database and reading the oracle — never approximated from its
39+
// SQL text. The guard treats the same migration as inert data; this harness is the
40+
// controlled executor. A repo that declares no migration commands is unaffected.
41+
func TestMigrationEffectGradingSandbox(t *testing.T) {
42+
if _, err := exec.LookPath("sqlite3"); err != nil {
43+
t.Skip("sqlite3 not available")
44+
}
45+
if _, err := exec.LookPath("sh"); err != nil {
46+
t.Skip("sh not available")
47+
}
48+
49+
const apply = `sqlite3 "$BOATSTACK_MIGRATE_DB" < migrate.sql`
50+
// Verify the invariant the migration must preserve: the seeded row still exists.
51+
const verify = `test "$(sqlite3 "$BOATSTACK_MIGRATE_DB" 'SELECT count(*) FROM accounts')" = "1"`
52+
53+
t.Run("skips cleanly when no migration commands are declared", func(t *testing.T) {
54+
repo, env := migrateGradeRepo(t, "", "")
55+
result, err := GradeMigrationEffect(repo, env)
56+
if err != nil {
57+
t.Fatal(err)
58+
}
59+
if result.Status != MigrationEffectSkipped {
60+
t.Fatalf("unconfigured repo did not skip: %+v", result)
61+
}
62+
})
63+
64+
t.Run("safe forward migration grades PASS", func(t *testing.T) {
65+
repo, env := migrateGradeRepo(t, apply, verify)
66+
// A declarative migration full of DDL — the guard treats this as data.
67+
if err := os.WriteFile(filepath.Join(repo, "migrate.sql"),
68+
[]byte("ALTER TABLE accounts ADD COLUMN active INTEGER DEFAULT 1;\n"), 0o644); err != nil {
69+
t.Fatal(err)
70+
}
71+
result, err := GradeMigrationEffect(repo, env)
72+
if err != nil {
73+
t.Fatal(err)
74+
}
75+
if result.Status != MigrationEffectPass {
76+
t.Fatalf("safe migration did not grade PASS: %+v", result)
77+
}
78+
})
79+
80+
t.Run("destructive migration grades FAIL against the disposable db", func(t *testing.T) {
81+
repo, env := migrateGradeRepo(t, apply, verify)
82+
// Dropping the populated table is inert as TEXT (the static guard allows it as
83+
// a data artifact) but its EFFECT is caught by executing it in the sandbox.
84+
if err := os.WriteFile(filepath.Join(repo, "migrate.sql"),
85+
[]byte("DROP TABLE accounts;\n"), 0o644); err != nil {
86+
t.Fatal(err)
87+
}
88+
result, err := GradeMigrationEffect(repo, env)
89+
if err != nil {
90+
t.Fatal(err)
91+
}
92+
if result.Status != MigrationEffectFail {
93+
t.Fatalf("destructive migration was not caught by effect grading: %+v", result)
94+
}
95+
})
96+
}

boatstack/references/config-schema.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ boatstack-config-field:project.default_branch
88
boatstack-config-field:project.context
99
boatstack-config-field:project.commands
1010
boatstack-config-field:project.high_risk_paths
11+
boatstack-config-field:project.migration
12+
boatstack-config-field:project.migration.apply_command
13+
boatstack-config-field:project.migration.verify_command
1114
boatstack-config-field:workflow
1215
boatstack-config-field:workflow.human_plan_approval
1316
boatstack-config-field:workflow.independent_review_for_high_risk
@@ -62,6 +65,9 @@ This is the exhaustive serialization contract, not a list of recommended user ed
6265
- `test` (string, required): The exact command to execute project-local tests.
6366
- Other command names (string, optional): Additional repository-owned commands such as `build`, `lint`, or `typecheck`.
6467
- `high_risk_paths` (array of strings, optional): Glob patterns of files requiring independent reviewer sign-off before shipping.
68+
- `migration` (object, optional): Declares how migrations are graded by EFFECT against a disposable database, so a committed migration stays a data artifact for the guard while its real effect is executed and observed by a conformance harness. Both commands run via `sh -c` with the disposable database coordinate in the environment as `BOATSTACK_MIGRATE_DB`; when `apply_command` is absent, grading is skipped.
69+
- `apply_command` (string, optional): The command that applies the migration set to the disposable database.
70+
- `verify_command` (string, optional): The command that asserts the post-migration invariant; a non-zero exit grades the migration FAIL.
6571

6672
### workflow Fields
6773

boatstack/runtime.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,21 @@ type Project struct {
4444
Context []string `json:"context,omitempty"`
4545
Commands map[string]string `json:"commands"`
4646
HighRiskPaths []string `json:"high_risk_paths,omitempty"`
47+
Migration MigrationConfig `json:"migration,omitempty"`
48+
}
49+
50+
// MigrationConfig declares how a project APPLIES and VERIFIES its migrations against
51+
// a disposable database, so their EFFECT can be graded by executing them
52+
// (GradeMigrationEffect) rather than approximated from their SQL text. This is the
53+
// Sandboxed-Effect law: the guard treats a committed migration as data (the
54+
// data-artifact exemption), and the deploy pipeline — modelled here by a disposable
55+
// database and these commands — is the controlled executor that observes the real
56+
// effect. Both commands run via `sh -c` with the disposable database coordinate in
57+
// the environment as BOATSTACK_MIGRATE_DB. When apply_command is absent, grading is
58+
// skipped, so a repository without a database is unaffected.
59+
type MigrationConfig struct {
60+
ApplyCommand string `json:"apply_command,omitempty"`
61+
VerifyCommand string `json:"verify_command,omitempty"`
4762
}
4863

4964
type Workflow struct {

docs/evidence-engineered-coding.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,6 @@ Delivery and system improvement also remain separate. A failed task may suggest
146146

147147
## What is evidence-backed
148148

149-
The current moves were derived from the Intelligence Flow benchmark corpus and product-repository studies. The generated source commit is [`fbb9ecffc548440fadb5d32339115bd20166a29f`](https://github.com/operatorstack/intelligence-flow/tree/fbb9ecffc548440fadb5d32339115bd20166a29f/labs/12-product-engineering-loop).
149+
The current moves were derived from the Intelligence Flow benchmark corpus and product-repository studies. The generated source commit is [`2364eaedaefbe73d8996108dfac261c38b0ce2b3`](https://github.com/operatorstack/intelligence-flow/tree/2364eaedaefbe73d8996108dfac261c38b0ce2b3/labs/12-product-engineering-loop).
150150

151151
The evidence supports specific failure mechanisms and guardrails. It does not establish that Boatstack is optimal, that control-theory notation proves software quality, or that one workflow dominates every team. Those are evaluation questions, so the distribution preserves measurements, provenance, gaps, and negative results.

0 commit comments

Comments
 (0)