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/d589911ccb9138013c8693d85b96be0e7c903be3/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/91e33a95add752894fb532a67b980c109ecd28e8/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
41 changes: 21 additions & 20 deletions UPSTREAM.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"canonical_context": {
"characters": 60215,
"estimated_tokens": 15054,
"characters": 60587,
"estimated_tokens": 15147,
"estimator": "ceil(total characters / 4); compactness signal, not provider billing",
"files": [
"product-engineering-loop/references/workflow.md",
Expand All @@ -12,12 +12,12 @@
},
"files": {
".gitignore": "a7079e923a776f14f1bb3a6aa0a11a133a8e1dfb35af020f327623357b7e3957",
"CONTRIBUTING.md": "35fa5831dec2a7df663652bab27a2bf31b16f04f4ea9263cd780ce4bb9df8166",
"CONTRIBUTING.md": "f9ed388745c12187dcc3e39fc4821ec5094085182157005cae561df38a3ab2c8",
"README.md": "125b47671a68556df382f19756fb61fa18925606cbbaf54d6bc9df8872b36870",
"assets/boatstack-journey.svg": "e465befc50c8ce30f3e07e8fd97012931beeb053392c8fbf38ad645023b3cc63",
"assets/boatstack-mark.svg": "be1f984da1bfa69fa5d1f986d8343d21f7e20921b71db888c928b4d2e54b09b5",
"assets/boatstack-portability.svg": "66dfdfa85db857b3bd18b32047a6975f1fbbfc4dc091158e8277193f9969a346",
"boatstack/SKILL.md": "80c4599f2ffeb686d45fdfc1bb4eac4635bded5a5621fa71c41eed7160f4d125",
"boatstack/SKILL.md": "1fbdab536732aca6425d657acfe0eb2f37743cb542c07e92f8e06ddafdb910ff",
"boatstack/agents/gemini.yaml": "cbf43b387399e456fa6178f86d83e6e35567e6142ff800f8de6ffca306fa963e",
"boatstack/agents/openai.yaml": "68a30a60859556c5a26e16d184594ca243a6043d99c8cf7d66b5dd6d50a93cd1",
"boatstack/assets/templates/adr.md": "c577a3c1c1319061f61deb053597e6e853657022185fe28b8f733327e2a78565",
Expand All @@ -34,15 +34,15 @@
"boatstack/atomic_windows.go": "cefd775cbe7e7c3bd8a3f5673b11cdd784c6d3ebd6de7dcb8f39406b0bee511f",
"boatstack/changelog.go": "c5e1f31440b44d61e6037ad27af0333540af3545d655e35819a0241cbbebd8ec",
"boatstack/changelog_test.go": "ce792f23a7fe1e09fb3096cd1314130a6ab69321d4877b12a8e994027541baf7",
"boatstack/cmd/boatstack-helper/main.go": "3b75e71f201825d5c9b83309ec551f79ca27e6de4a58175841e404e74c106931",
"boatstack/cmd/boatstack-helper/main.go": "718803eb4e0c1ca1bb0ca2923f64cf055d35dab6e3ad7f6a9f62b0cec724b610",
"boatstack/cmd/boatstack-helper/main_test.go": "ff73003b6a5157202fa09ddf1129fb13c3d79702b2e05a8721ce5a11bf5ab779",
"boatstack/command.go": "94d2117c6e390d5a644afc5cd90f7e712e3f8b1032f8c9e3b253cc134524c28a",
"boatstack/command_test.go": "9f707abba3640add81c3e97ba7e72fedbf98f3394b1c060a9ca4b4a28e919968",
"boatstack/config_documentation_test.go": "bd34ac570d08c8d1d1e4f5e86c327c02a55dd5e96bd785b895a05f28fcd3f7a2",
"boatstack/config_documentation_test.go": "1fa56768409824afaff61bbe4ae2733ca854a4cbe87254a7954c6aaa2a6d75ac",
"boatstack/decision.go": "257ca328da6ae19ab252f10ee5d06bd7daf49dd8141d083ab1b32f106ea7a94c",
"boatstack/decision_test.go": "1a92ff832610f9559bd47ccac7fc1755a8b4f8261c35bc72a092830dff05f7c0",
"boatstack/delivery.go": "96b55c27d320c0a218b1db4ea9c61a8687dfcff8c6f9d3f1126f5b7ae7ed35bd",
"boatstack/delivery_test.go": "5ce806257307af5d2e4c74c07c77c3db119398f60953890d9b72f95cb168cae2",
"boatstack/delivery.go": "ea53af0e702ec3668a563a5f786dcac2e095362285ca6093b7ed71ec495a0a48",
"boatstack/delivery_test.go": "564ad2029a8412de7953967b1acdf377e6c87b6f6e3465d1f8741a4813f2949f",
"boatstack/evidence.go": "497a31e6ff632cb1d7c3adfc9f269af3f6aa84e948dd5d417c162767542a27df",
"boatstack/export.go": "1cb10bd4efc0906c22220a139da1b10ff7729881501b17ac369dd9dfedac97eb",
"boatstack/export_test.go": "50c372ad5713107a8d5d34edb8d65bb56b21b97e3c63ceddf98e099bd9290d71",
Expand All @@ -58,8 +58,8 @@
"boatstack/integrations.go": "75b39ce2e662fccd66bf4b9bff0e097a4db558f23b3aa1d9bc83a5fc6373444c",
"boatstack/migrate.go": "eaf589e2b266238068e42c6d78e01dc040266d28e342cb24f09e33e8541749b3",
"boatstack/migrate_test.go": "9f4bda2fb158c5e54bcc0242dace1da3c1965f9846a213c573956a35b7d1724e",
"boatstack/next.go": "6bf119965704e8979921aaf514b205f85082716f34a5d1901ed63c175103227b",
"boatstack/next_test.go": "641bf32cf160c426531134a4756b0c41c6a6d876d515bb151cd01fd9c1ae5056",
"boatstack/next.go": "b7852451069764a486fb01b297b83c7882622e62505abf622559a8edcd49490c",
"boatstack/next_test.go": "6d5acd01311a1c357580040d5a14804cc3bb12706f5c997b66f5e1198271a9d6",
"boatstack/operation.go": "62f97bf2091f33eb2ca91915bf08bee73d53387611b673e849355bfd516ca467",
"boatstack/operation_test.go": "2d624eaba342b2c81b45cdf50918a65a9c002b5376a02041b24180658ee6a25a",
"boatstack/plan.go": "8189ee42902bce62dcd39ce7a2e423cecb4c9e0d1dd34a5497e5a732a45c851f",
Expand All @@ -73,17 +73,17 @@
"boatstack/recovery.go": "dd816b18b54a0085b8d8276a93ee98d2b1e90099059a0d85cf6e24edf6f37d5b",
"boatstack/recovery_test.go": "29490e7477ba602491330036a491289dd9117b99ff862f66dae421ba17e04c9f",
"boatstack/references/artifacts.md": "5fa888ac519085d65cee1d04df5902761651bcf2d7af81711fa0f8ecd1fc0f59",
"boatstack/references/config-schema.md": "c07c2d532ef95ea6ae538a1fffefb92ded1f8dc6a06eb3b8d463e371d1ed8416",
"boatstack/references/config-schema.md": "4df91e9769125bf21654595a749483b8db7d2a27399db346e9e194d525a328f7",
"boatstack/references/failure-moves.md": "34a39aefb282b1b5d9ea387f8536bdc5e0145a240f102ae3d01c7ada6d4abebc",
"boatstack/references/host-hook-contracts.md": "d68ae1556e7b1e29e9ac7cb4db767809d510aabf0be52e60e44665ea7abb980e",
"boatstack/references/irreversible-operation-boundary.md": "e0076f0fea3bf729b2e9bdf353eaeaaf7cdafabfaf26b8d9b27287e5414c2441",
"boatstack/references/portability.md": "fb683095991bb0cb06ec56fb8884c49038b283172a7d2f8b203483b7cacb4bae",
"boatstack/references/workflow.md": "4d8331ff2ffc4cbcaba27818342fa11daa3b11fb7db6cbdce1b099a5175f6f52",
"boatstack/references/workflow.md": "5d9959ea1fdabec568472d08c14d47657ece6cdf108c231f1397baba200861b1",
"boatstack/release.go": "82dcb4ca59e8c79a68d5333d650f90e64abd448d04e0c6f504fdf07f42b5ed76",
"boatstack/release_test.go": "5cf2d76fe9b836a91ca68eba53d5585e2c4be5b9421aaf939ea0723063a24690",
"boatstack/run.go": "fbdbf583c862c41f23d1a200f53d042842db72f19c29fe94e4288a69b0ac4a6b",
"boatstack/run_test.go": "34484285fd2457b84faf6d5353117b74af73d4601cd60a96e270b7fd0a7a6a8e",
"boatstack/runtime.go": "687b3543c22acbbed91c94e798c2a813bd828e087ac180ab29a21e3a429f9971",
"boatstack/run.go": "74967ad5b3ed3847baffec1231aae69a81a70f9cce3a9412fa05bcfdc4eca6d1",
"boatstack/run_test.go": "5b291510fa90cefdc26eb89e18a3443385456a6ebc73408325ac1945b7c084d6",
"boatstack/runtime.go": "007f38f0631200b448f27f79b8cefb9874dd767b500dc389f2c9a663d0d0f9b0",
"boatstack/runtime_cache.go": "60c4eb0c7dde91d40d6ef3f05adc1a1282d17ff1ca12470d0a008454f7ca7489",
"boatstack/runtime_cache_test.go": "b981467ddc9f0f562da6bff5de7a80a9fe5a433a0317541d1e48df268546ac85",
"boatstack/safety.go": "994baf314fe1fda41a70cf7b0696a8ce8c7bfc666c39fe773ec7c21c1c8b8afe",
Expand All @@ -106,11 +106,11 @@
"docs/account-recovery-walkthrough.md": "676034974594a7d1a559b24dbed31d7ccc429eb81404b203ca07bbdaa19ec3d3",
"docs/benchmark-corpus-audit.md": "f2d206fe8579a514f9da82b2c96c19b343ac004be67617e1bd34f0f8e0e5e6c6",
"docs/benchmark-submission-audit.md": "9518abdd17690729c6423f87cab20418ed47b0915b5faa44b9ef975e9e9c3b79",
"docs/configuration.md": "f530c5dcbacf32dcb6fdab590901d8f658a4f29bd93f6264cec5d4f449c2cbd1",
"docs/evidence-engineered-coding.md": "b888b8231789d7c24b12902a62e4a8c63527cbf88662acc3afac9d33fe0d89b1",
"docs/configuration.md": "4d8f207b415a5a1e3b9b1698ee7bb1221aa0e5496a061bb8054294df2f347ad1",
"docs/evidence-engineered-coding.md": "2f9d42da150a4552e59af30aaa83b3ff1dd7aadc5e2ba4135296aae2a4fab202",
"docs/generated-files.md": "437791765b0a4015032ae21d1a6618563cad92b7402819e4f963bf5ae16284a3",
"docs/getting-started.md": "d5f0b170209e61518810a23b851bf9eb50b6755906703ca313e6e9faab20e1cd",
"docs/public-claims.json": "4990ea85732e1bdeb24cd5b8a1bb8ec875c758c069645ec43e4c5023918e9e7f",
"docs/public-claims.json": "35181ffc37a69bec15347a024a6e49a0eacb03efcfc1c06dd17a477bfb90dca9",
"docs/public-surface.md": "713f7a050b5f339cf948299103ef3800417dccfecf2cc1a4166397ea6f978907",
"docs/research-and-design.md": "d65c66e323037bda5d45aacef5d48afa6bf93da55901378891d235aca3a5684f",
"docs/safety.md": "7b9b5c515d36e683767ec8d3d9d6d119ac93650b2f629d351deadd4c600ed6a6",
Expand All @@ -124,7 +124,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": "303844a2016d64a2c636708c83e62e33f03bd286eb6f5e3276e52d15a5624c62",
"labs/diagram-json/plan.lock.json": "9828551b5812c00108df19faf9b6faabc2e9efea6c2af10b83f1139e28637f9e",
"labs/diagram-json/plan.md": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51",
"labs/diagram-json/questions.md": "74733b015002c8a6777c558e7e997fa48c94850b9bd39054fe9366c97ecf728d",
"labs/diagram-json/request.md": "0808fc41c36779c404f4a3a121167da6e76cac56df526e70f9ed6d3e0d4c02ed",
Expand Down Expand Up @@ -184,12 +184,13 @@
"release-notes/2026-07-22-value-translation-boundary.md": "9cf168ff7caaf3906b78533935bdfb2c86e753984ed1e5ec8204cb373d083390",
"release-notes/2026-07-23-bootstrap-safe-update-repair.md": "d8e66c46ae05e3d228dfea45879f4d1166d5e3a253ac24bababd4c3e396c214b",
"release-notes/2026-07-23-canonical-update-ownership.md": "7f34f890b252493797519389b23f1b56ec7ec16db7547aac8ea196f75f3b8c2c",
"release-notes/2026-07-23-ignore-ambiguous-deliveries.md": "9b1b9fd48db340b91fcced1c282297de0ecad8744723f2b1fdd94033927a88c4",
"release-notes/2026-07-23-recoverable-repository-sync.md": "3afc4f6220ae76df3bd6dcd15fc180135274c808729e9c512a2c53462ec690c2"
},
"generator": "operatorstack/intelligence-flow:boatstack-distribution",
"schema_version": 1,
"source": {
"commit": "d589911ccb9138013c8693d85b96be0e7c903be3",
"commit": "91e33a95add752894fb532a67b980c109ecd28e8",
"path": "labs/12-product-engineering-loop",
"repository": "operatorstack/intelligence-flow"
}
Expand Down
2 changes: 1 addition & 1 deletion boatstack/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ For the full state machine, read [workflow.md](references/workflow.md). For arti

## Report what is next

Run the project-local helper's read-only `next-status --repo . --json` inspection. Repository artifacts, managed delivery state, gate receipts, and the recorded PR identity are evidence; conversation, terminal, worktree, and process observations are context only. Never run the returned operation automatically. `NOT_STARTED` and `SOURCE_PLAN_READY` point to `auto-plan`; `PUBLISHED` means a PR exists but is not a verified merge; only `FEATURE_COMPLETE` requires no action. If state is ambiguous, stale, or invalid, name the blocker instead of choosing by recency or clearing artifacts.
Run the project-local helper's read-only `next-status --repo . --json` inspection. Repository artifacts, managed delivery state, gate receipts, and the recorded PR identity are evidence; conversation, terminal, worktree, and process observations are context only. Never run the returned operation automatically. `NOT_STARTED` and `SOURCE_PLAN_READY` point to `auto-plan`; `PUBLISHED` means a PR exists but is not a verified merge; only `FEATURE_COMPLETE` requires no action. If state is ambiguous, stale, or invalid, name the blocker instead of choosing by recency or clearing artifacts. When an `AMBIGUOUS` block names only past deliveries the user no longer cares about, name the ignorable delivery slug(s) and offer to exclude them from ambiguity resolution; only after explicit user confirmation, add each slug with `.product-loop/bin/boatstack-helper ignore-delivery --repo . --feature <slug>` (a bounded, provenance-safe write to `workflow.ignored_deliveries` — never hand-edit config or delivery state). Any new, unlisted ambiguous delivery still pauses the workflow.

## Run through ship

Expand Down
26 changes: 25 additions & 1 deletion boatstack/cmd/boatstack-helper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,28 @@ func recordChangeCommand(arguments []string) int {
return 0
}

func ignoreDeliveryCommand(arguments []string) int {
flags := flag.NewFlagSet("ignore-delivery", flag.ContinueOnError)
repo := flags.String("repo", ".", "repository containing the Boatstack installation")
feature := flags.String("feature", "", "feature slug of the past delivery to ignore")
if err := flags.Parse(arguments); err != nil {
return 2
}
if *feature == "" {
return fail(fmt.Errorf("ignore-delivery requires --feature"))
}
added, err := boatstack.IgnoreDelivery(*repo, *feature)
if err != nil {
return fail(err)
}
if added {
fmt.Printf("PASS: delivery %s added to workflow.ignored_deliveries\n", *feature)
} else {
fmt.Printf("PASS: delivery %s already ignored\n", *feature)
}
return 0
}

func doctorCommand(arguments []string) int {
flags := flag.NewFlagSet("doctor", flag.ContinueOnError)
repo := flags.String("repo", ".", "repository whose Boatstack installation should be checked")
Expand Down Expand Up @@ -955,7 +977,7 @@ func workspaceSyncCommand(arguments []string) int {

func run() int {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <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|run-preflight|record-change|record-delivery-gate|record-pr-visual-evidence|record-pr-visual-publication|check-safety|migrate-config|safety-hook|diagnose-hook|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-status|workspace-sync|doctor|version>")
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <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|run-preflight|record-change|ignore-delivery|record-delivery-gate|record-pr-visual-evidence|record-pr-visual-publication|check-safety|migrate-config|safety-hook|diagnose-hook|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-status|workspace-sync|doctor|version>")
return 2
}
switch os.Args[1] {
Expand Down Expand Up @@ -999,6 +1021,8 @@ func run() int {
return runPreflightCommand(os.Args[2:])
case "record-change":
return recordChangeCommand(os.Args[2:])
case "ignore-delivery":
return ignoreDeliveryCommand(os.Args[2:])
case "record-delivery-gate":
return recordDeliveryGateCommand(os.Args[2:])
case "record-pr-visual-evidence":
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 @@ -120,6 +120,7 @@ func TestPublicConfigurationGuideContainsOnlySupportedUserControls(t *testing.T)
"workflow.allow_pass_with_gaps",
"workflow.boundary_analysis",
"workflow.human_plan_approval",
"workflow.ignored_deliveries",
"workflow.independent_review_for_high_risk",
"workflow.maintain_changelog",
"workflow.pr_visual_evidence",
Expand Down
79 changes: 79 additions & 0 deletions boatstack/delivery.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,3 +879,82 @@ func ActiveManagedDeliveries(repo string) ([]string, error) {
sort.Strings(active)
return active, nil
}

// withoutIgnoredDeliveries returns the feature slugs in features that are not
// listed in ignored. Order is preserved. It is used to scope delivery-ambiguity
// resolution to the deliveries the user still cares about; new, unlisted
// deliveries are unaffected.
func withoutIgnoredDeliveries(features []string, ignored []string) []string {
if len(ignored) == 0 {
return features
}
skip := make(map[string]bool, len(ignored))
for _, slug := range ignored {
skip[slug] = true
}
kept := make([]string, 0, len(features))
for _, f := range features {
if !skip[f] {
kept = append(kept, f)
}
}
return kept
}

// withoutIgnoredDeliveryStates is the DeliveryState-slice variant of
// withoutIgnoredDeliveries, filtering by each state's Feature slug.
func withoutIgnoredDeliveryStates(states []DeliveryState, ignored []string) []DeliveryState {
if len(ignored) == 0 {
return states
}
skip := make(map[string]bool, len(ignored))
for _, slug := range ignored {
skip[slug] = true
}
kept := make([]DeliveryState, 0, len(states))
for _, state := range states {
if !skip[state.Feature] {
kept = append(kept, state)
}
}
return kept
}

// IgnoreDelivery appends a feature slug to workflow.ignored_deliveries in the
// repository's project.json, deduplicating and preserving all other config. It
// is the bounded, provenance-safe write behind the ignore-delivery helper
// subcommand: the config round-trips through LoadConfig -> GeneratedJSON so the
// serialization contract and generator metadata are preserved. It returns
// whether the slug was newly added.
func IgnoreDelivery(repo, feature string) (bool, error) {
feature = strings.TrimSpace(feature)
if feature == "" {
return false, fmt.Errorf("ignore-delivery requires a feature slug")
}
if !featureSlugPattern.MatchString(feature) {
return false, fmt.Errorf("feature slug %q is not a valid Boatstack feature slug", feature)
}
resolved, err := ResolveRepository(repo)
if err != nil {
return false, err
}
configPath := filepath.Join(resolved, ".product-loop", "project.json")
config, _, err := LoadConfig(configPath)
if err != nil {
return false, err
}
for _, existing := range config.Workflow.IgnoredDeliveries {
if existing == feature {
return false, nil
}
}
config.Workflow.IgnoredDeliveries = append(config.Workflow.IgnoredDeliveries, feature)
value, err := GeneratedJSON(config)
if err != nil {
return false, err
}
if err := atomicWriteMode(configPath, value, 0o644); err != nil {
return false, err
}
return true, nil
}
40 changes: 40 additions & 0 deletions boatstack/delivery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,50 @@ package boatstack
import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)

func TestIgnoreDeliveryAppendsDedupsAndPreservesConfig(t *testing.T) {
repo := nextTestRepo(t)

added, err := IgnoreDelivery(repo, "old-feature")
if err != nil {
t.Fatal(err)
}
if !added {
t.Fatal("first ignore-delivery should report the slug as newly added")
}

config, _, err := LoadConfig(filepath.Join(repo, ".product-loop", "project.json"))
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(config.Workflow.IgnoredDeliveries, []string{"old-feature"}) {
t.Fatalf("ignored delivery not persisted: %+v", config.Workflow.IgnoredDeliveries)
}
// Other config must survive the round-trip.
if config.Project.Name != "fixture" || config.Workflow.HumanPlanApproval != true {
t.Fatalf("ignore-delivery clobbered unrelated config: %+v", config)
}

added, err = IgnoreDelivery(repo, "old-feature")
if err != nil {
t.Fatal(err)
}
if added {
t.Fatal("repeat ignore-delivery should be a no-op")
}
config, _, err = LoadConfig(filepath.Join(repo, ".product-loop", "project.json"))
if err != nil {
t.Fatal(err)
}
if len(config.Workflow.IgnoredDeliveries) != 1 {
t.Fatalf("ignore-delivery duplicated the slug: %+v", config.Workflow.IgnoredDeliveries)
}
}

func twoSlicePlan() map[string]any {
plan := validPlan()
plan["acceptance_criteria"] = []any{
Expand Down
Loading
Loading