Files
Bo 3213afcf1c Default to native execution and report independently accepted work (#1129)
## Change

Make native coding-agent execution the default AgentOps entry path with
zero mandatory skills. Preserve full bundles and add repeatable `ao
skills link --skill NAME` selection, validating the entire selection
before writes. Align product, installation, architecture and generated
command documentation.

Extend the existing trial readout to separate endpoint test results,
execution state and independently accepted work. Bind supplied judgments
to exact content, acceptance and native evidence. Reject empty
implementation subjects and require the caller's complete criterion ID
set before reporting acceptance. Preserve genuine nonempty and
deletion-only subjects, valid failures and missing-proof outcomes.

## Validation

- Native onboarding from empty home/consumer directories produces no
setup files; selective/full linking and failure boundaries are covered.
- Actual RED/GREEN regressions cover empty subjects and the
partial-criterion omission found by independent review.
- Full Go build, vet and race/shuffle tests; affected Go lint; 88 Python
readout/statistics tests passed.
- All 73 gates, generated projections, strict documentation build and
local aggregate passed (10 passed; one documented optional absence).
- All nine PR checks succeeded at
`7df0d42b12f35ffc22008cc10a40339afcfbb6a0`.
- Fresh author-distinct review passed all six acceptance criteria over
all 59 changed paths, with no findings or unchecked scope, after
repairing the criterion-coverage finding.

## Evidence limits

The real native coding repair demonstrates usability, not comparative
skill uplift. The strict live-session machine replay remains NOT_PROVEN
where execution/identity observations are unavailable; the source review
PASS is retained separately. Existing cohort limits and the historical
aggregate-enforcement gap remain unwaived. No new comparative cohort,
scheduler, skill-corpus deletion, memory migration or global
installation is included.
2026-09-10 20:28:22 +00:00

173 lines
5.7 KiB
Go

package skilltrial
import (
"bytes"
"encoding/json"
"sort"
"github.com/boshu2/agentops/cli/internal/evidence"
"github.com/boshu2/agentops/cli/internal/parser"
)
// Work joins one caller-selected subject to an observed native author. Status
// reports supplied independent judgment, not a new semantic verdict. Endpoint
// reward and execution remain separate; no trial is selected from retries.
type Work struct {
Status string `json:"status"`
Execution string `json:"execution"`
Association string `json:"association"`
AuthorContextID string `json:"author_context_id"`
TrialDirectory string `json:"trial_directory"`
ManifestPath string `json:"manifest_path"`
IntentPath string `json:"intent_path"`
AuthorEvidence []Evidence `json:"author_evidence"`
Judgments *evidence.JudgmentResult `json:"judgments"`
Problems []string `json:"problems"`
}
// InspectWork reuses the provenance owner to inspect original verdicts, exact
// subject/acceptance and native reviewer evidence. An inspection error remains
// visible in the report instead of hiding unsuccessful or incomplete attempts.
func InspectWork(report *Report, options evidence.JudgmentOptions) *Work {
w := &Work{Status: "not_proven", Execution: "unknown", Association: "missing", AuthorContextID: options.AuthorContextID,
ManifestPath: options.Manifest, IntentPath: options.Intent, AuthorEvidence: []Evidence{}, Problems: []string{}}
w.associate(report)
result, err := evidence.VerifyJudgments(options)
w.Judgments = result
if len(options.RequiredCriteria) == 0 {
w.Problems = append(w.Problems, "required_criteria_missing")
}
if err != nil {
w.Problems = append(w.Problems, "judgment_verification: "+err.Error())
}
if len(w.Problems) == 0 && result != nil {
if result.Satisfied && w.Execution == "completed" {
w.Status = "accepted"
} else if verifiedFailure(result) {
w.Status = "failed"
}
}
if w.Execution != "completed" {
w.Problems = append(w.Problems, "author_execution_"+w.Execution)
}
sort.Strings(w.Problems)
return w
}
// Non-PASS is deliberately unsatisfied in VerifyJudgments. A FAIL whose only
// leg problem is verdict_not_pass still supplies an independently bound failure.
// Other missing or invalid legs remain unchanged in the original result.
func verifiedFailure(result *evidence.JudgmentResult) bool {
for _, leg := range result.Legs {
if leg.Verdict == "FAIL" && len(leg.Problems) == 1 && leg.Problems[0] == "verdict_not_pass" {
return true
}
}
return false
}
func (w *Work) associate(report *Report) {
var author *Session
for i := range report.Sessions {
if report.Sessions[i].ID == w.AuthorContextID {
author = &report.Sessions[i]
break
}
}
if author == nil || len(author.Copies) == 0 {
w.Problems = append(w.Problems, "author_session_missing")
return
}
w.Association = "standalone_session"
if len(author.Diagnostics) > 0 {
w.Problems = append(w.Problems, "author_session_diagnostics")
}
trials, hashes := map[string]bool{}, map[string]bool{}
for _, copy := range author.Copies {
w.AuthorEvidence = append(w.AuthorEvidence, copy.Evidence)
hashes[copy.Evidence.SHA256] = true
if copy.Trial != "" {
trials[copy.Trial] = true
}
if authorAccountingUnverified(copy.Accounting) {
w.Problems = append(w.Problems, "author_accounting_unverified")
}
}
if len(hashes) != 1 || len(trials) > 1 {
w.Association = "ambiguous"
w.Problems = append(w.Problems, "author_session_copies_or_trial_assignment_ambiguous")
return
}
w.Execution = authorExecution(author.Copies[0].Accounting)
for directory := range trials {
w.Association, w.TrialDirectory = "trial", directory
for _, job := range report.Jobs {
for _, trial := range job.Trials {
if trial.Directory == directory {
w.bindTrialExecution(trial)
return
}
}
}
w.Problems = append(w.Problems, "associated_trial_missing")
}
}
func authorAccountingUnverified(accounting *parser.CodexAccounting) bool {
if accounting == nil {
return true
}
for _, diagnostic := range accounting.Diagnostics {
// This existing parser diagnostic concerns aggregate usage only. Keep it
// in native accounting without invalidating independently observed author
// identity or execution. Unknown and parse diagnostics still fail closed.
if diagnostic.Message != "cumulative counters decreased; last native snapshot retained, aggregation requires review" {
return true
}
}
return false
}
func authorExecution(accounting *parser.CodexAccounting) string {
if accounting == nil {
return "unknown"
}
// These terminal markers are retained by accounting as uninterpreted event
// counts. Their order relative to its latest supported marker is unavailable;
// do not let an earlier task_complete stand in for proven completion.
for _, marker := range []string{"event_msg/turn_aborted", "event_msg/error"} {
if accounting.Uninterpreted[marker] > 0 {
return "unknown"
}
}
var payload struct {
Error json.RawMessage `json:"error"`
}
if json.Unmarshal(accounting.LatestTurnPayload, &payload) == nil && len(payload.Error) > 0 && !bytes.Equal(bytes.TrimSpace(payload.Error), []byte("null")) {
return "failed"
}
switch accounting.LatestTurnState {
case "task_complete":
return "completed"
case "task_started":
return "unfinished"
case "task_aborted":
return "failed"
default:
return "unknown"
}
}
func (w *Work) bindTrialExecution(trial Trial) {
switch trial.Outcome {
case "execution_error", "infrastructure_error":
w.Execution = "failed"
case "created", "unfinished":
w.Execution = "unfinished"
default:
if !trial.Completed {
w.Execution = "unknown"
}
}
}