Skip to content

Unify safe-output outcome status enums - #54846

Merged
pelikhan merged 5 commits into
mainfrom
copilot/fix-9919-1036865607-c4e562cf-0d2e-4448-8b72-d747995ae1c5
Aug 22, 2026
Merged

Unify safe-output outcome status enums#54846
pelikhan merged 5 commits into
mainfrom
copilot/fix-9919-1036865607-c4e562cf-0d2e-4448-8b72-d747995ae1c5

Conversation

Copilot AI commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

pkg/cli had two parallel outcome classification enums (OutcomeResult and OutcomeStatus) and OutcomeReport carried both, making safe-output evaluation ambiguous. This consolidates classification onto one status field.

  • Unified outcome model

    • Removed OutcomeResult and OutcomeReport.Result.
    • Added OutcomeStatusError to the existing OutcomeStatus enum.
    • Kept OutcomeStatusSkipped in the unified enum.
  • Updated evaluation flow

    • Migrated outcome evaluators, summary aggregation, domain breakdowns, and CLI rendering to use OutcomeReport.OutcomeStatus.
    • Summary error/lifecycle counts now use normalized OutcomeEvaluation.
  • Cleaned serialized output

    • OutcomeReport JSON now exposes a single classification field: outcome_status.
    • Outcome JSONL output no longer duplicates classification as both result and outcome_status.
type OutcomeReport struct {
    OutcomeEvaluation
    Type      string `json:"type" console:"header:Type"`
    ObjectURL string `json:"object_url,omitempty" console:"header:URL,omitempty"`
    // ...
}

type OutcomeEvaluation struct {
    OutcomeStatus    OutcomeStatus    `json:"outcome_status" console:"header:Outcome"`
    EvidenceStrength EvidenceStrength `json:"evidence_strength"`
    Signal           string           `json:"signal,omitempty"`
}
  • Test coverage
    • Updated outcome tests to assert the unified status model.
    • Added regression coverage that OutcomeReport and outcome JSONL do not emit a separate result classification.

Copilot AI and others added 2 commits August 22, 2026 15:43
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Copilot Request Unify safe-output outcome status enums Aug 22, 2026
Copilot AI requested a review from pelikhan August 22, 2026 15:58
@pelikhan
pelikhan marked this pull request as ready for review August 22, 2026 15:58
Copilot AI balanced review requested due to automatic review settings August 22, 2026 15:58
@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Lean already. Ship. This PR mechanically consolidates two duplicate outcome-classification enums (OutcomeResult and OutcomeStatus) into one; it removes duplication rather than adding it, and introduces no new abstractions, dependencies, or speculative flexibility. Nothing to cut.

Generated by Ponytail Reviewer for #54846

@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Consolidates safe-output classification onto OutcomeStatus, resolving #54528.

Changes:

  • Removes OutcomeResult and OutcomeReport.Result.
  • Migrates evaluators, summaries, rendering, and domain metrics.
  • Updates serialization and regression tests.
Show a summary per file
File Description
pkg/cli/outcomes_command.go Uses unified status in CLI output.
pkg/cli/outcome_evaluation.go Adds error status and normalization.
pkg/cli/outcome_eval.go Removes duplicate result model.
pkg/cli/outcome_eval_workflow.go Migrates workflow evaluators.
pkg/cli/outcome_eval_workflow_test.go Updates workflow assertions.
pkg/cli/outcome_eval_update.go Migrates update evaluators.
pkg/cli/outcome_eval_update_test.go Removes duplicate assertions.
pkg/cli/outcome_eval_test.go Tests unified serialization and summaries.
pkg/cli/outcome_eval_review.go Migrates review evaluators.
pkg/cli/outcome_eval_pr.go Migrates pull-request evaluation.
pkg/cli/outcome_eval_label.go Migrates label evaluation.
pkg/cli/outcome_eval_jsonl.go Removes JSONL result field.
pkg/cli/outcome_eval_issue.go Migrates issue evaluation.
pkg/cli/outcome_eval_generic.go Migrates generic evaluators.
pkg/cli/outcome_eval_formal_test.go Updates formal status invariants.
pkg/cli/outcome_eval_comment.go Migrates comment evaluation.
pkg/cli/outcome_eval_agent.go Migrates agent evaluation.
pkg/cli/outcome_domain_breakdown.go Uses unified status for aggregation.
pkg/cli/outcome_domain_breakdown_test.go Updates domain test fixtures.

Review details

  • Files reviewed: 19/19 changed files
  • Comments generated: 2
  • Review effort level: Balanced

"run_id": runID,
"type": r.Type,
"result": r.Result,
"outcome_status": eval.OutcomeStatus,
Comment thread pkg/cli/outcome_eval.go

// OutcomeReport is the result of evaluating one safe output item.
type OutcomeReport struct {
OutcomeEvaluation
@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-08-22T00:00:00Z
review_event: REQUEST_CHANGES
top_themes:
  - stale public docs still describe removed OutcomeResult API
files_reviewed:
  - pkg/cli/outcome_domain_breakdown.go
  - pkg/cli/outcome_eval.go
  - pkg/cli/outcome_eval_agent.go
  - pkg/cli/outcome_eval_comment.go
  - pkg/cli/outcome_eval_formal_test.go
  - pkg/cli/outcome_eval_generic.go
  - pkg/cli/outcome_eval_issue.go
  - pkg/cli/outcome_eval_jsonl.go
  - pkg/cli/outcome_eval_label.go
  - pkg/cli/outcome_eval_pr.go
  - pkg/cli/outcome_eval_review.go
  - pkg/cli/outcome_eval_test.go
  - pkg/cli/outcome_eval_update.go
  - pkg/cli/outcome_eval_update_test.go
  - pkg/cli/outcome_eval_workflow.go
  - pkg/cli/outcome_eval_workflow_test.go
  - pkg/cli/outcome_evaluation.go
  - pkg/cli/outcomes_command.go
comment_count: 1

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · gpt54 · 17.2 AIC · ⌖ 6.94 AIC · ⊞ 4.6K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes

This refactor mostly looks mechanical, but it leaves the public CLI reference out of sync with the code: pkg/cli/README.md still documents the removed OutcomeResult API and old serialized result field.

Blocking theme
  • The implementation now exposes only OutcomeStatus / outcome_status.
  • The checked-in docs still advertise OutcomeResult, OutcomeAccepted, OutcomeRejected, and related legacy constants.
  • That makes the published reference materially wrong for anyone integrating against this output model.

Please update/regenerate the README in the same change so the documented API matches the shipped one.

🔎 Code quality review by PR Code Quality Reviewer · gpt54 · 17.2 AIC · ⌖ 6.94 AIC · ⊞ 4.6K
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /codebase-design — requesting changes for two issues found.

📋 Key Themes & Highlights

Key Themes

  • Enum coverage gap: validResults in TestFormalOTelGracefulDegradation omits OutcomeStatusSkipped, diverging from the canonical domain set in TestFormalOutcomeDomainInvariant. A workflow that produces a skipped report would incorrectly fail the P11 invariant assertion.
  • Stale log key: result=%s in normalizeOutcomeEvaluation refers to the now-removed OutcomeResult field; the key should be status=%s for accurate debug output.

Positive Highlights

  • ✅ Clean removal of OutcomeResult and the dual-field ambiguity — the unified OutcomeStatus enum is much easier to reason about.
  • ✅ Strong formal test coverage (TLA+ predicates, domain invariant) updated throughout.
  • ✅ The normalizeOutcomeEvaluation change to return OutcomeStatusError instead of OutcomeStatusUnknown for error paths is a correctness improvement — callers can now distinguish evaluation errors from genuinely unknown outcomes.
  • ✅ JSON serialization cleanup (result field removed) prevents downstream consumers from relying on the now-ambiguous duplicate field.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 62.4 AIC · ⌖ 10 AIC · ⊞ 7.6K
Comment /matt to run again

OutcomePending: true, OutcomeLifecycle: true, OutcomeLifecycleClose: true, OutcomeUnknown: true, OutcomeError: true,
validResults := map[OutcomeStatus]bool{
OutcomeStatusAccepted: true, OutcomeStatusRejected: true, OutcomeStatusIgnored: true,
OutcomeStatusPending: true, OutcomeStatusLifecycle: true, OutcomeStatusLifecycleClose: true, OutcomeStatusUnknown: true, OutcomeStatusError: true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/codebase-design] validResults is missing OutcomeStatusSkipped, creating a gap between this invariant check and the domain-invariant test at line 58 that includes it. A report with OutcomeStatusSkipped would fail the P11 assertion here.

💡 Suggested fix
validResults := map[OutcomeStatus]bool{
    OutcomeStatusAccepted: true, OutcomeStatusRejected: true, OutcomeStatusIgnored: true,
    OutcomeStatusPending: true, OutcomeStatusLifecycle: true, OutcomeStatusLifecycleClose: true,
    OutcomeStatusUnknown: true, OutcomeStatusError: true, OutcomeStatusSkipped: true,
}

The TestFormalOutcomeDomainInvariant test (line 58) now includes OutcomeStatusSkipped in the canonical set; validResults here should match.

@copilot please address this.

}

outcomeEvaluationLog.Printf("Normalizing outcome from heuristics: type=%s, result=%s, detail=%q", report.Type, report.Result, report.Detail)
outcomeEvaluationLog.Printf("Normalizing outcome from heuristics: type=%s, result=%s, detail=%q", report.Type, report.OutcomeStatus, report.Detail)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/codebase-design] The log format key result=%s is a stale reference to the removed OutcomeResult type. It should be status=%s to match the renamed field and avoid confusion when reading debug logs.

💡 Suggested fix
outcomeEvaluationLog.Printf("Normalizing outcome from heuristics: type=%s, status=%s, detail=%q", report.Type, report.OutcomeStatus, report.Detail)

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (322 new lines in pkg/cli) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/54846-unify-outcome-status-enum.md — review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI couldn't infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-54846: Unify Outcome Classification Onto a Single OutcomeStatus Enum

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 0042-use-postgresql.md for PR #42).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 131.3 AIC · ⌖ 29 AIC · ⊞ 9.5K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Unify safe-output outcome status enums

Good consolidation — removing the parallel OutcomeResult enum and collapsing everything onto OutcomeStatus+OutcomeEvaluation eliminates a long-standing ambiguity about which field represented the authoritative outcome. The migration is consistent across all evaluators, and the tests are properly updated.

Non-blocking issues found

Two log format-string verb labels were not updated to match the new field name. Both files still use result=%s while passing report.OutcomeStatus as the argument:

  • pkg/cli/outcome_eval_label.go:177"Label evaluation result: result=%s, label_count=%d"
  • pkg/cli/outcome_evaluation.go:48"Normalizing outcome from heuristics: type=%s, result=%s, detail=%q"

The value is correct but the key name is misleading and inconsistent. Inline comments added on both lines.

Everything else looks good

  • OutcomeStatusError correctly added to the enum and handled in normalizeOutcomeEvaluation.
  • normalizeOutcomeEvaluation early-exit guard (OutcomeStatus != "" && EvidenceStrength != "") is correct — a pre-filled OutcomeEvaluation short-circuits heuristics as intended.
  • Redundant report.OutcomeStatus = ... assignments before explicit report.OutcomeEvaluation = OutcomeEvaluation{...} blocks are intentional (keeping the field in sync) and not harmful.
  • JSONL writer correctly dropped the old result field.
  • Formal invariant test updated to include OutcomeStatusSkipped and OutcomeStatusError.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 90 AIC · ⌖ 9.22 AIC · ⊞ 6.2K

}

outcomeEvalLabelLog.Printf("Label evaluation result: result=%s, label_count=%d", report.Result, len(labels))
outcomeEvalLabelLog.Printf("Label evaluation result: result=%s, label_count=%d", report.OutcomeStatus, len(labels))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format-string verb label result= is stale after the OutcomeResultOutcomeStatus rename. The argument is now report.OutcomeStatus, so the key should be outcome_status= to match:

outcomeEvalLabelLog.Printf("Label evaluation result: outcome_status=%s, label_count=%d", report.OutcomeStatus, len(labels))

Leaving it as result= means log consumers / grep patterns looking for outcome_status= won't find this entry. @copilot please address this.

}

outcomeEvaluationLog.Printf("Normalizing outcome from heuristics: type=%s, result=%s, detail=%q", report.Type, report.Result, report.Detail)
outcomeEvaluationLog.Printf("Normalizing outcome from heuristics: type=%s, result=%s, detail=%q", report.Type, report.OutcomeStatus, report.Detail)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format-string verb label result= is stale after the OutcomeResultOutcomeStatus rename. The argument is now report.OutcomeStatus, so the key should be outcome_status=:

outcomeEvaluationLog.Printf("Normalizing outcome from heuristics: type=%s, outcome_status=%s, detail=%q", report.Type, report.OutcomeStatus, report.Detail)

Leaving it as result= is misleading in logs and inconsistent with the unified naming. @copilot please address this.

@pelikhan
pelikhan merged commit d0d3b9f into main Aug 22, 2026
32 checks passed
@pelikhan
pelikhan deleted the copilot/fix-9919-1036865607-c4e562cf-0d2e-4448-8b72-d747995ae1c5 branch August 22, 2026 20:49
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.87.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[deep-report] Merge duplicate OutcomeResult/OutcomeStatus enums in pkg/cli

3 participants