Add completion semantics for saturated/binary metrics

Author: MPIsaac-SynCreated May 12, 2026Updated May 12, 2026

Problem

Autoresearch currently works very well for open-ended optimization targets like runtime, memory, loss, bundle size, etc. But it can get stuck for bounded or acceptance-style objectives where the primary metric saturates.

We hit this in a real session:

  • Goal: make Moss chat demo cards render playable YouTube/video demos inline.
  • Primary metric: feature_score, higher is better.
  • 0 = the card is link-only.
  • 1 = the card renders a trusted playable video embed.

The loop successfully reached feature_score=1, all checks passed, and the user locally smoke-tested the feature successfully. But subsequent iterations could not improve the primary metric because it was already saturated. Under the current rule:

Use status keep if the PRIMARY metric improved. discard if worse or unchanged.

successful validation reruns were logged as discard. This created a discard-only trap: the objective was complete, but the loop kept requesting more iterations and semantically recorded successful confirmations as discarded experiments.

Why this matters

This is not specific to one project. It affects any bounded objective, for example:

  • binary feature acceptance: 0/1
  • test pass/fail score
  • error count reaches 0
  • coverage reaches a target
  • compliance/security checks pass
  • correctness score reaches 100%
  • bug reproduction eliminated

For these tasks, "unchanged" after reaching the target often means "done" or "confirmed", not "failed experiment".

Observed sequence

  1. Baseline: feature_score=0, logged/kept as baseline.
  2. Implementation: feature_score=1, checks pass, keep.
  3. Validation reruns: feature_score=1, checks pass.
  4. Because the metric was unchanged, each successful validation run had to be logged as discard.
  5. User noticed the system was in a discard-only trap.

Proposed direction

I think this is best treated as an issue/design gap first rather than a direct PR, because there are a few possible implementation choices.

1. Add optional completion criteria to init_experiment

For bounded objectives, allow the experiment to declare what "done" means:

typescript
completion?: {
  target_metric: number;
  comparison: "gte" | "lte" | "eq";
  patience_after_target?: number;
}

Example:

json
{
  "name": "Moss demo cards embedded playable videos",
  "metric_name": "feature_score",
  "direction": "higher",
  "completion": {
    "target_metric": 1,
    "comparison": "gte",
    "patience_after_target": 1
  }
}

When the target is reached and checks pass, the extension could pause auto-resume and tell the agent to finalize or reinitialize with a new target.

2. Add confirm and/or complete statuses

Current statuses force successful no-change confirmation runs into discard.

Possible expanded statuses:

typescript
"keep" | "discard" | "crash" | "checks_failed" | "confirm" | "complete"
  • confirm: no primary improvement, but validates the current best without treating it as a failed/discarded experiment.
  • complete: declared objective reached; stop auto-resume and prompt for finalize/reinitialize.

3. Add saturated-metric detection as a safety net

Even without explicit completion config, the extension could warn or pause when it sees something like:

  • metric values are only 0 and 1
  • direction is higher
  • best kept value is 1
  • N consecutive passing runs are unchanged
  • checks pass

Suggested message:

This metric appears saturated. Consider finalizing this autoresearch branch or reinitializing with a new primary metric.

I would not fully auto-stop on heuristics alone for all cases, but a warning/pause would prevent blind discard loops.

4. Qualify the "NEVER STOP" prompt rule

The current skill says to never stop. For bounded objectives, this should probably become something like:

Continue autonomously until interrupted or until declared completion criteria are satisfied.

5. Clarify Current best output

In the session, run_experiment printed:

Current best feature_score: 0

even after a kept run had reached feature_score=1. If that line is actually the baseline, it should be renamed to Baseline. If it is intended to be the best kept metric, it should compute from kept runs.

Desired behavior

For the example above, the ideal flow would be:

  1. Baseline: feature_score=0
  2. Implementation: feature_score=1, checks pass → keep
  3. Optional confirmation: feature_score=1, checks pass → confirm
  4. Extension detects target satisfied → pauses loop and says:

Objective complete: feature_score reached 1 and validation passed. Finalize this branch or reinitialize autoresearch with a new target.

This would avoid successful terminal states being recorded as discards.

Source: davebcn87/pi-autoresearch