From d42dcc6a2c8c654e1835178b43a800303218244b Mon Sep 17 00:00:00 2001 From: bigboateng Date: Wed, 12 Aug 2026 17:47:44 +0100 Subject: [PATCH 1/5] Close kernel conformance review gaps --- .github/tests/test_repository_contract.py | 26 ++++--- boatstack/kernel/conformance/conformance.go | 75 +++++++++++++++---- boatstack/kernel/conformance/integer.go | 24 +++++- ...-conformance-freshness-authority-guards.md | 3 + 4 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 release-notes/2026-08-12-conformance-freshness-authority-guards.md diff --git a/.github/tests/test_repository_contract.py b/.github/tests/test_repository_contract.py index 92f26d0..9e27333 100644 --- a/.github/tests/test_repository_contract.py +++ b/.github/tests/test_repository_contract.py @@ -16,6 +16,10 @@ REPO = Path(__file__).resolve().parents[2] RUNTIME = REPO / "boatstack" CONFIG = REPO / "project.example.json" +DOMAIN_NEUTRAL_PATTERN = re.compile( + r"\b(?:git|repository|worktree|branch|pull request|coding agent|publication)\b", + re.IGNORECASE, +) def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: @@ -36,6 +40,14 @@ def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: return json.loads(result.stdout) +def domain_vocabulary_hits(paths: list[Path]) -> list[tuple[Path, str]]: + hits: list[tuple[Path, str]] = [] + for path in paths: + for match in DOMAIN_NEUTRAL_PATTERN.finditer(path.read_text()): + hits.append((path, match.group(0).lower())) + return hits + + class RepositoryContract(unittest.TestCase): @classmethod def setUpClass(cls) -> None: @@ -415,15 +427,11 @@ def test_public_tree_excludes_private_context_and_v1_operating_guidance(self) -> def test_general_kernel_is_domain_neutral_and_owns_shared_control_laws(self) -> None: kernel = REPO / "boatstack" / "kernel" kernel_files = sorted(kernel.glob("*.go")) - production_files = [ - path for path in kernel_files if not path.name.endswith("_test.go") - ] - source = "\n".join(path.read_text() for path in production_files) - for token in ( - "git", "repository", "worktree", "branch", "pull request", - "coding agent", "publication", - ): - self.assertNotIn(token, source.lower(), token) + self.assertEqual([], domain_vocabulary_hits(kernel_files)) + with tempfile.TemporaryDirectory() as temporary: + fixture = Path(temporary) / "domain_leak_test.go" + fixture.write_text('package kernel\nconst fixtureDomain = "pull request"\n') + self.assertEqual([(fixture, "pull request")], domain_vocabulary_hits([fixture])) boatstack_packages = "github.com/operatorstack/boatstack/boatstack/" kernel_package = boatstack_packages + "kernel" diff --git a/boatstack/kernel/conformance/conformance.go b/boatstack/kernel/conformance/conformance.go index 1edecb0..69e2f2c 100644 --- a/boatstack/kernel/conformance/conformance.go +++ b/boatstack/kernel/conformance/conformance.go @@ -51,6 +51,8 @@ type Scenario struct { ChangeObservation func() RebindObjective func(kernel.Objective) BumpStateRevision func() + RetargetProgram func(kernel.ProgramIdentity) + AdvanceClock func(time.Duration) IndependentLocker func() kernel.Locker VerifyCommitted func(Snapshot, Snapshot, kernel.Receipt) error InterruptNextOperator func() @@ -87,6 +89,8 @@ func (suite KernelConformance) Run(t *testing.T) { t.Run("stale_prescription_precedes_effects", suite.stalePrescriptionPrecedesEffects) t.Run("authority_denial_fails_closed", suite.authorityDenialFailsClosed) t.Run("future_authority_fails_closed", suite.futureAuthorityFailsClosed) + t.Run("expired_authority_fails_closed", suite.expiredAuthorityFailsClosed) + t.Run("authority_expiry_invalidates_prescription_before_effects", suite.authorityExpiryInvalidatesPrescription) t.Run("capability_classifier_cannot_be_weakened", suite.capabilityClassifierCannotBeWeakened) t.Run("targeted_and_untargeted_share_one_relation", suite.targetedAndUntargetedShareRelation) t.Run("interrupted_operator_requires_explicit_recovery", suite.interruptedOperatorRequiresExplicitRecovery) @@ -161,19 +165,37 @@ func (suite KernelConformance) stateRevisionInvalidatesPrescription(t *testing.T } func (suite KernelConformance) programFingerprintInvalidatesPrescription(t *testing.T) { - fixture, runtime := suite.fresh(t, SetupBound) - transition := fixture.Scenario.AdvanceTransitions[0] - request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, fixture.Scenario.Authority) - alternate, err := kernel.NewRuntime(fixture.Scenario.AlternateProgram, fixture.Domain, fixture.Operator, fixture.CapabilityClassifier, fixture.Store, fixture.Locker, fixture.Clock) - if err != nil { - t.Fatal(err) - } - before := fixture.Scenario.Snapshot() - _, err = alternate.Apply(context.Background(), kernel.ApplyRequest{ResolveRequest: request, Prescription: prescription}) - after := fixture.Scenario.Snapshot() - if unchangedErr := refusedApplyMutationError(before, after, transition); err == nil || unchangedErr != nil { - t.Fatalf("control-law program-fingerprint-freshness: error=%v mutation=%v before=%#v after=%#v", err, unchangedErr, before, after) - } + t.Run("executable_mismatch", func(t *testing.T) { + fixture, runtime := suite.fresh(t, SetupBound) + transition := fixture.Scenario.AdvanceTransitions[0] + request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, fixture.Scenario.Authority) + alternate, err := kernel.NewRuntime(fixture.Scenario.AlternateProgram, fixture.Domain, fixture.Operator, fixture.CapabilityClassifier, fixture.Store, fixture.Locker, fixture.Clock) + if err != nil { + t.Fatal(err) + } + before := fixture.Scenario.Snapshot() + _, err = alternate.Apply(context.Background(), kernel.ApplyRequest{ResolveRequest: request, Prescription: prescription}) + after := fixture.Scenario.Snapshot() + if unchangedErr := refusedApplyMutationError(before, after, transition); err == nil || unchangedErr != nil { + t.Fatalf("control-law executable-program-freshness: error=%v mutation=%v before=%#v after=%#v", err, unchangedErr, before, after) + } + }) + t.Run("prescription_fingerprint", func(t *testing.T) { + fixture, runtime := suite.fresh(t, SetupBound) + transition := fixture.Scenario.AdvanceTransitions[0] + request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, fixture.Scenario.Authority) + fixture.Scenario.RetargetProgram(fixture.Scenario.AlternateProgram.Identity()) + alternate, err := kernel.NewRuntime(fixture.Scenario.AlternateProgram, fixture.Domain, fixture.Operator, fixture.CapabilityClassifier, fixture.Store, fixture.Locker, fixture.Clock) + if err != nil { + t.Fatal(err) + } + before := fixture.Scenario.Snapshot() + _, err = alternate.Apply(context.Background(), kernel.ApplyRequest{ResolveRequest: request, Prescription: prescription}) + after := fixture.Scenario.Snapshot() + if unchangedErr := refusedApplyMutationError(before, after, transition); !kernel.IsStale(err) || unchangedErr != nil { + t.Fatalf("control-law prescription-program-freshness: error=%v mutation=%v before=%#v after=%#v", err, unchangedErr, before, after) + } + }) } func (suite KernelConformance) stalePrescriptionPrecedesEffects(t *testing.T) { @@ -219,6 +241,31 @@ func (suite KernelConformance) futureAuthorityFailsClosed(t *testing.T) { } } +func (suite KernelConformance) expiredAuthorityFailsClosed(t *testing.T) { + fixture, runtime := suite.fresh(t, SetupBound) + transition := fixture.Scenario.AdvanceTransitions[0] + authority := fixture.Scenario.Authority + authority.Receipts = append([]kernel.AuthorityReceipt(nil), authority.Receipts...) + authority.Receipts[0].ExpiresAt = fixture.Clock.Now() + resolution, err := resolveWithoutMutation(context.Background(), runtime, fixture.Scenario, kernel.ResolveRequest{InstanceID: fixture.Scenario.InstanceID, Objective: &fixture.Scenario.Objective, Authority: authority, Requested: transition}) + if err != nil || resolution.Decision.Kind != kernel.Refused { + t.Fatalf("control-law expired-authority: decision=%#v error=%v", resolution.Decision, err) + } +} + +func (suite KernelConformance) authorityExpiryInvalidatesPrescription(t *testing.T) { + fixture, runtime := suite.fresh(t, SetupBound) + transition := fixture.Scenario.AdvanceTransitions[0] + request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, fixture.Scenario.Authority) + fixture.Scenario.AdvanceClock(2 * time.Hour) + before := fixture.Scenario.Snapshot() + _, err := runtime.Apply(context.Background(), kernel.ApplyRequest{ResolveRequest: request, Prescription: prescription}) + after := fixture.Scenario.Snapshot() + if unchangedErr := refusedApplyMutationError(before, after, transition); err == nil || unchangedErr != nil { + t.Fatalf("control-law apply-time-authority-expiry: error=%v mutation=%v before=%#v after=%#v", err, unchangedErr, before, after) + } +} + func (suite KernelConformance) capabilityClassifierCannotBeWeakened(t *testing.T) { fixture := suite.fixture(t, SetupBound) transition := fixture.Scenario.AdvanceTransitions[0] @@ -653,7 +700,7 @@ func (suite KernelConformance) fixture(t testing.TB, setup Setup) KernelConforma t.Fatal("kernel conformance requires a fresh fixture factory") } fixture := suite.New(t, setup) - if fixture.Domain == nil || fixture.Operator == nil || fixture.CapabilityClassifier == nil || fixture.Store == nil || fixture.Locker == nil || fixture.Clock == nil || fixture.Scenario.Snapshot == nil || fixture.Scenario.ChangeObservation == nil || fixture.Scenario.RebindObjective == nil || fixture.Scenario.BumpStateRevision == nil || fixture.Scenario.IndependentLocker == nil || fixture.Scenario.VerifyCommitted == nil || fixture.Scenario.InterruptNextOperator == nil || fixture.Scenario.PanicNextOperator == nil || fixture.Scenario.FailNextCommit == nil || fixture.Scenario.RetargetInstance == nil || fixture.Scenario.InstanceID == "" || fixture.Scenario.BindTransition == "" || len(fixture.Scenario.AdvanceTransitions) == 0 || fixture.Scenario.MaintenanceTransition == "" || fixture.Scenario.RecoveryTransition == "" || fixture.Scenario.RecoveryCapability.Validate() != nil || fixture.Scenario.ExtraCapability.Validate() != nil { + if fixture.Domain == nil || fixture.Operator == nil || fixture.CapabilityClassifier == nil || fixture.Store == nil || fixture.Locker == nil || fixture.Clock == nil || fixture.Scenario.Snapshot == nil || fixture.Scenario.ChangeObservation == nil || fixture.Scenario.RebindObjective == nil || fixture.Scenario.BumpStateRevision == nil || fixture.Scenario.RetargetProgram == nil || fixture.Scenario.AdvanceClock == nil || fixture.Scenario.IndependentLocker == nil || fixture.Scenario.VerifyCommitted == nil || fixture.Scenario.InterruptNextOperator == nil || fixture.Scenario.PanicNextOperator == nil || fixture.Scenario.FailNextCommit == nil || fixture.Scenario.RetargetInstance == nil || fixture.Scenario.InstanceID == "" || fixture.Scenario.BindTransition == "" || len(fixture.Scenario.AdvanceTransitions) == 0 || fixture.Scenario.MaintenanceTransition == "" || fixture.Scenario.RecoveryTransition == "" || fixture.Scenario.RecoveryCapability.Validate() != nil || fixture.Scenario.ExtraCapability.Validate() != nil { t.Fatal("kernel conformance fixture is incomplete") } if fixture.Scenario.RevisedObjective.Validate() != nil || fixture.Scenario.RevisedObjective.ID != fixture.Scenario.Objective.ID || fixture.Scenario.RevisedObjective.Revision <= fixture.Scenario.Objective.Revision || fixture.Scenario.RevisedObjective.Fingerprint == fixture.Scenario.Objective.Fingerprint { diff --git a/boatstack/kernel/conformance/integer.go b/boatstack/kernel/conformance/integer.go index 9cf5034..64ede1d 100644 --- a/boatstack/kernel/conformance/integer.go +++ b/boatstack/kernel/conformance/integer.go @@ -263,10 +263,23 @@ func (l memoryLock) Unlock() error { return nil } -// FixedClock returns one deterministic time. -type FixedClock struct{ Time time.Time } +// FixedClock returns one deterministic, test-controlled time. +type FixedClock struct { + mu sync.Mutex + Time time.Time +} + +func (c *FixedClock) Now() time.Time { + c.mu.Lock() + defer c.mu.Unlock() + return c.Time +} -func (c FixedClock) Now() time.Time { return c.Time } +func (c *FixedClock) advance(duration time.Duration) { + c.mu.Lock() + defer c.mu.Unlock() + c.Time = c.Time.Add(duration) +} // IntegerProgram compiles the reference control program. func IntegerProgram() (kernel.Program, error) { @@ -338,13 +351,14 @@ func newIntegerFixture(setup Setup) KernelConformance { domain := &IntegerDomain{value: value, executions: map[string]int{}} receipts := &MemoryReceipts{} store := &MemoryStateStore{state: state, receipts: receipts} + clock := &FixedClock{Time: now} fixture := KernelConformance{ Domain: domain, Operator: IntegerOperator{Domain: domain}, CapabilityClassifier: IntegerCapabilities{}, Store: store, Locker: &MemoryLocker{}, - Clock: FixedClock{Time: now}, + Clock: clock, Program: program, } fixture.Scenario = Scenario{ @@ -363,6 +377,8 @@ func newIntegerFixture(setup Setup) KernelConformance { ChangeObservation: domain.changeObservation, RebindObjective: store.rebind, BumpStateRevision: store.bumpRevision, + RetargetProgram: store.retargetProgram, + AdvanceClock: clock.advance, IndependentLocker: func() kernel.Locker { return &MemoryLocker{} }, VerifyCommitted: func(before, after Snapshot, receipt kernel.Receipt) error { return verifyIntegerCommitted(program, before, after, receipt) diff --git a/release-notes/2026-08-12-conformance-freshness-authority-guards.md b/release-notes/2026-08-12-conformance-freshness-authority-guards.md new file mode 100644 index 0000000..2de4210 --- /dev/null +++ b/release-notes/2026-08-12-conformance-freshness-authority-guards.md @@ -0,0 +1,3 @@ +### Close kernel conformance review gaps + +Kernel conformance now independently checks program-fingerprint freshness, authority expiry, and domain-neutral root tests. From cf15aa75bce75a2d9fa52d53783bf5d8c2f9d83f Mon Sep 17 00:00:00 2001 From: bigboateng Date: Wed, 12 Aug 2026 17:54:29 +0100 Subject: [PATCH 2/5] Harden conformance expiry and vocabulary checks --- .github/tests/test_repository_contract.py | 31 ++++++++++++++++----- boatstack/kernel/conformance/conformance.go | 5 +++- boatstack/kernel/conformance/integer.go | 2 +- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/.github/tests/test_repository_contract.py b/.github/tests/test_repository_contract.py index 9e27333..42a3521 100644 --- a/.github/tests/test_repository_contract.py +++ b/.github/tests/test_repository_contract.py @@ -16,10 +16,10 @@ REPO = Path(__file__).resolve().parents[2] RUNTIME = REPO / "boatstack" CONFIG = REPO / "project.example.json" -DOMAIN_NEUTRAL_PATTERN = re.compile( - r"\b(?:git|repository|worktree|branch|pull request|coding agent|publication)\b", - re.IGNORECASE, -) +DOMAIN_NEUTRAL_WORDS = {"git", "repository", "worktree", "branch", "publication"} +DOMAIN_NEUTRAL_PHRASE = re.compile(r"\b(?:pull\s+request|coding\s+agent)\b", re.IGNORECASE) +GO_IDENTIFIER = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") +GO_IDENTIFIER_PART = re.compile(r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+") def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: @@ -43,8 +43,18 @@ def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: def domain_vocabulary_hits(paths: list[Path]) -> list[tuple[Path, str]]: hits: list[tuple[Path, str]] = [] for path in paths: - for match in DOMAIN_NEUTRAL_PATTERN.finditer(path.read_text()): + source = path.read_text() + for match in DOMAIN_NEUTRAL_PHRASE.finditer(source): hits.append((path, match.group(0).lower())) + for identifier in GO_IDENTIFIER.findall(source): + if identifier.lower() == "github": + continue + parts = [ + part.lower() + for component in identifier.split("_") + for part in GO_IDENTIFIER_PART.findall(component) + ] + hits.extend((path, part) for part in parts if part in DOMAIN_NEUTRAL_WORDS) return hits @@ -430,8 +440,15 @@ def test_general_kernel_is_domain_neutral_and_owns_shared_control_laws(self) -> self.assertEqual([], domain_vocabulary_hits(kernel_files)) with tempfile.TemporaryDirectory() as temporary: fixture = Path(temporary) / "domain_leak_test.go" - fixture.write_text('package kernel\nconst fixtureDomain = "pull request"\n') - self.assertEqual([(fixture, "pull request")], domain_vocabulary_hits([fixture])) + for source, token in ( + ('package kernel\nconst fixtureDomain = "pull request"\n', "pull request"), + ("package kernel\ntype gitClient struct{}\n", "git"), + ("package kernel\nvar testRepository string\n", "repository"), + ("package kernel\ntype worktreeManager struct{}\n", "worktree"), + ): + with self.subTest(token=token): + fixture.write_text(source) + self.assertEqual([(fixture, token)], domain_vocabulary_hits([fixture])) boatstack_packages = "github.com/operatorstack/boatstack/boatstack/" kernel_package = boatstack_packages + "kernel" diff --git a/boatstack/kernel/conformance/conformance.go b/boatstack/kernel/conformance/conformance.go index 69e2f2c..46eb873 100644 --- a/boatstack/kernel/conformance/conformance.go +++ b/boatstack/kernel/conformance/conformance.go @@ -256,7 +256,10 @@ func (suite KernelConformance) expiredAuthorityFailsClosed(t *testing.T) { func (suite KernelConformance) authorityExpiryInvalidatesPrescription(t *testing.T) { fixture, runtime := suite.fresh(t, SetupBound) transition := fixture.Scenario.AdvanceTransitions[0] - request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, fixture.Scenario.Authority) + authority := fixture.Scenario.Authority + authority.Receipts = append([]kernel.AuthorityReceipt(nil), authority.Receipts...) + authority.Receipts[0].ExpiresAt = fixture.Clock.Now().Add(time.Hour) + request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, authority) fixture.Scenario.AdvanceClock(2 * time.Hour) before := fixture.Scenario.Snapshot() _, err := runtime.Apply(context.Background(), kernel.ApplyRequest{ResolveRequest: request, Prescription: prescription}) diff --git a/boatstack/kernel/conformance/integer.go b/boatstack/kernel/conformance/integer.go index 64ede1d..bf19db6 100644 --- a/boatstack/kernel/conformance/integer.go +++ b/boatstack/kernel/conformance/integer.go @@ -347,7 +347,7 @@ func newIntegerFixture(setup Setup) KernelConformance { state.Mode, state.ObjectiveBinding, value = "one", &binding, 1 } now := time.Date(2026, 8, 12, 10, 0, 0, 0, time.UTC) - authority := kernel.Authority{Receipts: []kernel.AuthorityReceipt{{ID: "human-counter", Subject: "fixture", Fingerprint: "fixture-authority", Capabilities: []kernel.Capability{"counter.audit", "counter.increment", "counter.reset", "objective.bind"}, IssuedAt: now.Add(-time.Minute), ExpiresAt: now.Add(time.Hour)}}} + authority := kernel.Authority{Receipts: []kernel.AuthorityReceipt{{ID: "human-counter", Subject: "fixture", Fingerprint: "fixture-authority", Capabilities: []kernel.Capability{"counter.audit", "counter.increment", "counter.reset", "objective.bind"}, IssuedAt: now.Add(-time.Minute), ExpiresAt: now.Add(24 * time.Hour)}}} domain := &IntegerDomain{value: value, executions: map[string]int{}} receipts := &MemoryReceipts{} store := &MemoryStateStore{state: state, receipts: receipts} From 71c7937c6acf40803c678560da0d902f7af1bc97 Mon Sep 17 00:00:00 2001 From: bigboateng Date: Wed, 12 Aug 2026 18:02:04 +0100 Subject: [PATCH 3/5] Enforce isolated conformance fixture dimensions --- .github/tests/go_source_metadata.go | 18 ++++++++-- .github/tests/test_repository_contract.py | 33 +++++++++++-------- boatstack/kernel/conformance/conformance.go | 31 ++++++++++++++++- .../kernel/conformance/conformance_test.go | 25 ++++++++++++++ 4 files changed, 90 insertions(+), 17 deletions(-) diff --git a/.github/tests/go_source_metadata.go b/.github/tests/go_source_metadata.go index 2bd0ab1..56c31e1 100644 --- a/.github/tests/go_source_metadata.go +++ b/.github/tests/go_source_metadata.go @@ -27,6 +27,7 @@ type metadata struct { Path string `json:"path"` Imports []string `json:"imports"` RuntimeConsumers []string `json:"runtime_consumers"` + Vocabulary []string `json:"vocabulary"` } func main() { @@ -35,11 +36,11 @@ func main() { if filename == "--" { continue } - parsed, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.AllErrors) + parsed, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.AllErrors|parser.ParseComments) if err != nil { fatalf("parse %s: %v", filename, err) } - item := metadata{Path: filename, Imports: []string{}, RuntimeConsumers: []string{}} + item := metadata{Path: filename, Imports: []string{}, RuntimeConsumers: []string{}, Vocabulary: []string{}} kernelAliases := map[string]bool{} for _, spec := range parsed.Imports { importPath, err := strconv.Unquote(spec.Path.Value) @@ -58,20 +59,33 @@ func main() { } ast.Inspect(parsed, func(node ast.Node) bool { switch value := node.(type) { + case *ast.ImportSpec: + return false case *ast.SelectorExpr: alias, ok := value.X.(*ast.Ident) if ok && kernelAliases[alias.Name] && runtimeSymbols[value.Sel.Name] { item.RuntimeConsumers = append(item.RuntimeConsumers, value.Sel.Name) } case *ast.Ident: + item.Vocabulary = append(item.Vocabulary, value.Name) if kernelAliases["."] && runtimeSymbols[value.Name] { item.RuntimeConsumers = append(item.RuntimeConsumers, value.Name) } + case *ast.BasicLit: + if value.Kind == token.STRING { + decoded, err := strconv.Unquote(value.Value) + if err == nil { + item.Vocabulary = append(item.Vocabulary, decoded) + } + } + case *ast.Comment: + item.Vocabulary = append(item.Vocabulary, value.Text) } return true }) sort.Strings(item.Imports) sort.Strings(item.RuntimeConsumers) + sort.Strings(item.Vocabulary) results = append(results, item) } if err := json.NewEncoder(os.Stdout).Encode(results); err != nil { diff --git a/.github/tests/test_repository_contract.py b/.github/tests/test_repository_contract.py index 42a3521..9151ab8 100644 --- a/.github/tests/test_repository_contract.py +++ b/.github/tests/test_repository_contract.py @@ -16,7 +16,7 @@ REPO = Path(__file__).resolve().parents[2] RUNTIME = REPO / "boatstack" CONFIG = REPO / "project.example.json" -DOMAIN_NEUTRAL_WORDS = {"git", "repository", "worktree", "branch", "publication"} +DOMAIN_NEUTRAL_WORDS = {"git", "github", "repository", "worktree", "branch", "publication"} DOMAIN_NEUTRAL_PHRASE = re.compile(r"\b(?:pull\s+request|coding\s+agent)\b", re.IGNORECASE) GO_IDENTIFIER = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") GO_IDENTIFIER_PART = re.compile(r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+") @@ -42,19 +42,20 @@ def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: def domain_vocabulary_hits(paths: list[Path]) -> list[tuple[Path, str]]: hits: list[tuple[Path, str]] = [] - for path in paths: - source = path.read_text() - for match in DOMAIN_NEUTRAL_PHRASE.finditer(source): - hits.append((path, match.group(0).lower())) - for identifier in GO_IDENTIFIER.findall(source): - if identifier.lower() == "github": - continue - parts = [ - part.lower() - for component in identifier.split("_") - for part in GO_IDENTIFIER_PART.findall(component) - ] - hits.extend((path, part) for part in parts if part in DOMAIN_NEUTRAL_WORDS) + for path, metadata in zip(paths, go_source_metadata(paths), strict=True): + for source in metadata["vocabulary"]: + for match in DOMAIN_NEUTRAL_PHRASE.finditer(source): + hits.append((path, match.group(0).lower())) + for identifier in GO_IDENTIFIER.findall(source): + if identifier.lower().startswith("github"): + hits.append((path, "github")) + continue + parts = [ + part.lower() + for component in identifier.split("_") + for part in GO_IDENTIFIER_PART.findall(component) + ] + hits.extend((path, part) for part in parts if part in DOMAIN_NEUTRAL_WORDS) return hits @@ -445,10 +446,14 @@ def test_general_kernel_is_domain_neutral_and_owns_shared_control_laws(self) -> ("package kernel\ntype gitClient struct{}\n", "git"), ("package kernel\nvar testRepository string\n", "repository"), ("package kernel\ntype worktreeManager struct{}\n", "worktree"), + ('package kernel\nconst provider = "github"\n', "github"), + ("package kernel\ntype githubClient struct{}\n", "github"), ): with self.subTest(token=token): fixture.write_text(source) self.assertEqual([(fixture, token)], domain_vocabulary_hits([fixture])) + fixture.write_text('package kernel\nimport "github.com/example/provider"\n') + self.assertEqual([], domain_vocabulary_hits([fixture])) boatstack_packages = "github.com/operatorstack/boatstack/boatstack/" kernel_package = boatstack_packages + "kernel" diff --git a/boatstack/kernel/conformance/conformance.go b/boatstack/kernel/conformance/conformance.go index 46eb873..5cb0e91 100644 --- a/boatstack/kernel/conformance/conformance.go +++ b/boatstack/kernel/conformance/conformance.go @@ -184,7 +184,12 @@ func (suite KernelConformance) programFingerprintInvalidatesPrescription(t *test fixture, runtime := suite.fresh(t, SetupBound) transition := fixture.Scenario.AdvanceTransitions[0] request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, fixture.Scenario.Authority) + beforeRetarget := fixture.Scenario.Snapshot() fixture.Scenario.RetargetProgram(fixture.Scenario.AlternateProgram.Identity()) + afterRetarget := fixture.Scenario.Snapshot() + if err := retargetProgramError(beforeRetarget, afterRetarget, fixture.Scenario.AlternateProgram.Identity()); err != nil { + t.Fatal(err) + } alternate, err := kernel.NewRuntime(fixture.Scenario.AlternateProgram, fixture.Domain, fixture.Operator, fixture.CapabilityClassifier, fixture.Store, fixture.Locker, fixture.Clock) if err != nil { t.Fatal(err) @@ -260,7 +265,15 @@ func (suite KernelConformance) authorityExpiryInvalidatesPrescription(t *testing authority.Receipts = append([]kernel.AuthorityReceipt(nil), authority.Receipts...) authority.Receipts[0].ExpiresAt = fixture.Clock.Now().Add(time.Hour) request, prescription := resolve(t, runtime, fixture.Scenario, transition, &fixture.Scenario.Objective, authority) - fixture.Scenario.AdvanceClock(2 * time.Hour) + duration := 2 * time.Hour + beforeClock := fixture.Clock.Now() + beforeAdvance := fixture.Scenario.Snapshot() + fixture.Scenario.AdvanceClock(duration) + afterClock := fixture.Clock.Now() + afterAdvance := fixture.Scenario.Snapshot() + if err := clockAdvanceError(beforeAdvance, afterAdvance, beforeClock, afterClock, duration); err != nil { + t.Fatal(err) + } before := fixture.Scenario.Snapshot() _, err := runtime.Apply(context.Background(), kernel.ApplyRequest{ResolveRequest: request, Prescription: prescription}) after := fixture.Scenario.Snapshot() @@ -269,6 +282,22 @@ func (suite KernelConformance) authorityExpiryInvalidatesPrescription(t *testing } } +func retargetProgramError(before, after Snapshot, program kernel.ProgramIdentity) error { + expected := before + expected.State.Program = program + if !reflect.DeepEqual(after, expected) { + return fmt.Errorf("control-law program-retarget fixture changed evidence outside State.Program: before=%#v after=%#v", before, after) + } + return nil +} + +func clockAdvanceError(before, after Snapshot, beforeTime, afterTime time.Time, duration time.Duration) error { + if !reflect.DeepEqual(after, before) || !afterTime.Equal(beforeTime.Add(duration)) { + return fmt.Errorf("control-law clock-advance fixture changed snapshot evidence or advanced by the wrong duration: before=%#v after=%#v before_time=%v after_time=%v duration=%v", before, after, beforeTime, afterTime, duration) + } + return nil +} + func (suite KernelConformance) capabilityClassifierCannotBeWeakened(t *testing.T) { fixture := suite.fixture(t, SetupBound) transition := fixture.Scenario.AdvanceTransitions[0] diff --git a/boatstack/kernel/conformance/conformance_test.go b/boatstack/kernel/conformance/conformance_test.go index 4e923cb..4d74995 100644 --- a/boatstack/kernel/conformance/conformance_test.go +++ b/boatstack/kernel/conformance/conformance_test.go @@ -38,6 +38,31 @@ func TestIntegerBoundFixtureIncludesCommittedHistory(t *testing.T) { } } +func TestRetargetProgramCheckRejectsAdditionalFreshnessChanges(t *testing.T) { + fixture := newIntegerFixture(SetupBound) + before := fixture.Scenario.Snapshot() + fixture.Scenario.RetargetProgram(fixture.Scenario.AlternateProgram.Identity()) + fixture.Scenario.BumpStateRevision() + after := fixture.Scenario.Snapshot() + if err := retargetProgramError(before, after, fixture.Scenario.AlternateProgram.Identity()); err == nil { + t.Fatal("expected revision-changing program hook to be rejected") + } +} + +func TestAdvanceClockCheckRejectsAdditionalFreshnessChanges(t *testing.T) { + fixture := newIntegerFixture(SetupBound) + duration := time.Hour + beforeTime := fixture.Clock.Now() + before := fixture.Scenario.Snapshot() + fixture.Scenario.AdvanceClock(duration) + fixture.Scenario.ChangeObservation() + afterTime := fixture.Clock.Now() + after := fixture.Scenario.Snapshot() + if err := clockAdvanceError(before, after, beforeTime, afterTime, duration); err == nil { + t.Fatal("expected observation-changing clock hook to be rejected") + } +} + func TestResolveWithoutMutationRejectsEffectfulLoad(t *testing.T) { fixture := newIntegerFixture(SetupBound) base := fixture.Store.(*MemoryStateStore) From fbd8679d2557756ad61bdf16ddb77729ba2fdc34 Mon Sep 17 00:00:00 2001 From: bigboateng Date: Wed, 12 Aug 2026 18:09:59 +0100 Subject: [PATCH 4/5] Close domain vocabulary identifier forms --- .github/tests/test_repository_contract.py | 35 ++++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/tests/test_repository_contract.py b/.github/tests/test_repository_contract.py index 9151ab8..733ea40 100644 --- a/.github/tests/test_repository_contract.py +++ b/.github/tests/test_repository_contract.py @@ -16,10 +16,18 @@ REPO = Path(__file__).resolve().parents[2] RUNTIME = REPO / "boatstack" CONFIG = REPO / "project.example.json" -DOMAIN_NEUTRAL_WORDS = {"git", "github", "repository", "worktree", "branch", "publication"} +DOMAIN_NEUTRAL_ROOTS = ( + ("pullrequest", "pull request"), + ("codingagent", "coding agent"), + ("github", "github"), + ("repository", "repository"), + ("worktree", "worktree"), + ("publication", "publication"), + ("branch", "branch"), + ("git", "git"), +) DOMAIN_NEUTRAL_PHRASE = re.compile(r"\b(?:pull\s+request|coding\s+agent)\b", re.IGNORECASE) GO_IDENTIFIER = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") -GO_IDENTIFIER_PART = re.compile(r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+") def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: @@ -47,15 +55,11 @@ def domain_vocabulary_hits(paths: list[Path]) -> list[tuple[Path, str]]: for match in DOMAIN_NEUTRAL_PHRASE.finditer(source): hits.append((path, match.group(0).lower())) for identifier in GO_IDENTIFIER.findall(source): - if identifier.lower().startswith("github"): - hits.append((path, "github")) - continue - parts = [ - part.lower() - for component in identifier.split("_") - for part in GO_IDENTIFIER_PART.findall(component) - ] - hits.extend((path, part) for part in parts if part in DOMAIN_NEUTRAL_WORDS) + compact = identifier.lower().replace("_", "") + for root, token in DOMAIN_NEUTRAL_ROOTS: + if root in compact: + hits.append((path, token)) + break return hits @@ -448,6 +452,15 @@ def test_general_kernel_is_domain_neutral_and_owns_shared_control_laws(self) -> ("package kernel\ntype worktreeManager struct{}\n", "worktree"), ('package kernel\nconst provider = "github"\n', "github"), ("package kernel\ntype githubClient struct{}\n", "github"), + ("package kernel\ntype gitclient struct{}\n", "git"), + ("package kernel\ntype mygitclient struct{}\n", "git"), + ("package kernel\ntype clientgit struct{}\n", "git"), + ("package kernel\ntype repositoryclient struct{}\n", "repository"), + ("package kernel\ntype worktreemanager struct{}\n", "worktree"), + ("package kernel\ntype branchmanager struct{}\n", "branch"), + ("package kernel\ntype publicationqueue struct{}\n", "publication"), + ("package kernel\ntype pullrequesthandler struct{}\n", "pull request"), + ("package kernel\ntype codingagentpolicy struct{}\n", "coding agent"), ): with self.subTest(token=token): fixture.write_text(source) From cd211c23a01573397f68fda2a618bf3a0b1c4759 Mon Sep 17 00:00:00 2001 From: bigboateng Date: Wed, 12 Aug 2026 18:18:01 +0100 Subject: [PATCH 5/5] Separate import and identifier domain checks --- .github/tests/test_repository_contract.py | 50 +++++++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/.github/tests/test_repository_contract.py b/.github/tests/test_repository_contract.py index 733ea40..5d56248 100644 --- a/.github/tests/test_repository_contract.py +++ b/.github/tests/test_repository_contract.py @@ -28,6 +28,7 @@ ) DOMAIN_NEUTRAL_PHRASE = re.compile(r"\b(?:pull\s+request|coding\s+agent)\b", re.IGNORECASE) GO_IDENTIFIER = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") +GO_IDENTIFIER_PART = re.compile(r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+") def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: @@ -48,18 +49,45 @@ def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: return json.loads(result.stdout) +def identifier_domain_token(identifier: str) -> str | None: + for component in identifier.split("_"): + for part in GO_IDENTIFIER_PART.findall(component): + normalized = part.lower() + for root, token in DOMAIN_NEUTRAL_ROOTS: + if normalized == root or normalized.startswith(root): + return token + return None + + +def import_domain_token(import_path: str) -> str | None: + components = import_path.split("/") + if components and components[0] == "github.com": + components = components[1:] + for component in components: + for identifier in re.split(r"[.-]", component): + token = identifier_domain_token(identifier) + if token is not None: + return token + return None + + def domain_vocabulary_hits(paths: list[Path]) -> list[tuple[Path, str]]: hits: list[tuple[Path, str]] = [] for path, metadata in zip(paths, go_source_metadata(paths), strict=True): + for import_path in metadata["imports"]: + token = import_domain_token(import_path) + if token is not None: + hits.append((path, token)) for source in metadata["vocabulary"]: - for match in DOMAIN_NEUTRAL_PHRASE.finditer(source): - hits.append((path, match.group(0).lower())) + phrase = DOMAIN_NEUTRAL_PHRASE.search(source) + if phrase is not None: + hits.append((path, phrase.group(0).lower())) + continue for identifier in GO_IDENTIFIER.findall(source): - compact = identifier.lower().replace("_", "") - for root, token in DOMAIN_NEUTRAL_ROOTS: - if root in compact: - hits.append((path, token)) - break + token = identifier_domain_token(identifier) + if token is not None: + hits.append((path, token)) + break return hits @@ -453,8 +481,6 @@ def test_general_kernel_is_domain_neutral_and_owns_shared_control_laws(self) -> ('package kernel\nconst provider = "github"\n', "github"), ("package kernel\ntype githubClient struct{}\n", "github"), ("package kernel\ntype gitclient struct{}\n", "git"), - ("package kernel\ntype mygitclient struct{}\n", "git"), - ("package kernel\ntype clientgit struct{}\n", "git"), ("package kernel\ntype repositoryclient struct{}\n", "repository"), ("package kernel\ntype worktreemanager struct{}\n", "worktree"), ("package kernel\ntype branchmanager struct{}\n", "branch"), @@ -467,6 +493,12 @@ def test_general_kernel_is_domain_neutral_and_owns_shared_control_laws(self) -> self.assertEqual([(fixture, token)], domain_vocabulary_hits([fixture])) fixture.write_text('package kernel\nimport "github.com/example/provider"\n') self.assertEqual([], domain_vocabulary_hits([fixture])) + fixture.write_text( + 'package kernel\nimport vcs "github.com/go-git/go-git/v5"\nvar _ = vcs.PlainClone\n' + ) + self.assertEqual([(fixture, "git")], domain_vocabulary_hits([fixture])) + fixture.write_text("package kernel\nfunc TestDigitalSignature() {}\n") + self.assertEqual([], domain_vocabulary_hits([fixture])) boatstack_packages = "github.com/operatorstack/boatstack/boatstack/" kernel_package = boatstack_packages + "kernel"