Skip to content

Commit e249ab2

Browse files
Sync Boatstack from Intelligence Flow Labs @ c99b5f302c89 (#152)
Co-authored-by: operator-stack-publisher[bot] <operator-stack-publisher[bot]@users.noreply.github.com>
1 parent c03eadd commit e249ab2

14 files changed

Lines changed: 461 additions & 24 deletions

File tree

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/b37bfc311634ab20082ce1d768f0c953530d72cc/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/c99b5f302c89934cc18926e19c7f49eb5e6e01cf/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: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
},
1313
"files": {
1414
".gitignore": "a7079e923a776f14f1bb3a6aa0a11a133a8e1dfb35af020f327623357b7e3957",
15-
"CONTRIBUTING.md": "dde868caa9c38db8ceaa264ee034fdd33eb413e57b570845dfeb5d44c1e9d8d7",
15+
"CONTRIBUTING.md": "d751bf5c5f125b5909a23fad896a4b4700370a9bf5659b15020bfc0a2da8f20d",
1616
"README.md": "3ce3e95e511089b44e946a44b8d5f4f81d019ece5336db65b2cab1f9dc4d4dad",
1717
"assets/boatstack-journey.svg": "e465befc50c8ce30f3e07e8fd97012931beeb053392c8fbf38ad645023b3cc63",
1818
"assets/boatstack-mark.svg": "be1f984da1bfa69fa5d1f986d8343d21f7e20921b71db888c928b4d2e54b09b5",
1919
"assets/boatstack-portability.svg": "66dfdfa85db857b3bd18b32047a6975f1fbbfc4dc091158e8277193f9969a346",
2020
"boatstack/AGENTS.md": "bc76221e1fe90a91afbacd7c6bc9b41a70e6c10fc128c275a6a0b9bc094d9506",
2121
"boatstack/BUG-worktree-delivery-state.md": "02469cf51c3849dad5743783e248e5c04583e4240507fbef0e3f890cd6a95724",
22-
"boatstack/SKILL.md": "4b25970267c3ce508bf2dc8cf6d25e443a6ea6a7b905c170e5dc081ff2fb2be3",
22+
"boatstack/SKILL.md": "634be319004241bf6f6c6674b20350923e7fb7b6f8329ff0c1e31dabfd7df623",
2323
"boatstack/activation.go": "deb7712d20aed37371254596613bfaccfc05fcb59634408b03378d1a0bf693ea",
2424
"boatstack/agents/gemini.yaml": "cbf43b387399e456fa6178f86d83e6e35567e6142ff800f8de6ffca306fa963e",
2525
"boatstack/agents/openai.yaml": "68a30a60859556c5a26e16d184594ca243a6043d99c8cf7d66b5dd6d50a93cd1",
@@ -42,10 +42,11 @@
4242
"boatstack/capture_test.go": "63fa1177738081f1e862364d7a4257f5e259f8e9c36276ba1775b8085b277105",
4343
"boatstack/changelog.go": "6b06be7cd9738de29ba6e87aa2569f3b027a2e618b04524f5abd7abaa17945bf",
4444
"boatstack/changelog_test.go": "ce792f23a7fe1e09fb3096cd1314130a6ab69321d4877b12a8e994027541baf7",
45-
"boatstack/cmd/boatstack-helper/coverage_conformance_test.go": "347810fec8cc65300ad58cf84570040a001f6dffd9d464f21038034bec6f00e9",
45+
"boatstack/cmd/boatstack-helper/coverage_conformance_test.go": "f5a931d2b5cdb0d32af85acbbd499fa0f509ed36d62e772e6b6072e348200aca",
4646
"boatstack/cmd/boatstack-helper/flow.go": "0d41c7a86b49004e897f59551780220841d5941396202c81de97a4d52f593520",
47-
"boatstack/cmd/boatstack-helper/main.go": "9e0712b0a3936a3066a33b9c97f92d8ebc07f6e200664b21e6a3af03a00d9f3b",
47+
"boatstack/cmd/boatstack-helper/main.go": "81f10044f507a6d62646a2f0220595323823ab60f4921c2a0e925b88b0f2301a",
4848
"boatstack/cmd/boatstack-helper/main_test.go": "b36c52d6d5c9dd2428730de10ff18194b7e32a98722e41341c301c6f7a04cad5",
49+
"boatstack/cmd/boatstack-helper/retro.go": "68b83e33ade5b5fec126c70ec798fdcbed22fda755dc1cad2758143fead8e187",
4950
"boatstack/command.go": "4726ac515dedab4947be7eb48f88c6cb8b53d674124504b69f03e6396b080ee8",
5051
"boatstack/command_test.go": "9f707abba3640add81c3e97ba7e72fedbf98f3394b1c060a9ca4b4a28e919968",
5152
"boatstack/compiled_artifact_resolution_test.go": "0748d67643263e698211eb04d46464e1dd3db15d94537f5fd5092b5aa689745b",
@@ -130,8 +131,10 @@
130131
"boatstack/internal/deliverycontrol/trajectorylog_test.go": "227dd6ed9ce181d517a37b67ef4d64dd93779a533eae804798ab54de35c7f13e",
131132
"boatstack/internal/deliverycontrol/transition.go": "b43abb0e99d29697b27b0bb8ee2e2f5f31f3471a2983f25d18ae3564ee246775",
132133
"boatstack/internal/retromine/adapters.go": "7736ebe7fe200dc6aca2799dab964a49031e4f840a049dc872f0bb62053a1ed8",
134+
"boatstack/internal/retromine/classify.go": "060404bfbea3ccc7115dedea5af9c02e53f92c4042a027e68c17042ca996a2cf",
133135
"boatstack/internal/retromine/cluster.go": "ddb6b3506ac192ab37f0341e57437ddf365207c42163da423a06bbfd856758f5",
134136
"boatstack/internal/retromine/event.go": "4d41cbf37209d01bc5cc083d144f835950700aa7f6e39d0661b525b5e045227a",
137+
"boatstack/internal/retromine/report.go": "60a2ecc1416d4b6d71e359aafba7e2a8a80b12dd20d733dddcd90fcd691a9155",
135138
"boatstack/internal/retromine/retromine_conformance_test.go": "33883893db1e455901d9c0e93767c39b475fae728aa9ee9dd925a2c21e5b0ec4",
136139
"boatstack/internal/retromine/testdata/session-alpha.jsonl": "45ed8fa691af9db3f957e87175dbbeffd8fdd2f001324e86a5fd68e29ffbe244",
137140
"boatstack/internal/retromine/testdata/session-beta.txt": "8e85e6a4442e3c892166df97ec879d998b3a47bdad5ef2bf785674b3149c36c2",
@@ -184,6 +187,8 @@
184187
"boatstack/release.go": "82dcb4ca59e8c79a68d5333d650f90e64abd448d04e0c6f504fdf07f42b5ed76",
185188
"boatstack/release_test.go": "5cf2d76fe9b836a91ca68eba53d5585e2c4be5b9421aaf939ea0723063a24690",
186189
"boatstack/repair_state_test.go": "f3779ac47c3db3927175a545728d3b2e020dbc85f41394d8235753b52afc3739",
190+
"boatstack/retro.go": "8a6f13b948574c90d0f06c3b9f5570d08931e66a4c78dbcc208da8c696c2a42b",
191+
"boatstack/retro_conformance_test.go": "827250d2fc49717fb5e58a4cf79e1d5c489c37574d8a2cf9348fa1cd8c328713",
187192
"boatstack/run.go": "3127e8c054b80e41413011e423ba9463fe06a7e6c9e1e71750ab98587871d89d",
188193
"boatstack/run_test.go": "5b291510fa90cefdc26eb89e18a3443385456a6ebc73408325ac1945b7c084d6",
189194
"boatstack/runtime.go": "368bb43a0e3042bde2d4bab1b62df560a93f5928384c7c8f5e27e8a836628af4",
@@ -221,10 +226,10 @@
221226
"docs/benchmark-corpus-audit.md": "f2d206fe8579a514f9da82b2c96c19b343ac004be67617e1bd34f0f8e0e5e6c6",
222227
"docs/benchmark-submission-audit.md": "9518abdd17690729c6423f87cab20418ed47b0915b5faa44b9ef975e9e9c3b79",
223228
"docs/configuration.md": "221f979506a3a9de357e5277f1329c345bf175346ec8dfc8fdd1212fb100dea1",
224-
"docs/evidence-engineered-coding.md": "2a2b905e044d89cad2855f10c45261e980b0507c60e275165c8de8ed2a663f42",
229+
"docs/evidence-engineered-coding.md": "9e343b431d4fa013c094b252fb47eadcf06810f11c730538f1cafb19c0d802b7",
225230
"docs/generated-files.md": "437791765b0a4015032ae21d1a6618563cad92b7402819e4f963bf5ae16284a3",
226231
"docs/getting-started.md": "51c2823f21e35140d31e6d5083dc4b89fddd24721ac6acc474154a4da53ee9f8",
227-
"docs/public-claims.json": "6f78d9f8b0ce76079f23a43d84008045dc7e51e5004a2f3e394784e44a3b752e",
232+
"docs/public-claims.json": "b843e27a0ff7ce283a8ca5cdf7e54244c98dd551e9b35c0c9757845907dc6dd3",
228233
"docs/public-surface.md": "713f7a050b5f339cf948299103ef3800417dccfecf2cc1a4166397ea6f978907",
229234
"docs/research-and-design.md": "8d78678108f0a6c924e1ff9b32c0f81aae9d1f779e0082843b6f99ad993ae2b6",
230235
"docs/safety.md": "7b9b5c515d36e683767ec8d3d9d6d119ac93650b2f629d351deadd4c600ed6a6",
@@ -238,7 +243,7 @@
238243
"labs/diagram-json/compiled/evidence.md": "1ba1c989ade070a8ef9a508fbd788d100d7292f2dbacbb2bce895468019f619d",
239244
"labs/diagram-json/compiled/tasks.json": "88f60851abf79d851e9fccc754ff3040034ae595306bc87d64784c19eb403e71",
240245
"labs/diagram-json/compiled/test-matrix.json": "424657ff505768e50fa113801fd8363364a18269d5297480907a993d44063a39",
241-
"labs/diagram-json/plan.lock.json": "2c2182f842a9ab47814c685ad0762bb63da4859fc27f583a25a32597e97d7b79",
246+
"labs/diagram-json/plan.lock.json": "356bd3d6143508b68469cd651410e773f32614f71f5eaf906aa47f3921428047",
242247
"labs/diagram-json/plan.md": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51",
243248
"labs/diagram-json/questions.md": "74733b015002c8a6777c558e7e997fa48c94850b9bd39054fe9366c97ecf728d",
244249
"labs/diagram-json/request.md": "0808fc41c36779c404f4a3a121167da6e76cac56df526e70f9ed6d3e0d4c02ed",
@@ -377,12 +382,13 @@
377382
"release-notes/2026-07-28-post-publish-prescriptions.md": "a0b728df569bdba40e3b6107179b35873bad00e1ed1c2574e7eecf438b6962f4",
378383
"release-notes/2026-07-28-pr-phase-observation.md": "8c5615013eb88ce9561d30897e47f6fa967e0157c4c245f0c35a1f31e4e132e2",
379384
"release-notes/2026-07-28-protected-native-auto-merge.md": "67dc76a6e7ce51034a0eadc541ba7a8946cfcabe5433cedc25db55321dfb8b62",
385+
"release-notes/2026-07-28-retro-derive-proposals.md": "c90797d76fb00816340475620b584ad150fa32d7d10bc14997a614529ef9b558",
380386
"release-notes/2026-07-28-retromine-recurrence-detector.md": "27790993a02e73f3a2700dce7340d044aeb0e52f785ded68271ff6673add9e25"
381387
},
382388
"generator": "operatorstack/intelligence-flow:boatstack-distribution",
383389
"schema_version": 1,
384390
"source": {
385-
"commit": "b37bfc311634ab20082ce1d768f0c953530d72cc",
391+
"commit": "c99b5f302c89934cc18926e19c7f49eb5e6e01cf",
386392
"path": "labs/12-product-engineering-loop",
387393
"repository": "operatorstack/intelligence-flow"
388394
}

boatstack/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ This is a two-slice ZCA projection: the reviewer brief minimizes review effort,
254254

255255
Read [failure-moves.md](references/failure-moves.md) before proposing a loop change.
256256

257+
For a retro over past sessions, run the read-only `.product-loop/bin/boatstack-helper retro derive --input <transcript> [--input <transcript> ...]`. It detects operator instructions that recur across sessions and classifies each as a missing observation, verb, setpoint, or guard, with a suggested typed promotion. It reads only the transcript files the user names, works fully offline, and writes nothing. A recurring instruction is evidence of a missing typed control — promote it by hand through the normal reviewed delivery flow; never turn it into a saved prompt, and never apply a proposal automatically.
258+
257259
1. Classify the observed failure below the surface symptom.
258260
2. State a mechanism and the exact failure population the move targets.
259261
3. Estimate cost, risk, and possible regressions.

boatstack/cmd/boatstack-helper/coverage_conformance_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ var nonDeliveryVerbs = map[string]bool{
8282
"workspace-sync": true,
8383
// Flow layer itself is read-only navigation over the machine, not a transition.
8484
"flow": true,
85+
// Retro derivation reads operator-supplied transcripts and proposes typed
86+
// promotions; it mutates nothing, so it registers no delivery transition.
87+
// control-law: retro-proposes-never-enforces
88+
"retro": true,
8589
}
8690

8791
// dispatchVerbs parses main.go and returns the set of command verbs the run()

boatstack/cmd/boatstack-helper/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,7 @@ func workspaceSyncCommand(arguments []string) int {
14391439

14401440
func run() int {
14411441
if len(os.Args) < 2 {
1442-
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <attach|detach|detached-status|context|activate|deactivate|init|update|check-update|repair-status|operation-status|prepare-update-pr|publish-update-pr|release-classify|next-patch|export|check-source-plan|planning-write|check-plan|record-approval|activate-plan|delivery-status|next-status|recovery-status|repair-state|mutation-status|undo|run-preflight|record-change|ignore-delivery|record-delivery-gate|record-pr-visual-evidence|capture-evidence|provision-capability|capability-register|record-pr-visual-publication|check-safety|migrate-config|safety-hook|ambient-safety-hook|diagnose-hook|render-denial|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-reap|workspace-status|workspace-sync|flow|doctor|version>")
1442+
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <attach|detach|detached-status|context|activate|deactivate|init|update|check-update|repair-status|operation-status|prepare-update-pr|publish-update-pr|release-classify|next-patch|export|check-source-plan|planning-write|check-plan|record-approval|activate-plan|delivery-status|next-status|recovery-status|repair-state|mutation-status|undo|run-preflight|record-change|ignore-delivery|record-delivery-gate|record-pr-visual-evidence|capture-evidence|provision-capability|capability-register|record-pr-visual-publication|check-safety|migrate-config|safety-hook|ambient-safety-hook|diagnose-hook|render-denial|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-reap|workspace-status|workspace-sync|flow|retro|doctor|version>")
14431443
return 2
14441444
}
14451445
switch os.Args[1] {
@@ -1553,6 +1553,8 @@ func run() int {
15531553
return migrateConfigCommand(os.Args[2:])
15541554
case "flow":
15551555
return flowCommand(os.Args[2:])
1556+
case "retro":
1557+
return retroCommand(os.Args[2:])
15561558
case "version":
15571559
fmt.Printf("Boatstack %s (%s)\n", boatstack.Version, boatstack.SourceCommit)
15581560
return 0
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"os"
7+
8+
boatstack "github.com/operatorstack/boatstack/boatstack"
9+
)
10+
11+
// retroCommand is the derive-only entry point for transcript mining. The CLI
12+
// boundary owns the ONLY I/O in the pipeline: it reads the operator-supplied
13+
// paths and prints the report to stdout. Below this boundary the derivation
14+
// is capability-free (no filesystem, network, subprocess, or clock), and
15+
// nothing anywhere in the pipeline writes, mutates state, or runs a command.
16+
// control-law: retro-proposes-never-enforces
17+
func retroCommand(arguments []string) int {
18+
if len(arguments) == 0 || arguments[0] != "derive" {
19+
fmt.Fprintln(os.Stderr, "usage: boatstack-helper retro derive --input <transcript> [--input <transcript> ...] [--format events|claudecode|plaintext] [--json]")
20+
return 2
21+
}
22+
flags := flag.NewFlagSet("retro derive", flag.ContinueOnError)
23+
var inputs stringList
24+
flags.Var(&inputs, "input", "transcript file to mine (repeatable)")
25+
format := flags.String("format", "", "transcript format: events, claudecode, or plaintext (default: sniff per file)")
26+
jsonOutput := flags.Bool("json", false, "print the structured derivation report")
27+
if err := flags.Parse(arguments[1:]); err != nil {
28+
return 2
29+
}
30+
inputs = append(inputs, flags.Args()...)
31+
if len(inputs) == 0 {
32+
fmt.Fprintln(os.Stderr, "retro derive requires at least one --input transcript; Boatstack never scans for transcripts on its own")
33+
return 2
34+
}
35+
loaded := make([]boatstack.RetroInput, 0, len(inputs))
36+
for _, path := range inputs {
37+
content, err := os.ReadFile(path)
38+
if err != nil {
39+
return fail(err)
40+
}
41+
loaded = append(loaded, boatstack.RetroInput{Name: path, Content: content})
42+
}
43+
report, err := boatstack.RetroDerive(*format, loaded)
44+
if err != nil {
45+
return fail(err)
46+
}
47+
if *jsonOutput {
48+
value, marshalErr := boatstack.MarshalJSON(report)
49+
if marshalErr != nil {
50+
return fail(marshalErr)
51+
}
52+
fmt.Print(string(value))
53+
} else {
54+
fmt.Print(boatstack.FormatRetroReport(report))
55+
}
56+
return 0
57+
}
58+
59+
// stringList is a repeatable string flag.
60+
type stringList []string
61+
62+
func (s *stringList) String() string { return fmt.Sprint([]string(*s)) }
63+
func (s *stringList) Set(value string) error {
64+
*s = append(*s, value)
65+
return nil
66+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package retromine
2+
3+
import "strings"
4+
5+
// Gap classification names WHICH typed construct a recurring instruction is
6+
// compensating for. The four gap types are the four ways a controller can be
7+
// missing a term:
8+
//
9+
// missing_observation — the operator keeps asking what the system could show
10+
// missing_verb — the operator keeps describing an action to take
11+
// missing_setpoint — the operator keeps restating a goal or condition to
12+
// pursue ("until", "every time", "at least")
13+
// missing_guard — the operator keeps warning what must not happen
14+
//
15+
// The classifier is a deterministic keyword lexicon over the normalized
16+
// instruction, with fixed precedence guard > setpoint > observation > verb:
17+
// a guard misclassified as a verb could become an action proposal, so the
18+
// constraining readings win. Anything the lexicon cannot place lands in
19+
// unclassified, which is REPORTED but never generates a proposal.
20+
// control-law: retro-proposes-never-enforces
21+
const (
22+
GapObservation = "missing_observation"
23+
GapVerb = "missing_verb"
24+
GapSetpoint = "missing_setpoint"
25+
GapGuard = "missing_guard"
26+
GapUnclassified = "unclassified"
27+
)
28+
29+
// The lexicons match either whole tokens or normalized phrases. Normalization
30+
// has already lowered the text and stripped punctuation ("don't" → "don t").
31+
var (
32+
guardPhrases = []string{"don t", "do not", "make sure not", "must not", "never", "only if", "unless", "be careful", "avoid", "without asking", "instead of"}
33+
guardTokens = []string{"dont", "stop"}
34+
35+
setpointPhrases = []string{"until", "at least", "at most", "within", "every time", "each time", "whenever", "keep doing", "always", "from now on", "before you finish", "when green", "when it passes"}
36+
37+
observationPhrases = []string{"check the", "check whether", "check if", "what is the", "what s the", "show me", "look at", "status of", "is it", "did it", "how is", "where is", "monitor"}
38+
39+
verbTokens = []string{"run", "merge", "publish", "push", "rerun", "retry", "open", "record", "fix", "update", "deploy", "rebase", "commit", "create", "install", "sync", "clean", "make"}
40+
)
41+
42+
// ClassifyGap places one normalized instruction into a gap type.
43+
func ClassifyGap(normalized string) string {
44+
padded := " " + normalized + " "
45+
containsPhrase := func(phrases []string) bool {
46+
for _, phrase := range phrases {
47+
if strings.Contains(padded, " "+phrase+" ") {
48+
return true
49+
}
50+
}
51+
return false
52+
}
53+
tokens := map[string]bool{}
54+
for _, token := range strings.Fields(normalized) {
55+
tokens[token] = true
56+
}
57+
containsToken := func(list []string) bool {
58+
for _, token := range list {
59+
if tokens[token] {
60+
return true
61+
}
62+
}
63+
return false
64+
}
65+
switch {
66+
case containsPhrase(guardPhrases) || containsToken(guardTokens):
67+
return GapGuard
68+
case containsPhrase(setpointPhrases):
69+
return GapSetpoint
70+
case containsPhrase(observationPhrases):
71+
return GapObservation
72+
case containsToken(verbTokens):
73+
return GapVerb
74+
default:
75+
return GapUnclassified
76+
}
77+
}
78+
79+
// SuggestedShape names the typed construct to add for a gap type — prose
80+
// pointing a human at the right kind of promotion, never a diff.
81+
func SuggestedShape(gapType string) string {
82+
switch gapType {
83+
case GapObservation:
84+
return "Add a typed observation: a read-only status or frontier field that answers this without being asked."
85+
case GapVerb:
86+
return "Add or prescribe a typed verb: a deterministic command the flow names at the right state."
87+
case GapSetpoint:
88+
return "Add a typed setpoint: a persisted goal or condition (like delivery.terminal) the flow pursues so this stops being restated."
89+
case GapGuard:
90+
return "Add a typed guard: an enforced precondition or denial (a gate or policy) instead of a remembered warning."
91+
default:
92+
return ""
93+
}
94+
}

0 commit comments

Comments
 (0)