diff --git a/CHANGELOG.md b/CHANGELOG.md index dbee3e9..448771d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,11 +119,13 @@ repository still gets a decision, never by following the link; no release carrie - :sparkles: feat(examples): thicken service-catalog with nested sla/runtime objects - :sparkles: feat(examples): thicken topic-registry nested YAML and nested-pointer rules - :sparkles: feat(examples): thicken infra-vars nested tfvars maps (EX-S04) +- :sparkles: feat(examples): close REF-EX C1-C4 in topic-registry/service-catalog ### Fixes - :bug: fix(ci): pin ci-audit-test in the AUD-S18 check-stage list - :bug: specs(p5-ex): make S10 schema freeze and S07 fence non-vacuous - :bug: fix(docs-gates): fail on unmapped format tokens and de-confound the tests-dir mutation (EX-S01 review) +- :bug: fix(examples): make privilege-tier's proving fixture load-bearing ### Refactoring - :recycle: refactor(examples): discover dogfood packs instead of a hardcoded 3-name loop diff --git a/examples/packs/service-catalog/.assent/packs/catalog/rules/privilege-tier.yaml b/examples/packs/service-catalog/.assent/packs/catalog/rules/privilege-tier.yaml new file mode 100644 index 0000000..eb75427 --- /dev/null +++ b/examples/packs/service-catalog/.assent/packs/catalog/rules/privilege-tier.yaml @@ -0,0 +1,46 @@ +# Privilege-tier allow-list (REF-EX C2) — /services/*/tier already exists on every +# service entry and is already caught by only-safe-fields (any non-/oncall field +# change escalates to require-review, allowed-fields.yaml). This rule adds a +# NARROWER, value-level check on top of that coarse field-level one: a tier value +# outside the allow-list is a privilege-escalation attempt, not just an "other +# field changed" event. Reuses the allowed-fields obligation (already required) +# so no bindings.yaml change is needed. D-061: this stays a scalar leaf check on +# the already-governed keyed-list entry field — no unkeyed nested list is +# reintroduced. +# +# Allow-list is [1, 3], deliberately EXCLUDING 2: tier 2 is a deprecated +# mid-band value being consolidated away (existing tier-2 services, e.g. +# storefront-web in the S02 fixtures, are grandfathered — they simply never +# trigger a tier MODIFY — but no NEW assignment to 2 is permitted). This is +# also what makes the rule's proving polarity load-bearing: a flat [1,2,3] +# allow-list would let the pre-existing S02 allowed-fields/negative golden +# (which coincidentally moves tier 1->2) silently double as this rule's +# proving-polarity witness, so deleting THIS pack's own privilege-tier/ +# positive fixture would leave --coverage falsely green. With 2 excluded, that +# S02 fixture's 1->2 change instead becomes a (tolerated, unasserted) FAILING +# firing of this rule — proving-polarity coverage comes ONLY from this pack's +# own privilege-tier/ fixture (tier 1->3). +apiVersion: assent.dev/v1alpha1 +kind: MergePolicy +metadata: + name: catalog-privilege-tier +spec: + entries: + catalog-service: + mode: list + root: /services + identity: + pointer: /name + rules: + - name: tier-allow-list + phase: enforce + match: + valueChanges: + pointers: ["/services/*/tier"] + kinds: [modify] + prove: + obligation: allowed-fields + when: "new in [1, 3]" + onFailure: + effect: require-review + code: allowed-fields.tier-out-of-range diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/base/catalog/prod/core-services.json b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/base/catalog/prod/core-services.json new file mode 100644 index 0000000..a22f42c --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/base/catalog/prod/core-services.json @@ -0,0 +1,47 @@ +{ + "apiVersion": "catalog/v1", + "services": [ + { + "name": "orders-api", + "owner": "orders-team", + "tier": 1, + "oncall": "orders-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "payments-gateway", + "owner": "payments-team", + "tier": 1, + "oncall": "payments-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "storefront-web", + "owner": "storefront-team", + "tier": 2, + "oncall": "storefront-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + } + ] +} diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/expect.yaml b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/expect.yaml new file mode 100644 index 0000000..680c01b --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/expect.yaml @@ -0,0 +1,12 @@ +# tier 1 -> 3 is within the allow-list ([1,3]) -> tier-allow-list proves silently +# (its proving polarity — and, since 2 is deliberately excluded from the +# allow-list, this fixture is the SOLE source of that proving credit in the +# pack: deleting it genuinely reddens --coverage, see the rule file comment). +# The pre-existing only-safe-fields rule still escalates ANY non-/oncall field +# change to require-review regardless of value — that is unrelated +# coarse-grained coverage, not this rule's finding. +decision: REVIEW +findings: + - rule: only-safe-fields + obligation: allowed-fields + effect: require-review diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/facts.yaml b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/facts.yaml new file mode 100644 index 0000000..940a17a --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [orders-team] +oncall: + orders_rotation: "ready" +schema: + valid: true diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/head/catalog/prod/core-services.json b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/head/catalog/prod/core-services.json new file mode 100644 index 0000000..b18e9fb --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/head/catalog/prod/core-services.json @@ -0,0 +1,47 @@ +{ + "apiVersion": "catalog/v1", + "services": [ + { + "name": "orders-api", + "owner": "orders-team", + "tier": 3, + "oncall": "orders-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "payments-gateway", + "owner": "payments-team", + "tier": 1, + "oncall": "payments-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "storefront-web", + "owner": "storefront-team", + "tier": 2, + "oncall": "storefront-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + } + ] +} diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/base/catalog/prod/core-services.json b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/base/catalog/prod/core-services.json new file mode 100644 index 0000000..a22f42c --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/base/catalog/prod/core-services.json @@ -0,0 +1,47 @@ +{ + "apiVersion": "catalog/v1", + "services": [ + { + "name": "orders-api", + "owner": "orders-team", + "tier": 1, + "oncall": "orders-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "payments-gateway", + "owner": "payments-team", + "tier": 1, + "oncall": "payments-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "storefront-web", + "owner": "storefront-team", + "tier": 2, + "oncall": "storefront-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + } + ] +} diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/expect.yaml b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/expect.yaml new file mode 100644 index 0000000..6c77f1a --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/expect.yaml @@ -0,0 +1,11 @@ +# tier 1 -> 0 is outside the allow-list ([1,3]) -> tier-allow-list fails, on top +# of the coarse-grained only-safe-fields escalation (both effects are +# require-review, so the decision stays REVIEW either way). +decision: REVIEW +findings: + - rule: only-safe-fields + obligation: allowed-fields + effect: require-review + - rule: tier-allow-list + obligation: allowed-fields + effect: require-review diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/facts.yaml b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/facts.yaml new file mode 100644 index 0000000..940a17a --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [orders-team] +oncall: + orders_rotation: "ready" +schema: + valid: true diff --git a/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/head/catalog/prod/core-services.json b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/head/catalog/prod/core-services.json new file mode 100644 index 0000000..954b75b --- /dev/null +++ b/examples/packs/service-catalog/.assent/tests/catalog/privilege-tier/negative/head/catalog/prod/core-services.json @@ -0,0 +1,47 @@ +{ + "apiVersion": "catalog/v1", + "services": [ + { + "name": "orders-api", + "owner": "orders-team", + "tier": 0, + "oncall": "orders-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "payments-gateway", + "owner": "payments-team", + "tier": 1, + "oncall": "payments-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + }, + { + "name": "storefront-web", + "owner": "storefront-team", + "tier": 2, + "oncall": "storefront-rotation", + "sla": { + "slo_percent": 99.9, + "window": "30d" + }, + "runtime": { + "language": "go", + "replicas": 3 + } + } + ] +} diff --git a/examples/packs/topic-registry/.assent/bindings.yaml b/examples/packs/topic-registry/.assent/bindings.yaml index b27edcf..36bd58d 100644 --- a/examples/packs/topic-registry/.assent/bindings.yaml +++ b/examples/packs/topic-registry/.assent/bindings.yaml @@ -5,9 +5,9 @@ bindings: environment: dev packs: [topics] risk: { threshold: 10 } - require: [ownership, bounded-change, non-destructive, schema-valid] + require: [ownership, bounded-change, non-destructive, schema-valid, wildcard-grant] - class: kafka-topic environment: prod packs: [topics] risk: { threshold: 4 } - require: [ownership, bounded-change, non-destructive, schema-valid] + require: [ownership, bounded-change, non-destructive, schema-valid, wildcard-grant] diff --git a/examples/packs/topic-registry/.assent/packs/topics/rules/list-no-shrink.yaml b/examples/packs/topic-registry/.assent/packs/topics/rules/list-no-shrink.yaml new file mode 100644 index 0000000..e8bc997 --- /dev/null +++ b/examples/packs/topic-registry/.assent/packs/topics/rules/list-no-shrink.yaml @@ -0,0 +1,32 @@ +# List-no-shrink (REF-EX C1) — identity-keyed `consumers` map on a topic entry. +# consumers is keyed by consumer name (not an unkeyed list, ADR-0017 §5), so a +# per-key add/delete is a genuine one-sided scalar change the differ can represent +# (E1-S05: a one-sided ADD of a whole new mapping is opaque, but consumers already +# exists as a map on every topic that declares it — only the LEAF key is one-sided). +# Reuses the no-destruction pattern (non-destructive obligation, cf. +# non-destructive.yaml): a consumer key ADD proves (kind != "delete"), a consumer +# key DELETE fails -> require-review. Pointer-scoped to /*/consumers/* so this rule +# is silent (vacuous, no match at all) on every topic file that never declares +# `consumers` — the S02 goldens stay green untouched. +apiVersion: assent.dev/v1alpha1 +kind: MergePolicy +metadata: + name: topics-list-no-shrink +spec: + entries: + kafka-topic: + mode: map + root: "" + rules: + - name: consumer-removal + phase: enforce + match: + valueChanges: + pointers: ["/*/consumers/*"] + kinds: [add, delete] + prove: + obligation: non-destructive + when: 'kind != "delete"' + onFailure: + effect: require-review + code: destruction.consumer-removed diff --git a/examples/packs/topic-registry/.assent/packs/topics/rules/soft-delete.yaml b/examples/packs/topic-registry/.assent/packs/topics/rules/soft-delete.yaml new file mode 100644 index 0000000..887c796 --- /dev/null +++ b/examples/packs/topic-registry/.assent/packs/topics/rules/soft-delete.yaml @@ -0,0 +1,32 @@ +# Soft-delete-as-field-add (REF-EX C4) — a topic adding `status: retired` (a field +# base entirely lacked) is a destructive lifecycle transition disguised as an +# innocuous field add, not a silent-APPROVE metadata edit. Reuses the +# non-destructive obligation (cf. non-destructive.yaml / list-no-shrink.yaml): +# matched ONLY on kind add at the dedicated /*/status pointer, so a modify of any +# OTHER field (retention_hours, description, ...) never fires this rule even when +# status is untouched (Edge case in REQ-EX-S06-04) — proven by every existing S02 +# golden, none of which declare `status` at all (vacuous, no match). Both +# polarities exercise the SAME add-kind match: adding status as anything other +# than "retired" proves silently; adding it as exactly "retired" fails. +apiVersion: assent.dev/v1alpha1 +kind: MergePolicy +metadata: + name: topics-soft-delete +spec: + entries: + kafka-topic: + mode: map + root: "" + rules: + - name: status-retired-add + phase: enforce + match: + valueChanges: + pointers: ["/*/status"] + kinds: [add] + prove: + obligation: non-destructive + when: 'new != "retired"' + onFailure: + effect: require-review + code: destruction.soft-delete diff --git a/examples/packs/topic-registry/.assent/packs/topics/rules/wildcard-grant.yaml b/examples/packs/topic-registry/.assent/packs/topics/rules/wildcard-grant.yaml new file mode 100644 index 0000000..7543ab7 --- /dev/null +++ b/examples/packs/topic-registry/.assent/packs/topics/rules/wildcard-grant.yaml @@ -0,0 +1,36 @@ +# Wildcard-grant block (REF-EX C3) — nested acl.grants map, keyed by principal. +# A grant keyed by the literal wildcard principal "*" (i.e. "any principal may act +# on this topic") is a security escalation disguised as an ordinary ACL entry -> +# BLOCK, not a soft require-review. An explicit, named principal key does not +# block. `path` carries the full JSON Pointer of the matched change (mirrors +# allowed-fields.yaml's path.endsWith usage), so the last pointer segment IS the +# principal key; a wildcard grant's pointer always ends in the literal "/*" +# segment. Pointer-scoped to /*/acl/grants/* so this rule never matches (vacuous) +# on a topic that has no acl.grants at all — the S02 goldens stay green. A +# brand-new obligation (`wildcard-grant`, added to bindings.yaml require:) is safe +# to require: coverage marks an obligation "covered" the instant an enforce rule +# names it, independent of whether any subject actually matches it (E2-S04 +# uncovered-obligation guard), so every existing golden — none of which declare +# acl — stays vacuously satisfied. +apiVersion: assent.dev/v1alpha1 +kind: MergePolicy +metadata: + name: topics-wildcard-grant +spec: + entries: + kafka-topic: + mode: map + root: "" + rules: + - name: wildcard-grant-block + phase: enforce + match: + valueChanges: + pointers: ["/*/acl/grants/*"] + kinds: [add, modify] + prove: + obligation: wildcard-grant + when: '!path.endsWith("/*")' + onFailure: + effect: block + code: acl.wildcard-grant diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/base/topics/prod/notifications.events.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/base/topics/prod/notifications.events.v1.yaml new file mode 100644 index 0000000..c60de97 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/base/topics/prod/notifications.events.v1.yaml @@ -0,0 +1,19 @@ +notifications.events.v1: + owner: growth-team + partitions: 8 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: notifications.events.v1-value + compatibility: BACKWARD + references: + notifications.common: 1 + compaction: + strategy: delete + description: > + Notification dispatch events consumed by downstream channels. + consumers: + email-service: reader + sms-service: reader diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/expect.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/expect.yaml new file mode 100644 index 0000000..4b400da --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/expect.yaml @@ -0,0 +1,3 @@ +# consumers key ADD (push-service) -> kind != "delete" -> consumer-removal proves silently. +decision: APPROVE +findings: [] diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/facts.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/facts.yaml new file mode 100644 index 0000000..9f7007b --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [growth-team] +quota: + max_partitions: 24 +schema: + valid: true diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/head/topics/prod/notifications.events.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/head/topics/prod/notifications.events.v1.yaml new file mode 100644 index 0000000..4c9ae8d --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/head/topics/prod/notifications.events.v1.yaml @@ -0,0 +1,20 @@ +notifications.events.v1: + owner: growth-team + partitions: 8 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: notifications.events.v1-value + compatibility: BACKWARD + references: + notifications.common: 1 + compaction: + strategy: delete + description: > + Notification dispatch events consumed by downstream channels. + consumers: + email-service: reader + sms-service: reader + push-service: reader diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/base/topics/prod/notifications.events.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/base/topics/prod/notifications.events.v1.yaml new file mode 100644 index 0000000..c60de97 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/base/topics/prod/notifications.events.v1.yaml @@ -0,0 +1,19 @@ +notifications.events.v1: + owner: growth-team + partitions: 8 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: notifications.events.v1-value + compatibility: BACKWARD + references: + notifications.common: 1 + compaction: + strategy: delete + description: > + Notification dispatch events consumed by downstream channels. + consumers: + email-service: reader + sms-service: reader diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/expect.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/expect.yaml new file mode 100644 index 0000000..1fe5ee1 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/expect.yaml @@ -0,0 +1,6 @@ +# consumers key DELETE (sms-service removed) -> consumer-removal fails -> REVIEW. +decision: REVIEW +findings: + - rule: consumer-removal + obligation: non-destructive + effect: require-review diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/facts.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/facts.yaml new file mode 100644 index 0000000..9f7007b --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [growth-team] +quota: + max_partitions: 24 +schema: + valid: true diff --git a/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/head/topics/prod/notifications.events.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/head/topics/prod/notifications.events.v1.yaml new file mode 100644 index 0000000..cc7dd76 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/list-no-shrink/negative/head/topics/prod/notifications.events.v1.yaml @@ -0,0 +1,18 @@ +notifications.events.v1: + owner: growth-team + partitions: 8 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: notifications.events.v1-value + compatibility: BACKWARD + references: + notifications.common: 1 + compaction: + strategy: delete + description: > + Notification dispatch events consumed by downstream channels. + consumers: + email-service: reader diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/base/topics/prod/inventory.updates.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/base/topics/prod/inventory.updates.v1.yaml new file mode 100644 index 0000000..e681d0a --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/base/topics/prod/inventory.updates.v1.yaml @@ -0,0 +1,16 @@ +inventory.updates.v1: + owner: inventory-team + partitions: 4 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: inventory.updates.v1-value + compatibility: BACKWARD + references: + inventory.common: 1 + compaction: + strategy: delete + description: > + Inventory level change events for the warehouse domain. diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/expect.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/expect.yaml new file mode 100644 index 0000000..167cf2a --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/expect.yaml @@ -0,0 +1,3 @@ +# status ADD (base lacked it) as "active" (not "retired") -> status-retired-add proves silently. +decision: APPROVE +findings: [] diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/facts.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/facts.yaml new file mode 100644 index 0000000..21c3fc5 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [inventory-team] +quota: + max_partitions: 24 +schema: + valid: true diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/head/topics/prod/inventory.updates.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/head/topics/prod/inventory.updates.v1.yaml new file mode 100644 index 0000000..2a9d48f --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/head/topics/prod/inventory.updates.v1.yaml @@ -0,0 +1,17 @@ +inventory.updates.v1: + owner: inventory-team + partitions: 4 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: inventory.updates.v1-value + compatibility: BACKWARD + references: + inventory.common: 1 + compaction: + strategy: delete + description: > + Inventory level change events for the warehouse domain. + status: active diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/base/topics/prod/inventory.updates.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/base/topics/prod/inventory.updates.v1.yaml new file mode 100644 index 0000000..e681d0a --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/base/topics/prod/inventory.updates.v1.yaml @@ -0,0 +1,16 @@ +inventory.updates.v1: + owner: inventory-team + partitions: 4 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: inventory.updates.v1-value + compatibility: BACKWARD + references: + inventory.common: 1 + compaction: + strategy: delete + description: > + Inventory level change events for the warehouse domain. diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/expect.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/expect.yaml new file mode 100644 index 0000000..14ccb5e --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/expect.yaml @@ -0,0 +1,6 @@ +# status ADD as exactly "retired" (base lacked it) -> soft-delete-as-field-add, not silent APPROVE. +decision: REVIEW +findings: + - rule: status-retired-add + obligation: non-destructive + effect: require-review diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/facts.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/facts.yaml new file mode 100644 index 0000000..21c3fc5 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [inventory-team] +quota: + max_partitions: 24 +schema: + valid: true diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/head/topics/prod/inventory.updates.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/head/topics/prod/inventory.updates.v1.yaml new file mode 100644 index 0000000..d5862c7 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/negative/head/topics/prod/inventory.updates.v1.yaml @@ -0,0 +1,17 @@ +inventory.updates.v1: + owner: inventory-team + partitions: 4 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: inventory.updates.v1-value + compatibility: BACKWARD + references: + inventory.common: 1 + compaction: + strategy: delete + description: > + Inventory level change events for the warehouse domain. + status: retired diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/base/topics/prod/inventory.updates.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/base/topics/prod/inventory.updates.v1.yaml new file mode 100644 index 0000000..2a9d48f --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/base/topics/prod/inventory.updates.v1.yaml @@ -0,0 +1,17 @@ +inventory.updates.v1: + owner: inventory-team + partitions: 4 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: inventory.updates.v1-value + compatibility: BACKWARD + references: + inventory.common: 1 + compaction: + strategy: delete + description: > + Inventory level change events for the warehouse domain. + status: active diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/expect.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/expect.yaml new file mode 100644 index 0000000..6384283 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/expect.yaml @@ -0,0 +1,6 @@ +# Edge (REQ-EX-S06-04): retention_hours modify with status already present and +# UNCHANGED ("active") must not fire status-retired-add — the rule is scoped to +# the dedicated /*/status ADD pointer only, never a general field-add trigger. +decision: APPROVE +findings: [] +absent: [status-retired-add] diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/facts.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/facts.yaml new file mode 100644 index 0000000..21c3fc5 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [inventory-team] +quota: + max_partitions: 24 +schema: + valid: true diff --git a/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/head/topics/prod/inventory.updates.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/head/topics/prod/inventory.updates.v1.yaml new file mode 100644 index 0000000..6d0551b --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/soft-delete/unrelated-modify/head/topics/prod/inventory.updates.v1.yaml @@ -0,0 +1,17 @@ +inventory.updates.v1: + owner: inventory-team + partitions: 4 + replication_factor: 3 + retention_hours: 336 + cleanup_policy: delete + schema: + format: avro + subject: inventory.updates.v1-value + compatibility: BACKWARD + references: + inventory.common: 1 + compaction: + strategy: delete + description: > + Inventory level change events for the warehouse domain. + status: active diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/base/topics/prod/billing.invoices.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/base/topics/prod/billing.invoices.v1.yaml new file mode 100644 index 0000000..97f9a5d --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/base/topics/prod/billing.invoices.v1.yaml @@ -0,0 +1,19 @@ +billing.invoices.v1: + owner: billing-team + partitions: 6 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: billing.invoices.v1-value + compatibility: BACKWARD + references: + billing.common: 1 + compaction: + strategy: delete + description: > + Invoice lifecycle events for the billing domain. + acl: + grants: + billing-team: read diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/expect.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/expect.yaml new file mode 100644 index 0000000..2cef084 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/expect.yaml @@ -0,0 +1,3 @@ +# acl.grants ADD keyed by an explicit principal (finance-team) -> not "*" -> wildcard-grant-block proves silently. +decision: APPROVE +findings: [] diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/facts.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/facts.yaml new file mode 100644 index 0000000..cd3976b --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [billing-team] +quota: + max_partitions: 24 +schema: + valid: true diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/head/topics/prod/billing.invoices.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/head/topics/prod/billing.invoices.v1.yaml new file mode 100644 index 0000000..cf89498 --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/head/topics/prod/billing.invoices.v1.yaml @@ -0,0 +1,20 @@ +billing.invoices.v1: + owner: billing-team + partitions: 6 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: billing.invoices.v1-value + compatibility: BACKWARD + references: + billing.common: 1 + compaction: + strategy: delete + description: > + Invoice lifecycle events for the billing domain. + acl: + grants: + billing-team: read + finance-team: read diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/base/topics/prod/billing.invoices.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/base/topics/prod/billing.invoices.v1.yaml new file mode 100644 index 0000000..97f9a5d --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/base/topics/prod/billing.invoices.v1.yaml @@ -0,0 +1,19 @@ +billing.invoices.v1: + owner: billing-team + partitions: 6 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: billing.invoices.v1-value + compatibility: BACKWARD + references: + billing.common: 1 + compaction: + strategy: delete + description: > + Invoice lifecycle events for the billing domain. + acl: + grants: + billing-team: read diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/expect.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/expect.yaml new file mode 100644 index 0000000..c8ac23b --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/expect.yaml @@ -0,0 +1,6 @@ +# acl.grants ADD keyed by the literal wildcard principal "*" -> BLOCK. +decision: BLOCK +findings: + - rule: wildcard-grant-block + obligation: wildcard-grant + effect: block diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/facts.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/facts.yaml new file mode 100644 index 0000000..cd3976b --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/facts.yaml @@ -0,0 +1,7 @@ +author: + login: alice + groups: [billing-team] +quota: + max_partitions: 24 +schema: + valid: true diff --git a/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/head/topics/prod/billing.invoices.v1.yaml b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/head/topics/prod/billing.invoices.v1.yaml new file mode 100644 index 0000000..39f9d1e --- /dev/null +++ b/examples/packs/topic-registry/.assent/tests/topics/wildcard-grant/negative/head/topics/prod/billing.invoices.v1.yaml @@ -0,0 +1,20 @@ +billing.invoices.v1: + owner: billing-team + partitions: 6 + replication_factor: 3 + retention_hours: 168 + cleanup_policy: delete + schema: + format: avro + subject: billing.invoices.v1-value + compatibility: BACKWARD + references: + billing.common: 1 + compaction: + strategy: delete + description: > + Invoice lifecycle events for the billing domain. + acl: + grants: + billing-team: read + "*": read