// Formal predicates encoded by this suite (see specs/intent-attribution-compliance/README.md):
//
// F8_FailClosedIgnoresRuleConfig(a):
// Resolve(a).status ∈ {ambiguous, unlinked} ⇒
// ∀ rules R. Compile(Resolve(a), R) = safest_policy
// (fail-closed short-circuits BEFORE any rule matching is attempted, even when a
// permissive wildcard/no-condition rule is present that would otherwise match).
//
// F9_ExplicitLabelsDoNotOverrideMappedSource(a):
// Explicit(a) ≠ null ⇒ Resolve(a).source = explicit_metadata
// even when |Labels(a)| > 0 and the labels would independently resolve via
// FromArtifactLabels — explicit intent precedence is absolute, not merely
// "first non-empty signal wins".
//
// These are additive to the existing pkg/intent/compliance_fixtures_formal_test.go
// suite; they exercise the fail-closed short-circuit against a permissive rule set
// (rather than an empty one) and confirm label-signal presence does not leak into
// mapped-status resolution.
package intent_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/github/gh-aw/pkg/intent"
)
// permissiveWildcardRule matches every record unconditionally and grants the most
// relaxed policy in every ranked dimension. If fail-closed short-circuiting were
// broken, this rule would leak a relaxed policy into ambiguous/unlinked records.
func permissiveWildcardRule() intent.PolicyRule {
autoMerge := true
return intent.PolicyRule{
ID: "permissive-wildcard",
// Zero-value PolicyCondition matches everything (all fields empty).
When: intent.PolicyCondition{},
Set: intent.ExecutionPolicy{
Autonomy: "bounded",
WriteScope: "any_branch",
HumanApprovalRequired: false,
AutoMergeAllowed: &autoMerge,
MaxAttempts: 5,
},
}
}
func TestFormalFixture_AmbiguousIgnoresPermissiveRuleConfig(t *testing.T) {
fixture := loadIntentComplianceFixture(t, "ambiguous-root-closing-issues.yaml")
rec := matchingResolver().ResolvePullRequest(buildFixturePullRequest(fixture))
compiler := intent.PolicyCompiler{Rules: []intent.PolicyRule{permissiveWildcardRule()}}
policy := compiler.Compile(rec, intent.RepositoryContext{})
assert.Equal(t, "propose_only", policy.Autonomy, "ambiguous status must fail closed even against a matching permissive rule")
assert.Equal(t, "none", policy.WriteScope, "ambiguous status must fail closed on write scope even against a matching permissive rule")
assert.True(t, policy.HumanApprovalRequired, "ambiguous status must require approval even against a matching permissive rule")
require.NotNil(t, policy.AutoMergeAllowed)
assert.False(t, *policy.AutoMergeAllowed, "ambiguous status must deny auto-merge even against a matching permissive rule")
assert.Equal(t, 1, policy.MaxAttempts, "ambiguous status must cap attempts at 1 even against a matching permissive rule")
}
func TestFormalFixture_UnlinkedIgnoresPermissiveRuleConfig(t *testing.T) {
fixture := loadIntentComplianceFixture(t, "unlinked-pr-fail-closed.yaml")
rec := matchingResolver().ResolvePullRequest(buildFixturePullRequest(fixture))
compiler := intent.PolicyCompiler{Rules: []intent.PolicyRule{permissiveWildcardRule()}}
policy := compiler.Compile(rec, intent.RepositoryContext{})
assert.Equal(t, "propose_only", policy.Autonomy, "unlinked status must fail closed even against a matching permissive rule")
assert.Equal(t, "none", policy.WriteScope, "unlinked status must fail closed on write scope even against a matching permissive rule")
assert.True(t, policy.HumanApprovalRequired, "unlinked status must require approval even against a matching permissive rule")
require.NotNil(t, policy.AutoMergeAllowed)
assert.False(t, *policy.AutoMergeAllowed, "unlinked status must deny auto-merge even against a matching permissive rule")
assert.Equal(t, 1, policy.MaxAttempts, "unlinked status must cap attempts at 1 even against a matching permissive rule")
}
func TestFormalFixture_ExplicitIntentWinsEvenWithMatchingLabels(t *testing.T) {
fixture := loadIntentComplianceFixture(t, "explicit-intent-wins.yaml")
pr := buildFixturePullRequest(fixture)
// Attach labels that, in isolation (no explicit intent, no closing issues),
// would independently resolve via FromArtifactLabels. Explicit intent must
// still win — precedence is absolute, not merely "labels are a weaker signal".
pr.Labels = append(pr.Labels, "bug", "priority-high")
rec := matchingResolver().ResolvePullRequest(pr)
assert.Equal(t, string(intent.SourceExplicitMetadata), string(rec.Source), "explicit intent must remain the sole attribution source even when independently-resolvable labels are present")
assert.Equal(t, string(intent.AttributionMapped), string(rec.Status), "status must remain mapped when explicit intent is present regardless of label signals")
}
func TestFormalFixture_NoRulesMatchYieldsSafestPolicyForMappedStatus(t *testing.T) {
fixture := loadIntentComplianceFixture(t, "explicit-intent-wins.yaml")
rec := matchingResolver().ResolvePullRequest(buildFixturePullRequest(fixture))
// Empty rule set: even though status is mapped (not fail-closed by status),
// the compiler's "no match" branch must still return the safest default,
// since no rule granted a relaxed policy.
compiler := intent.PolicyCompiler{}
policy := compiler.Compile(rec, intent.RepositoryContext{})
assert.Equal(t, "propose_only", policy.Autonomy, "mapped status with zero matching rules must fall back to safest default autonomy")
assert.Equal(t, "none", policy.WriteScope, "mapped status with zero matching rules must fall back to safest default write scope")
assert.True(t, policy.HumanApprovalRequired, "mapped status with zero matching rules must fall back to safest default approval requirement")
}
Summary
specs/intent-attribution-compliance/README.mddefines the three normative compliance fixtures (explicit-intent-wins, ambiguous-root-closing-issues, unlinked-pr-fail-closed) for the Intent Attribution & Agent Governance spec, and formally specifies the guard-orderedResolve(a)attribution function plus fail-closed policy compilation. An existing formal test suite (pkg/intent/compliance_fixtures_formal_test.go) already covers predicates F1–F7. This run extends the formalization with two additional predicates (F8, F9) that were implicit in the spec's prose but not previously encoded as standalone tests: fail-closed short-circuiting must ignore even a matching permissive rule, and explicit-intent precedence must hold even when competing label signals are independently resolvable.Specification
specs/intent-attribution-compliance/README.mdpkg/intent.Resolver,pkg/intent.PolicyCompiler)Resolvefunction) / Z3-style guard conjunction (predicate implications)Formal Model
Predicates and invariants (illustrative notation)
Behavioral Coverage Map
F1_ExplicitIntentWinsTestFormalFixture_ExplicitIntentWinsOverLinkedIssues(existing)F2_AmbiguousRootStatusTestFormalFixture_AmbiguousRootIssueSet(existing)F3_UnlinkedFailsClosedTestFormalFixture_UnlinkedPullRequestFailsClosed(existing)F4_SafestPolicyOnIndeterminateTestFormalFixture_AmbiguousResolvesToSafestPolicy/TestFormalFixture_UnlinkedResolvesToSafestPolicy(existing)F8_FailClosedIgnoresRuleConfig(ambiguous)TestFormalFixture_AmbiguousIgnoresPermissiveRuleConfigF8_FailClosedIgnoresRuleConfig(unlinked)TestFormalFixture_UnlinkedIgnoresPermissiveRuleConfigF9_ExplicitLabelsDoNotOverrideMappedSourceTestFormalFixture_ExplicitIntentWinsEvenWithMatchingLabelsbug,priority-high) are attachedF5_MappedStatusPermitsRelaxedPolicy(converse, empty-rule edge)TestFormalFixture_NoRulesMatchYieldsSafestPolicyForMappedStatusGenerated Test Suite
📄 `pkg/intent/intent_compliance_extended_formal_test.go`
Usage
pkg/intent/intent_compliance_extended_formal_test.go.pkg/intent.Resolverandpkg/intent.PolicyCompiler, plus fixture helpers already defined inpkg/intent/compliance_fixtures_formal_test.go(matchingResolver,loadIntentComplianceFixture,buildFixturePullRequest).go test ./pkg/intent/... -run FormalFixtureContext
specs/intent-attribution-compliance/README.md