(go/redacted):build !integration
// Package workflow_test – Intent Attribution & Agent Governance formal model tests.
//
// Source specification: specs/intent-attribution-agent-governance.md
//
// This file encodes formal predicates derived from the specification's
// deterministic-resolution, multiple-root, risk-classification, execution-policy,
// and policy-precedence sections as illustrative Z3/TLA+ style predicates, and maps
// each predicate to a Go testify test function.
//
// Formal predicates encoded (see issue body "Formal Model" section for full notation):
//
// P1_ExplicitIntentPrecedence — explicit workflow intent always wins first
// P2_SingleClosingIssueMapped — exactly one closing issue -> mapped/closing_issue
// P3_AmbiguousOnMultipleRoots — 2+ closing issues -> ambiguous, never arbitrary pick
// P4_ArtifactLabelFallback — zero closing issues + labels present -> artifact fallback
// P5_UnlinkedWhenNoSource — zero closing issues + no labels -> unlinked
// P6_AmbiguousNotMapped — ambiguous status MUST NOT be treated as mapped
// P7_FailClosedPolicy — unlinked/ambiguous/indeterminate -> safest policy
// P8_PolicyDeterminism — identical inputs -> identical policy output
// P9_RiskClassificationOrder — explicit risk wins; else derived by domain rules
// P10_PrecedenceOrdering — org > repo > intent > workflow > agent request
// P11_NoElevatedAuthorityOnAbsentAttribution — unresolved attribution never grants elevation
//
// Edge cases covered: zero closing issues with no labels (unlinked), exactly one
// closing issue (mapped), 2+ closing issues (ambiguous, order-independent),
// explicit risk overriding derived risk, and unknown domain combinations.
package workflow_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// ---------------------------------------------------------------------------
// stub — replace with real implementation
//
// Minimal reproduction of the resolver/policy types described in the spec's
// "Attribution states", "Deterministic resolution", "Risk classification",
// "Execution policy", and "Policy precedence" sections. Real implementation
// should live in an intent-attribution package once Phase 2+ lands.
// ---------------------------------------------------------------------------
type attributionStatus string
const (
attributionMapped attributionStatus = "mapped"
attributionUnmapped attributionStatus = "unmapped"
attributionUnlinked attributionStatus = "unlinked"
attributionAmbiguous attributionStatus = "ambiguous"
attributionSuggested attributionStatus = "suggested"
)
type attributionSource string
const (
sourceExplicitMetadata attributionSource = "explicit_metadata"
sourceClosingIssue attributionSource = "closing_issue"
sourceArtifactLabels attributionSource = "artifact_labels"
sourceNone attributionSource = "none"
)
type intentRecord struct {
Status attributionStatus
Source attributionSource
Reason string
Risk string
Domains []string
Priority string
}
type pullRequestData struct {
ExplicitIntent *intentRecord
ClosingIssues []int
Labels []string
NodeID string
URL string
}
// resolve implements the Deterministic Resolution algorithm from the spec.
func resolve(pr pullRequestData) intentRecord {
if pr.ExplicitIntent != nil {
rec := *pr.ExplicitIntent
rec.Status = attributionMapped
rec.Source = sourceExplicitMetadata
rec.Reason = "explicit_workflow_intent"
return rec
}
switch len(pr.ClosingIssues) {
case 1:
return intentRecord{
Status: attributionMapped,
Source: sourceClosingIssue,
Reason: "single_closing_issue",
}
case 0:
if len(pr.Labels) > 0 {
return intentRecord{
Status: attributionMapped,
Source: sourceArtifactLabels,
Reason: "pull_request_label_fallback",
}
}
return intentRecord{
Status: attributionUnlinked,
Source: sourceNone,
Reason: "no_supported_intent_source",
}
default:
return intentRecord{
Status: attributionAmbiguous,
Source: sourceClosingIssue,
Reason: "multiple_closing_issues",
}
}
}
// executionPolicy mirrors the spec's ExecutionPolicy struct.
type executionPolicy struct {
Autonomy string
WriteScope string
HumanApprovalRequired bool
AutoMergeAllowed bool
MaxAttempts int
}
// safestPolicy is the fail-closed default described in "Fail-Closed Behavior".
func safestPolicy() executionPolicy {
return executionPolicy{
Autonomy: "propose_only",
WriteScope: "none",
HumanApprovalRequired: true,
AutoMergeAllowed: false,
MaxAttempts: 1,
}
}
// derivePolicy implements the fail-closed rule: indeterminate attribution
// (unlinked, ambiguous, suggested) always yields the safest policy.
func derivePolicy(intent intentRecord, elevated executionPolicy) executionPolicy {
switch intent.Status {
case attributionUnlinked, attributionAmbiguous, attributionSuggested:
return safestPolicy()
case attributionUnmapped:
return safestPolicy()
default:
return elevated
}
}
// resolveRisk implements the "Risk classification" derivation rules.
func resolveRisk(intent intentRecord) string {
if intent.Risk != "" {
return intent.Risk
}
contains := func(list []string, v string) bool {
for _, x := range list {
if x == v {
return true
}
}
return false
}
if contains(intent.Domains, "security") && intent.Priority == "critical" {
return "high"
}
if contains(intent.Domains, "production") {
return "high"
}
if contains(intent.Domains, "infrastructure") {
return "medium"
}
if contains(intent.Domains, "documentation") {
return "low"
}
return "unknown"
}
// precedenceLevel maps a constraint source name to its ordinal precedence rank
// per "Policy precedence": organization(0) > repository(1) > intent(2) >
// workflow(3) > agent request(4). Lower rank == higher precedence.
func precedenceLevel(source string) int {
switch source {
case "organization":
return 0
case "repository":
return 1
case "intent":
return 2
case "workflow":
return 3
case "agent_request":
return 4
default:
return 99
}
}
// effectivePrecedence returns true if `higher` may not be weakened by `lower`.
func higherWins(higher, lower string) bool {
return precedenceLevel(higher) < precedenceLevel(lower)
}
// ---------------------------------------------------------------------------
// P1: Explicit intent precedence
// ---------------------------------------------------------------------------
func TestFormal_P1_ExplicitIntentPrecedence(t *testing.T) {
explicit := &intentRecord{Reason: "manual_override"}
pr := pullRequestData{
ExplicitIntent: explicit,
ClosingIssues: []int{1, 2, 3}, // would otherwise be ambiguous
Labels: []string{"bug"},
}
rec := resolve(pr)
assert.Equal(t, attributionMapped, rec.Status, "explicit intent MUST always resolve to mapped regardless of other candidates")
assert.Equal(t, sourceExplicitMetadata, rec.Source, "explicit intent MUST be sourced as explicit_metadata")
assert.Equal(t, "explicit_workflow_intent", rec.Reason, "reason MUST record explicit workflow intent precedence")
}
// ---------------------------------------------------------------------------
// P2: Single closing issue -> mapped
// ---------------------------------------------------------------------------
func TestFormal_P2_SingleClosingIssueMapped(t *testing.T) {
pr := pullRequestData{ClosingIssues: []int{42}}
rec := resolve(pr)
require.Equal(t, attributionMapped, rec.Status, "exactly one closing issue MUST resolve to mapped status")
assert.Equal(t, sourceClosingIssue, rec.Source, "single closing issue MUST be sourced as closing_issue")
assert.Equal(t, "single_closing_issue", rec.Reason, "reason MUST reflect the single_closing_issue path")
}
// ---------------------------------------------------------------------------
// P3: Ambiguous on multiple roots, order-independent
// ---------------------------------------------------------------------------
func TestFormal_P3_AmbiguousOnMultipleRoots(t *testing.T) {
cases := []struct {
name string
closingIssues []int
}{
{"two_issues_ascending", []int{1, 2}},
{"two_issues_descending", []int{2, 1}},
{"three_issues", []int{5, 3, 9}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
rec := resolve(pullRequestData{ClosingIssues: tc.closingIssues})
require.Equal(t, attributionAmbiguous, rec.Status, "2+ closing issues MUST always resolve to ambiguous, case=%s", tc.name)
assert.Equal(t, sourceClosingIssue, rec.Source, "ambiguous attribution MUST record source=closing_issue, case=%s", tc.name)
assert.Equal(t, "multiple_closing_issues", rec.Reason, "ambiguous reason MUST be multiple_closing_issues, case=%s", tc.name)
assert.NotEqual(t, tc.closingIssues[0], -1, "sanity: resolver MUST NOT silently pick first candidate arbitrarily")
})
}
}
// ---------------------------------------------------------------------------
// P4: Artifact label fallback when no closing issues but labels exist
// ---------------------------------------------------------------------------
func TestFormal_P4_ArtifactLabelFallback(t *testing.T) {
pr := pullRequestData{ClosingIssues: []int{}, Labels: []string{"feature"}}
rec := resolve(pr)
require.Equal(t, attributionMapped, rec.Status, "zero closing issues with labels present MUST fall back to artifact label mapping")
assert.Equal(t, sourceArtifactLabels, rec.Source, "fallback attribution MUST be sourced as artifact_labels")
assert.Equal(t, "pull_request_label_fallback", rec.Reason, "fallback reason MUST be pull_request_label_fallback")
}
// ---------------------------------------------------------------------------
// P5: Unlinked when no source available (edge case)
// ---------------------------------------------------------------------------
func TestFormal_P5_UnlinkedWhenNoSource(t *testing.T) {
pr := pullRequestData{ClosingIssues: []int{}, Labels: []string{}}
rec := resolve(pr)
require.Equal(t, attributionUnlinked, rec.Status, "zero closing issues and zero labels MUST resolve to unlinked")
assert.Equal(t, sourceNone, rec.Source, "unlinked attribution MUST be sourced as none")
assert.Equal(t, "no_supported_intent_source", rec.Reason, "unlinked reason MUST be no_supported_intent_source")
}
// ---------------------------------------------------------------------------
// P6: Ambiguous MUST NOT be treated as mapped
// ---------------------------------------------------------------------------
func TestFormal_P6_AmbiguousNotMapped(t *testing.T) {
rec := resolve(pullRequestData{ClosingIssues: []int{7, 8}})
require.Equal(t, attributionAmbiguous, rec.Status, "precondition: status must be ambiguous")
assert.NotEqual(t, attributionMapped, rec.Status, "ambiguous attribution MUST NOT be equivalent to mapped for reporting/authorization")
}
// ---------------------------------------------------------------------------
// P7: Fail-closed policy for indeterminate attribution
// ---------------------------------------------------------------------------
func TestFormal_P7_FailClosedPolicy(t *testing.T) {
elevated := executionPolicy{
Autonomy: "bounded",
WriteScope: "full",
HumanApprovalRequired: false,
AutoMergeAllowed: true,
MaxAttempts: 10,
}
cases := []struct {
name string
status attributionStatus
}{
{"unlinked", attributionUnlinked},
{"ambiguous", attributionAmbiguous},
{"suggested", attributionSuggested},
{"unmapped", attributionUnmapped},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
intent := intentRecord{Status: tc.status}
policy := derivePolicy(intent, elevated)
safe := safestPolicy()
assert.Equal(t, safe, policy, "indeterminate attribution (%s) MUST always yield the safest policy, never the elevated one", tc.name)
assert.Equal(t, "propose_only", policy.Autonomy, "fail-closed autonomy MUST be propose_only for %s", tc.name)
assert.False(t, policy.AutoMergeAllowed, "fail-closed policy MUST NOT allow auto-merge for %s", tc.name)
assert.True(t, policy.HumanApprovalRequired, "fail-closed policy MUST require human approval for %s", tc.name)
assert.Equal(t, 1, policy.MaxAttempts, "fail-closed policy MUST cap max_attempts at 1 for %s", tc.name)
})
}
}
// ---------------------------------------------------------------------------
// P8: Policy determinism — identical inputs -> identical outputs
// ---------------------------------------------------------------------------
func TestFormal_P8_PolicyDeterminism(t *testing.T) {
elevated := executionPolicy{Autonomy: "supervised", WriteScope: "branch"}
intent := intentRecord{Status: attributionMapped}
first := derivePolicy(intent, elevated)
second := derivePolicy(intent, elevated)
third := derivePolicy(intent, elevated)
assert.Equal(t, first, second, "identical attribution inputs MUST always produce the same policy (run 1 vs 2)")
assert.Equal(t, second, third, "identical attribution inputs MUST always produce the same policy (run 2 vs 3)")
}
// ---------------------------------------------------------------------------
// P9: Risk classification ordering (explicit wins, then derived rules)
// ---------------------------------------------------------------------------
func TestFormal_P9_RiskClassificationOrder(t *testing.T) {
cases := []struct {
name string
intent intentRecord
expected string
}{
{"explicit_risk_wins", intentRecord{Risk: "low", Domains: []string{"security"}, Priority: "critical"}, "low"},
{"security_critical_high", intentRecord{Domains: []string{"security"}, Priority: "critical"}, "high"},
{"production_high", intentRecord{Domains: []string{"production"}}, "high"},
{"infrastructure_medium", intentRecord{Domains: []string{"infrastructure"}}, "medium"},
{"documentation_low", intentRecord{Domains: []string{"documentation"}}, "low"},
{"unknown_domain_edge_case", intentRecord{Domains: []string{"marketing"}}, "unknown"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := resolveRisk(tc.intent)
assert.Equal(t, tc.expected, got, "risk classification mismatch for case=%s", tc.name)
})
}
}
// ---------------------------------------------------------------------------
// P10: Precedence ordering — org > repo > intent > workflow > agent request
// ---------------------------------------------------------------------------
func TestFormal_P10_PrecedenceOrdering(t *testing.T) {
order := []string{"organization", "repository", "intent", "workflow", "agent_request"}
for i := 0; i < len(order)-1; i++ {
higher, lower := order[i], order[i+1]
assert.True(t, higherWins(higher, lower), "%s MUST take precedence over %s", higher, lower)
assert.False(t, higherWins(lower, higher), "%s MUST NOT override %s (lower-precedence cannot weaken higher)", lower, higher)
}
// Non-adjacent check: organization must dominate agent_request directly.
assert.True(t, higherWins("organization", "agent_request"), "organization constraints MUST dominate agent_request even non-adjacently")
}
// ---------------------------------------------------------------------------
// P11: No elevated authority on absent/unresolved attribution
// ---------------------------------------------------------------------------
func TestFormal_P11_NoElevatedAuthorityOnAbsentAttribution(t *testing.T) {
elevated := executionPolicy{
Autonomy: "bounded",
AutoMergeAllowed: true,
MaxAttempts: 20,
}
unlinkedIntent := intentRecord{Status: attributionUnlinked}
policy := derivePolicy(unlinkedIntent, elevated)
assert.NotEqual(t, elevated.Autonomy, policy.Autonomy, "unresolved attribution MUST NOT grant the elevated autonomy level")
assert.False(t, policy.AutoMergeAllowed, "unresolved attribution MUST NOT grant auto-merge authority")
assert.NotEqual(t, elevated.MaxAttempts, policy.MaxAttempts, "unresolved attribution MUST NOT inherit elevated max_attempts")
}
// ---------------------------------------------------------------------------
// SAFETY: combined invariant — ambiguous status always fails closed regardless
// of an otherwise-elevated policy request (cross-cutting P6 + P7).
// ---------------------------------------------------------------------------
func TestFormal_SAFETY_AmbiguousAlwaysFailsClosed(t *testing.T) {
pr := pullRequestData{ClosingIssues: []int{100, 200, 300}}
rec := resolve(pr)
require.Equal(t, attributionAmbiguous, rec.Status, "precondition: multiple closing issues must resolve ambiguous")
elevated := executionPolicy{Autonomy: "bounded", AutoMergeAllowed: true, HumanApprovalRequired: false}
policy := derivePolicy(rec, elevated)
assert.Equal(t, safestPolicy(), policy, "safety invariant: ambiguous attribution MUST always fail closed to the safest policy, even under an elevated request")
}
Warning
threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.
Details
The threat detection engine failed to produce results.
Review the workflow run logs for details.
Summary
This issue formalizes
specs/intent-attribution-agent-governance.md, the specification defining a deterministic intent layer for gh-aw agentic workflows. The spec covers deterministic attribution resolution (explicit intent → closing issue → labels → unlinked), fail-closed governance policy for indeterminate attribution states, risk classification, and a strict policy-precedence hierarchy (organization > repository > intent > workflow > agent request). A Go testify formal test suite encodes 11 core predicates plus a cross-cutting safety invariant.Specification
specs/intent-attribution-agent-governance.mdFormal Model
Predicates and invariants (illustrative notation)
Behavioral Coverage Map
P1_ExplicitIntentPrecedenceTestFormal_P1_ExplicitIntentPrecedenceP2_SingleClosingIssueMappedTestFormal_P2_SingleClosingIssueMappedmapped/closing_issueP3_AmbiguousOnMultipleRootsTestFormal_P3_AmbiguousOnMultipleRootsambiguous, order-independent (table-driven, 3 orderings)P4_ArtifactLabelFallbackTestFormal_P4_ArtifactLabelFallbackP5_UnlinkedWhenNoSourceTestFormal_P5_UnlinkedWhenNoSourceunlinked(edge case)P6_AmbiguousNotMappedTestFormal_P6_AmbiguousNotMappedP7_FailClosedPolicyTestFormal_P7_FailClosedPolicyP8_PolicyDeterminismTestFormal_P8_PolicyDeterminismP9_RiskClassificationOrderTestFormal_P9_RiskClassificationOrderP10_PrecedenceOrderingTestFormal_P10_PrecedenceOrderingP11_NoElevatedAuthorityOnAbsentAttributionTestFormal_P11_NoElevatedAuthorityOnAbsentAttributionSAFETY_AmbiguousAlwaysFailsClosedTestFormal_SAFETY_AmbiguousAlwaysFailsClosedGenerated Test Suite
📄 `pkg/workflow/intent_attribution_governance_formal_test.go`
Usage
pkg/workflow/intent_attribution_governance_formal_test.go.stub — replace with real implementationtypes (intentRecord,pullRequestData,executionPolicy,resolve,derivePolicy,resolveRisk) with the real intent-attribution resolver once Phase 2+ ("honest attribution model") lands.go test ./pkg/workflow/... -run FormalContext
specs/intent-attribution-agent-governance.md