Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Introduce deterministic DecisionOperator at plan boundary

Replaced the hardcoded validation errors for architecture facts with a deterministic `PlanDecisionOperator` primitive (`Infer`, `Query`, `Verify`, `Reject`, `Escalate`). The `validateArchitectureGrounding` boundary now evaluates repository evidence and human intent against a strict policy matrix before routing the decision. This optimizes the query architecture to only block or query when evidence is insufficient, making the supervisor's decisions formal, recordable, and explainable.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package boatstack

type DecisionOperator string

const (
OperatorInfer DecisionOperator = "infer"
OperatorQuery DecisionOperator = "query"
OperatorVerify DecisionOperator = "verify"
OperatorReject DecisionOperator = "reject"
OperatorEscalate DecisionOperator = "escalate"
)

type EvidenceLevel string

const (
EvidenceVerified EvidenceLevel = "verified"
EvidenceSupported EvidenceLevel = "supported"
EvidenceAbsent EvidenceLevel = "absent"
EvidenceConflicting EvidenceLevel = "conflicting"
)

type PremiseStatus string

const (
PremiseUnknown PremiseStatus = "unknown"
PremiseValid PremiseStatus = "valid"
PremiseInvalid PremiseStatus = "invalid"
)

type PlanDecisionInput struct {
DecisionKind string
IsMaterial bool
RepositoryEvidence []EvidenceRecord
EvidenceLevel EvidenceLevel
PremiseStatus PremiseStatus
}

type DecisionResolution struct {
Operator DecisionOperator
RuleID string
Reason string
Evidence []EvidenceRecord
EvidenceLevel EvidenceLevel
PremiseStatus PremiseStatus
}

func ResolvePlanDecision(input PlanDecisionInput) DecisionResolution {
resolution := DecisionResolution{
Evidence: input.RepositoryEvidence,
EvidenceLevel: input.EvidenceLevel,
PremiseStatus: input.PremiseStatus,
}

if input.PremiseStatus == PremiseInvalid {
resolution.Operator = OperatorReject
resolution.RuleID = "invalid-premise-rejected"
resolution.Reason = "planning premise is not supported"
return resolution
}

if input.EvidenceLevel == EvidenceConflicting {
resolution.Operator = OperatorEscalate
resolution.RuleID = "conflicting-evidence-escalated"
resolution.Reason = "repository evidence conflicts"
return resolution
}

if input.EvidenceLevel == EvidenceVerified {
// Assuming verified evidence is sufficient to resolve the decision
resolution.Operator = OperatorInfer
resolution.RuleID = "verified-evidence-inferred"
resolution.Reason = "verified repository evidence resolves the decision"
return resolution
}

if input.EvidenceLevel == EvidenceSupported {
resolution.Operator = OperatorVerify
resolution.RuleID = "supported-evidence-requires-verification"
resolution.Reason = "evidence is supported but requires independent verification"
return resolution
}

if input.IsMaterial && input.EvidenceLevel == EvidenceAbsent {
resolution.Operator = OperatorQuery
resolution.RuleID = "material-intent-requires-human"
resolution.Reason = "material product intent requires human input"
return resolution
}

resolution.Operator = OperatorEscalate
resolution.RuleID = "unresolved-uncertainty-escalated"
resolution.Reason = "unresolved uncertainty requires escalation"
return resolution
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package boatstack

import "testing"

func TestResolvePlanDecision(t *testing.T) {
tests := []struct {
name string
input PlanDecisionInput
expected DecisionOperator
}{
{
name: "invalid premise rejects",
input: PlanDecisionInput{
PremiseStatus: PremiseInvalid,
},
expected: OperatorReject,
},
{
name: "conflicting evidence escalates",
input: PlanDecisionInput{
PremiseStatus: PremiseValid,
EvidenceLevel: EvidenceConflicting,
},
expected: OperatorEscalate,
},
{
name: "verified evidence infers",
input: PlanDecisionInput{
PremiseStatus: PremiseValid,
EvidenceLevel: EvidenceVerified,
},
expected: OperatorInfer,
},
{
name: "supported evidence verifies",
input: PlanDecisionInput{
PremiseStatus: PremiseValid,
EvidenceLevel: EvidenceSupported,
},
expected: OperatorVerify,
},
{
name: "absent evidence for material intent queries",
input: PlanDecisionInput{
PremiseStatus: PremiseValid,
IsMaterial: true,
EvidenceLevel: EvidenceAbsent,
},
expected: OperatorQuery,
},
{
name: "unknown state escalates",
input: PlanDecisionInput{
PremiseStatus: PremiseUnknown,
EvidenceLevel: EvidenceAbsent,
IsMaterial: false,
},
expected: OperatorEscalate,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resolution := ResolvePlanDecision(tt.input)
if resolution.Operator != tt.expected {
t.Errorf("expected operator %s, got %s", tt.expected, resolution.Operator)
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,56 @@ func validateArchitectureGrounding(plan map[string]any, opts *ValidatePlanOption
return fmt.Errorf("unsupported architecture fact kind: %s", kind)
}

var evidence []EvidenceRecord
evidenceLevel := EvidenceVerified
premiseStatus := PremiseValid
evidenceIDs, ok := stringSlice(fact["evidence_ids"])

if !ok || len(evidenceIDs) == 0 {
return fmt.Errorf("architecture fact %s requires at least one evidence ID", id)
}

// Validation of evidence against ledger
for _, evID := range evidenceIDs {
record, exists := ledger[evID]
if !exists {
return fmt.Errorf("architecture fact %s references unknown evidence ID: %s", id, evID)
}
if opts != nil && opts.RepoRevision != "" && record.RepositoryRevision != opts.RepoRevision {
return fmt.Errorf("evidence %s has stale repository revision: %s", evID, record.RepositoryRevision)
}

// Basic operation check
if kind == "route_absent" && record.Operation != "repository_search" && record.Operation != "route_lookup" {
return fmt.Errorf("architecture fact %s requires repository_search evidence", id)
}

if opts != nil && opts.RepoRoot != "" && record.Path != "" {
if err := ValidateEvidencePath(opts.RepoRoot, record.Path); err != nil {
return fmt.Errorf("invalid evidence path in %s: %w", evID, err)
evidenceLevel = EvidenceAbsent
} else {
// Validation of evidence against ledger
for _, evID := range evidenceIDs {
record, exists := ledger[evID]
if !exists {
evidenceLevel = EvidenceAbsent
break
}
if len(record.Anchors) > 0 {
targetPath := filepath.Join(opts.RepoRoot, filepath.Clean(record.Path))
if err := CheckFileAnchors(targetPath, record.Anchors); err != nil {
return fmt.Errorf("evidence %s anchor check failed: %w", evID, err)
evidence = append(evidence, record)
if opts != nil && opts.RepoRevision != "" && record.RepositoryRevision != opts.RepoRevision {
evidenceLevel = EvidenceSupported
}

// Basic operation check
if kind == "route_absent" && record.Operation != "repository_search" && record.Operation != "route_lookup" {
premiseStatus = PremiseInvalid
}

if opts != nil && opts.RepoRoot != "" && record.Path != "" {
if err := ValidateEvidencePath(opts.RepoRoot, record.Path); err != nil {
premiseStatus = PremiseInvalid
}
if len(record.Anchors) > 0 {
targetPath := filepath.Join(opts.RepoRoot, filepath.Clean(record.Path))
if err := CheckFileAnchors(targetPath, record.Anchors); err != nil {
premiseStatus = PremiseInvalid
}
}
}
}
}

resolution := ResolvePlanDecision(PlanDecisionInput{
DecisionKind: "architecture_fact",
IsMaterial: false,
RepositoryEvidence: evidence,
EvidenceLevel: evidenceLevel,
PremiseStatus: premiseStatus,
})

if resolution.Operator != OperatorInfer {
return fmt.Errorf("architecture fact %s requires %s (%s): %s", id, resolution.Operator, resolution.RuleID, resolution.Reason)
}
}

unknowns, _ := objectSlice(plan["architecture_unknowns"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func TestValidatePlanV2FactMissingEvidenceID(t *testing.T) {
opts := &ValidatePlanOptions{PlanPath: "plan.md", RepoRoot: ""}

err := validateArchitectureGrounding(plan, opts)
if err == nil || !strings.Contains(err.Error(), "references unknown evidence ID") {
t.Fatalf("expected missing evidence error, got: %v", err)
if err == nil || !strings.Contains(err.Error(), "requires escalate") || !strings.Contains(err.Error(), "unresolved uncertainty requires escalation") {
t.Fatalf("expected escalate error due to absent evidence, got: %v", err)
}
}

Expand Down