Skip to content

Release 0.9.0: merge develop into main#467

Merged
SaJaToGu merged 118 commits into
mainfrom
develop
Jun 27, 2026
Merged

Release 0.9.0: merge develop into main#467
SaJaToGu merged 118 commits into
mainfrom
develop

Conversation

@SaJaToGu

Copy link
Copy Markdown
Owner

Summary

Release 0.9.0 close-out PR. This brings main up to the current develop state and includes the final release documentation update.

Close-out commit on top of develop:

  • updates CHANGELOG.md with the late 0.9.0 solver-loop, model-catalog, benchmark-methodology, and README close-out items
  • rewrites docs/CURRENT_CONTEXT.md from in-progress 0.9.0 validation to post-0.9.0 status
  • updates docs/ROADMAP.md from 0.9.0 planned to 0.9.0 done
  • marks PLANNING_0.9.0.md as completed in the README tree

Validation requested before merge

  • AIS review via scripts/review_pr.py
  • pytest / unittest smoke selected by Mavis
  • tag validation: existing v0.9.0 tag currently points to the earlier milestone-sync commit and is not moved by this PR

Notes

  • develop is fast-forwardable from current main based on the local merge-base check before creating this PR.
  • Local untracked docs/PLANNING_0.10.0_DESIGN_BRIEF.md was intentionally not staged or committed.

SaJaToGu and others added 30 commits June 18, 2026 01:28
chore: 0.9.0 kickoff — planning doc + backlog cleanup
…328)

* fix(cost): forward --max-run-* flags from run_overnight.py to batch

The cost/token limit flags (`--max-run-cost-usd`, `--max-run-input-tokens`,
`--max-run-output-tokens`) are defined on solve_issues.py and consumed by
workers/opencode_adapter.py. They were forwarded by solve_issues_batch.py
to its worker processes, but were silently dropped by run_overnight.py
before reaching the batch runner — so an overnight run with a cost or
token cap set on the parent never actually enforced it.

This was already partially fixed for the batch path; the gap was the
overnight → batch hop. This commit closes that gap.

Changes:
- run_overnight.parse_args: 3 new flags (`--max-run-cost-usd`,
  `--max-run-input-tokens`, `--max-run-output-tokens`) with help text
  noting they are forwarded to the batch solver.
- run_overnight.build_batch_command: forwards the 3 flags when set,
  using the same defensive `getattr(args, ..., None) is not None`
  pattern that solve_issues_batch.build_worker_command already uses,
  so unset values are not emitted as flags and 0.0 / 0 are preserved
  as real caps.

Fixes #324

* test(cost): cover batch + overnight cost-limit forwarding

Regression coverage for #324. Pins the forwarding contract so the fix
in run_overnight.py cannot silently regress.

Three test classes:

* SolveIssuesBatchForwardingTests: solve_issues_batch.build_worker_command
  forwards each of the 3 limits when set, omits them when None, treats
  0.0 as a real cap, and handles the case where only one limit is set.

* RunOvernightForwardingTests: same contract, but for the overnight →
  batch hop (run_overnight.build_batch_command).

* EndToEndForwardingTests: an integration test that simulates the full
  overnight → batch → worker forwarding path and asserts the limit
  value set on the overnight runner reaches the worker command intact.

This complements the existing test_run_overnight.py suite (which covers
bounded-solver, health, and verbosity flags) by adding cost/token-limit
flag coverage. No existing tests are modified.

Part of #324
* feat(reviewer): add review_pr.py — runtime for the 3 reviewer prompts

The 3 reviewer prompt profiles introduced in #313 (under
`./agents/reviewers/reviewer-{code,architecture,documentation}.md`)
were loadable artifacts but nothing invoked them. This commit adds
`scripts/review_pr.py`, a separate script that:

- Takes `--pr N` and `--role {code,architecture,documentation}`.
- Resolves the role via `role_routing_loader.get_role_config`,
  mapping the user-facing arg to the role_routing.yaml key
  (`code` -> `reviewer_code`, etc.).
- Loads the prompt text from the role's `prompt_file` field
  (declared in role_routing.yaml); no prompt content is duplicated
  in the script.
- Fetches the PR diff from GitHub via the `application/vnd.github.v3.diff`
  Accept header (unified diff in one call).
- Calls OpenRouter's `chat/completions` endpoint with the
  system + user messages, using the model declared in role_routing.yaml
  for the resolved role — never a hardcoded model.
- Parses the `**Verdict**: <value>` line the prompts declare and
  emits the structured Markdown to stdout.

Architectural invariant: this is a SEPARATE script, not a flag on
`solve_issues.py`. The solver stays dumb.

The script is the infrastructure for the 0.9.0 validation run (issue
#326). It does NOT, by itself, validate that ai-issue-solver works
end-to-end — that is the role of #326's real Solver run with a real
`reports/runs/.../summary.txt`. This PR explicitly does not claim a
solver-validation success; it only claims the runtime exists and
works for all three reviewer sub-roles.

Exposes for testability:
- `resolve_role` / `load_prompt` / `fetch_pull_request_diff`
  / `call_openrouter` / `parse_verdict` / `run_review` — each
  can be called independently with injected dependencies.

Part of #325

* test(reviewer): cover code / architecture / documentation reviewer paths

Regression coverage for #325. The reviewer runtime depends on the
3 prompt files in .agents/reviewers/ and on the 3 reviewer entries
in config/role_routing.yaml. Both are loadable artifacts that can
drift independently of the script, so the tests pin the contract.

Coverage (8 test classes, 45 tests):

* ConstantsTests (4) — ROLE_ALIASES has exactly the 3 documented
  reviewer paths, the alias-to-role_routing.yaml mapping is correct,
  REVIEWER_PROMPT_FILES matches the 3 known prompt files, and those
  files exist on disk.

* ResolveRoleTests (5) — each of code/architecture/documentation
  resolves to the right role_routing.yaml entry; unknown role and
  missing-from-config cases raise ReviewerRoleError.

* LoadPromptTests (5) — all 3 real prompt files load and contain the
  expected header + '**Verdict**' marker; role without 'prompt_file'
  raises; missing prompt file raises.

* ParseVerdictTests (8) — the 3 documented verdict values parse
  correctly, including extra whitespace, case insensitivity, and
  negation of hallucinated / missing values.

* FetchPullRequestDiffTests (6) — 200 returns the diff; 404 raises
  PullRequestNotFoundError; 5xx propagates HTTPError; the Accept
  header is application/vnd.github.v3.diff; Bearer token is sent
  when present and skipped when absent; oversized diffs are
  truncated with a clear marker.

* CallOpenRouterTests (6) — success returns assistant content; the
  model in the request payload is the role's value, not a hardcoded
  string; missing token raises; HTTP error propagates; missing
  choices / missing message content raise ValueError.

* RunReviewTests (6) — happy path for each of code / architecture /
  documentation (3 separate tests); role error and PR-not-found
  propagate end-to-end; the model passed to the LLM call is the
  role_routing.yaml value, not a test-local constant.

* ParseArgsTests (4) — minimal args parse; --dry-run toggles;
  invalid role and missing --pr are rejected by argparse.

* ReviewerVerdictDataclassTests (1) — sanity check on the
  output dataclass.

Tests use real .agents/reviewers/ files and the real
config/role_routing.yaml; only the network calls (GitHub API and
OpenRouter) are mocked via a session-injection seam.

This is the infrastructure for #326, not a Solver-validation run.
The PR is a Mavis-as-dev contribution; #326 must produce a real
`reports/runs/.../summary.txt` to count as system-under-test evidence.

Part of #325
Pre-Work for #326 (first real Solver run with a real
`reports/runs/.../summary.txt`). The 0.9.0 validation run needs a
reliable issue pool to draw from, but `docs/BACKLOG/open.md` was
no longer trustworthy: it listed 10 sections whose GitHub issues
were already closed, plus 2 sections that were Priority-1 by label
but should be parked until 0.9.0 results are in.

Scope (per user instruction, 2026-06-18):

1. Move these 10 closed sections from open.md to done.md with
   provenance pointing at the closing issue number:

   | Section | Title                                | Closed via |
   |---------|--------------------------------------|------------|
   | §5      | Claude Code alternative              | #191       |
   | §6      | Low-code / non-code repos            | #188       |
   | §15     | Process retrospective                | #218       |
   | §16     | GitHub repository intelligence      | #213       |
   | §17     | Workflow control                     | #216       |
   | §18     | Codex sandbox hardening              | #217       |
   | §19     | Structured rework workflow           | #220       |
   | §21     | Solver process supervisor            | #223       |
   | §24     | GitHub Actions solver trigger        | #243       |
   | §25     | Decompose oversized issues           | #244       |

2. Mark §37 (Free OpenCode models) and §39 (periodic documentation
   benchmark) as parked/future under a new `## Parked / Future`
   section at the end of open.md, with a `Parked because:` note
   attached to each:

   - §37: not 0.9.0-critical; the hard-coded free-models list is
     known stale (see agent memory 2026-06-14) and must be
     re-verified before any real run.
   - §39: depends on later validation/model-comparison data from
     0.9.0; defer until the 0.9.0 validation report and free-model
     registry are stable.

3. Replace references to the legacy `NEXT_BACKLOG.md` filename
   inside the remaining open.md body with `open.md`. The single
   reference in the file header (line 14) is intentionally left
   alone: it documents the historical rename in 0.7.0 and is
   informative.

After this commit:

- `open.md` is 269 lines (was 648) and contains exactly 1 active
  section (§22) plus 2 parked sections (§37, §39).
- `done.md` is 213 lines (was 142) with the 10 new entries appended
  in the existing '## Done — §N Title (#issue)' format.
- The file is now a reliable input for #326's issue selection.
  Anything that still appears in open.md is either an open
  priority item (§22) or a parked/future entry explicitly marked as
  such; create_backlog_issues.py will not pick up parked entries.

Out of scope (explicit, per user):

- No Solver-validation runs (this is documentation hygiene, not a
  system-under-test event).
- No main-branch changes.
- No work on #326.

Sibling: #326 is the first real Solver run, gated on this cleanup
land-merging first.
Extends the existing post_merge_cleanup.py with a new
`--local-branches` mode that classifies and optionally deletes
stale local branches. The pre-existing remote-branch behavior is
unchanged; this commit is purely additive.

Safety rule (pinned by tests, per issue #331):

  protected         -> never delete
  merged into base  -> can delete (with --apply)
  gone upstream +
    unmerged        -> manual review only, never delete
  unmerged          -> manual review only, never delete

`gone-upstream` is a hint, never a deletion approval on its own.
A branch whose commits are already on `base` is safe to delete
even if its upstream is gone (typical post-merge flow: upstream
deleted by GitHub after squash-merge). A branch whose upstream is
gone AND unmerged is treated as a manual-review candidate because
the local commits might be lost work that the user has not pushed.

Implementation:

* `run_git(args, cwd=None)` — top-level runner using
  `subprocess.run(... capture_output=True, text=True, check=False)`.
  Injected as `git_runner` in cleanup_local_branches and
  apply_local_branch_cleanup. Tests use a `FakeGitRunner` that
  records every call and returns pre-configured responses per
  (args-substring) pattern — same injection style as
  `scripts/review_pr.py`.
* `cleanup_local_branches(base, show_unmerged, dry_run,
  protected_names, git_runner)` — runs `git fetch --prune origin`,
  enumerates local branches, classifies each into protected /
  merged (deletable) / unmerged (manual review). Returns a
  `LocalBranchReport` with all categories and any errors.
* `apply_local_branch_cleanup(report, dry_run, git_runner)` —
  invokes `git branch -d` (not `-D`; merged-not-protected
  guarantee makes `-d` safe) for each entry in
  `report.to_delete`. Returns a list of action strings including
  any `git branch -d` failures.
* `print_local_branch_report(report, dry_run)` — German output in
  the same style as the existing `print_cleanup_result`.
* `LocalBranchEntry` and `LocalBranchReport` frozen /
  mutable dataclasses for the structured result.

CLI:

  python scripts/post_merge_cleanup.py --local-branches --base develop
  python scripts/post_merge_cleanup.py --local-branches --base develop --apply
  python scripts/post_merge_cleanup.py --local-branches --base develop --show-unmerged
  python scripts/post_merge_cleanup.py --local-branches --base develop --apply --show-unmerged

`--base` defaults to `develop`. `--show-unmerged` is the
visibility flag for the manual-review list — it does NOT enable
deletion of unmerged branches. Deletion of unmerged branches is
not supported by this script and would require a conscious
`git branch -D` by the user.

Tests (added to existing test_post_merge_cleanup.py):

* `FakeGitRunner` — substring-based fake that records every
  call and returns pre-configured responses.
* `LocalBranchSafetyTests` (3) — main, develop, and current
  branch are never deletable, even with --apply.
* `LocalBranchDeletionTests` (5) — merged is deletable;
  unmerged is not (with or without --show-unmerged);
  gone-upstream-and-merged is still deletable; gone-upstream-and-
  unmerged is manual-review, never deletable; --show-unmerged
  controls visibility of the manual-review list.
* `LocalBranchFetchAndErrorsTests` (5) — git fetch --prune is
  invoked; fetch failure does not block classification;
  list-branches failure returns a non-empty errors list; git
  branch -d failure is reported in the actions list, not raised;
  no-local-branches case is empty and sane.
* `LocalBranchDryRunVsApplyTests` (2) — dry_run issues no
  `git branch -d`; --apply issues `git branch -d` once per
  deletable branch.
* `LocalBranchDataclassTests` (2) — LocalBranchEntry is frozen;
  LocalBranchReport exposes the expected fields.

Total tests: 8 existing + 17 new = 25 (all green).
`-W` (Python 3.10 and 3.12 CI matrix) clean.

Out of scope (per issue #331):

- No Solver-validation runs. This is workflow hygiene, not a
  system-under-test event.
- No main-branch changes.
- No work on #326. #326 is gated on this PR landing only via
  the merge queue, not on its semantic content.
- No interactive prompts. Safe to run in CI / cron.
- No new dependencies. Stays stdlib + `requests`.
- No changes to the existing remote-branch deletion behavior
  (`cleanup_stale_branches` is byte-identical to before).

Fixes #331
Documents the state of the 0.9.0 prep as of 2026-06-18 02:30 UTC,
just before the first real Solver run (#326).

What's new in CURRENT_CONTEXT:

* Each of the four 0.9.0 scope pieces is now annotated as **landed**,
  **in progress**, or **pre-work done** with the exact commit SHAs and
  PR numbers (#328, #329, #330, #332). This is the audit trail that
  separates Mavis-as-dev infrastructure work from the upcoming
  Solver-produced run.
* A new section "Resolved Questions (closed by 0.9.0 prep, 2026-06-18)"
  records the four closed items: backlog health, cost-limit flags,
  reviewer runtime, local branch cleanup.
* "Current Focus" explicitly distinguishes **Mavis-as-dev
  infrastructure** (the four landed pieces) from the
  **Solver-produced metric** that #326 will produce. This is the
  same distinction captured in agent memory as
  "Mavis-as-dev vs the system Mavis is being measured on"
  (2026-06-18) and in the validation report at
  reports/validation-0.9.0.md.
* "Open Questions" updated: "Solver effectiveness" is the only
  real open question, and the validation-target issue (#333) plus
  the report skeleton are in place to receive the first run.
* New open item: GITHUB_TOKEN validity — the next Solver run needs
  a fresh token (current one returns Bad credentials as of
  2026-06-18 02:25 UTC). The first Solver run cannot push branches
  or open PRs until this is fixed.

Out of scope (explicit): no Solver-validation run started in this
commit. This is documentation, not a run-report. No source code
outside docs/CURRENT_CONTEXT.md is touched.

Validation report skeleton lives at reports/validation-0.9.0.md but
is correctly gitignored (reports/* is in .gitignore, only
.gitkeep is tracked). The report is a runtime artifact, not source.

Sister issue: #326 (Validation Metrics & Run).
Validation target: #333.
…ipts/review_pr.py

Automatisch gelöst mit AI Issue Solver (Modell: opencode)
Issue: #333
[AI] Fix: Add docstring examples to parse_verdict in scripts/review_pr.py
…setup path (#343)

Automatisch gelöst mit AI Issue Solver (Modell: openrouter_direct)
Issue: #337

Co-authored-by: AI Issue Solver <ai@github.com>
#347)

* fix: Löse Issue #346 — Align OpenRouter Direct guardrails with OpenCode solver runs

Automatisch gelöst mit AI Issue Solver (Modell: opencode)
Issue: #346

* fix(openrouter): stabilize direct guardrail tests

* fix(openrouter): wire direct guardrails into solver reports

---------

Co-authored-by: AI Issue Solver <ai@github.com>
…flight for Solver runs (#348)

Automatisch gelöst mit AI Issue Solver (Modell: openrouter_direct)
Issue: #339

Co-authored-by: AI Issue Solver <ai@github.com>
… Direct default model (#349)

Automatisch gelöst mit AI Issue Solver (Modell: openrouter_direct)
Issue: #340

Co-authored-by: AI Issue Solver <ai@github.com>
)

* fix: Löse Issue #350 — Harden single-run watchdog for post-worker Solver phases

Automatisch gelöst mit AI Issue Solver (Modell: opencode)
Issue: #350

* fix(solver): bound post-worker git and PR operations

---------

Co-authored-by: AI Issue Solver <ai@github.com>
…355)

* fix: Löse Issue #354 — Harden OpenRouter Direct patch output and reject artifacts

Automatisch gelöst mit AI Issue Solver (Modell: opencode)
Issue: #354

* fix(openrouter): stabilize reject artifact handling tests

* test(openrouter): allow platform-specific patch failures

---------

Co-authored-by: AI Issue Solver <ai@github.com>
Merge PR #368 after MiniMax/Mavis rework and Codex review.\n\nValidated:\n- GitHub CI green on Python 3.10 and 3.12\n- scripts/review_pr.py code review after rework: approve\n- Scope stayed limited to shared solver command specs and tests\n\nFollow-up: add an explicit solve_issues.py rework mode for open PR branches.
Merge PR #365 after updating it onto current develop and addressing documentation review feedback.\n\nValidated:\n- Local targeted tests: tests.test_reviewer_runtime, tests.test_role_routing, plus combined reviewer/solver command tests during branch update\n- GitHub CI green on Python 3.10 and Python 3.12\n- Code reviewer verdict: approve\n- Documentation reviewer final verdict: approve\n\nAlso documents the 1.0 app vision and first production target repository.
Merge PR #358 after updating onto current develop.\n\nValidated:\n- Local targeted tests: tests.test_solve_issues, tests.test_worker_adapters, tests.test_opencode_session_reader, tests.test_solver_commands\n- GitHub CI green on Python 3.10 and Python 3.12\n- Code reviewer verdict: approve\n\nFollow-up improvements for WAL state conflict handling continue in PR #360.
Merge PR #360 after retargeting onto develop and resolving the stack after #358.\n\nValidated:\n- Local targeted tests: tests.test_solve_issues, tests.test_solve_issues_batch, tests.test_run_overnight, tests.test_solver_commands, tests.test_worker_adapters, tests.test_opencode_session_reader\n- GitHub CI green on Python 3.10 and Python 3.12\n- Code reviewer verdict: approve\n- Follow-up added clean-state guard coverage and fixed run_overnight step numbering.
Closes #336.\n\nValidated:\n- python scripts/solve_issues_batch.py --help\n- python -m compileall scripts tests\n- python -m unittest tests.test_solve_issues_batch\n- GitHub CI green on Python 3.10 and Python 3.12
Closes #338.\n\nValidated:\n- python -m unittest tests.test_model_selection\n- python -m compileall scripts tests\n- GitHub CI green on Python 3.10 and Python 3.12\n- Code reviewer verdict: approve
Refs #366.\n\nValidated:\n- rg/link checks over README and docs\n- git diff --check\n- GitHub CI green on Python 3.10 and Python 3.12\n- Documentation reviewer final verdict: approve\n\nThis is a policy/documentation step only; implementation gaps remain tracked in #366/#364.
SaJaToGu and others added 29 commits June 25, 2026 19:19
…able token cap (§56) (#440)

Three failure modes measured across 7 solver runs in this session
(see BACKLOG §56 in docs/BACKLOG/open.md for the full data):

- Mode A (OpenRouter 400 before any token output): stop forcing
  use_structured_output=True in the rework code path; emit a plain
  patch prompt via worker.build_patch_prompt(structured=False).

- Mode B (output truncated by 4096-token cap, then 'no patches'):
  read OPENROUTER_REWORK_MAX_TOKENS (default 16384) instead of the
  hardcoded 8192.

- Mode C (worker writes a full rewrite-from-scratch diff that
  git apply rejects against the current branch tip): rework prompt
  now carries the current head SHA + the existing PR commit list
  and instructs the model to produce an incremental patch only.

Plus: ValidationGitHubClient.get_pull_request_commits() for the new
prompt context (404 returns []). Tests added for prompt shape,
commit formatter, max_tokens override, and worker invocation
without response_format.

Branch: codex/rework-pr-fix (uncommitted before this commit)
Tests: 39+52+163 OK; git diff --check clean
Full discover: skipped (known slow repo-profiling path, single
relevant test green in isolation)
Live 3-run rework: not executed (no open PRs available right now)
stash@{0}: untouched

Co-authored-by: MiniMax <MiniMax@local>
- open.md: §57 stubbed out as DONE-via-PR-#442 (squash 8d68b50);
  §58 (priority/3, anti-pattern-doc) stays open as the next
  follow-up after this fix.
- done.md: full closure entry for §57 with bug description,
  repro (PR #441 / Issue #389 / gpt-4o / 1-of-2 patches),
  fix locations (workers/base.py, workers/openrouter_worker.py,
  workers/execution.py, scripts/solve_issues.py), acceptance
  test (total=2/applied=1/failed=1 → returncode 6, no commit,
  no push, no PR), verification matrix (53+21+95+163 tests OK
  + GitHub CI green + user live review 'mergebereit'), and
  out-of-scope notes for §58 + the still-relevant patch-mismatch
  hardening for the normal solve path (potential §59).
- open.md: §56 stubbed out as DONE-via-PR-#440 (squash 166f8b2).
  Cross-references §57 (done) and the still-open patch-mismatch
  hardening for the normal solve path.
- done.md: full §56 closure entry with three failure modes (A: 400,
  B: 4096 cap, C: rewrite-from-scratch diff), fix locations
  (prompts/rework_pr.md, scripts/validation/rework.py,
  scripts/validation/github_client.py), verification matrix
  (39+52+163 OK + GitHub CI green + user live review 'Sieht gut
  aus'), live-validation gap (no open PRs at fix time), and
  out-of-scope notes for §57 (now also closed), §58 (open), and
  the still-relevant patch-mismatch hardening for the normal
  solve path (potential §59).

Backlog is now: §58 priority/3 open, plus the implied §59
(Patch-Mismatch-Hardening for the normal Solve-Pfad) which can
be filed explicitly if/when we want a separate item.
* docs(solver): guard against recently removed patterns (§58)

* fix(solver): relativize recently-removed-patterns path in worker prompt

Live-review finding by Guido (PR #443 review): `build_solve_prompt`
included the local absolute path of docs/AGENTS.md in the worker
prompt (`(Pattern list sourced from
/Users/.../docs/AGENTS.md.)`), which leaked the operator's local
filesystem layout into the LLM context. Cuts against the existing
prompt-hygiene in this file.

Fix:
- In `build_solve_prompt`, display the patterns-file path as
  repo-relative (`docs/AGENTS.md`) when the resolved path lives
  under PROJECT_ROOT.
- If the operator explicitly sets `AIS_RECENTLY_REMOVED_PATTERNS_FILE`
  to an absolute path outside PROJECT_ROOT, show that absolute path
  verbatim — it was an explicit choice, not a leak from default
  config.
- Two new tests in tests/test_solve_issues.py:
  - `test_solve_prompt_does_not_leak_absolute_project_root_path`:
    default path is shown repo-relative, PROJECT_ROOT / /Users/ /
    tempdir are not in the prompt.
  - `test_solve_prompt_does_not_leak_absolute_path_from_env_var_override`:
    explicit env-var override outside PROJECT_ROOT is preserved
    verbatim.

Tests: 168 OK (+2). `git diff --check` clean.

---------

Co-authored-by: MiniMax <MiniMax@local>
- open.md: §58 stubbed out as DONE-via-PR-#443 (squash 11eafc1).
  Notes that the docs/AGENTS.md Recently-Removed-Patterns list is
  now maintainer-pflegbar; future PRs that intentionally remove a
  pattern should add a row in the same PR. Cross-references the
  still-open potential §59 (patch-mismatch hardening).
- done.md: full §58 closure entry with problem (PR #441 episode),
  fix locations (docs/AGENTS.md, scripts/solve_issues.py,
  tests/test_solve_issues.py), the user-found path-leak fix from
  live review (now repo-relative when under PROJECT_ROOT, verbatim
  for explicit env-var override), verification matrix (168 OK +
  GitHub CI green + manual probe + user live review 'mergebereit'),
  and the maintainer obligation going forward.

Backlog is now EMPTY: §51/§52/§53/§54 done-via-PRs, §55 done-via-PR-#439,
§56/§57/§58 documented as done. The implicit §59 (patch-mismatch
hardening for the normal solve path) remains a possible future
item but is not filed.
§59 = 'Watchlist: Patch-mismatch hardening for the normal solve
path'. Priority 4, labels kind/watchlist + theme/solver +
area/prompt.

Status: WATCHLIST ONLY — not an active backlog commitment. No code
or architecture work until the activation trigger (≥3 Mode-C
patch-mismatch runs on the normal solve path, ideally across
different files) is met.

Rationale:
- The Mode-C failure (worker produces a patch that git apply
  rejects because it does not match the current working tree) is
  the same patch-mismatch symptom §56 addressed for --rework-pr.
- Before §57, Mode-C on the normal solve path could produce
  regression-PRs (PR #441 vs. PR #439). After §57, Mode-C is a
  clean failure with no PR — acceptable behavior.
- A §59 fix would turn these clean failures into clean successes,
  but the architectural cost (prompt anchoring, per-file
  git apply --check, optional re-prompt loop) is only worth it
  if the failure pattern is systematic. One data point
  (Issue #389 re-run, scripts/model_selection.py:52, 1 patch,
  0 applied, no PR) is not enough to justify the work.
- When a Mode-C failure appears on the normal solve path, log
  the file + issue + date here so we can see when the threshold
  is met.

Non-goal while parked: no code changes. §57 + §58 are sufficient
to keep the pipeline correct.
Tracked from PR #444 (Issue #390 validation run, 2026-06-26).
Same failure mode as §57 (PR #442): worker reports nonzero exit +
partial on-disk state, but classify_worker_outcome in
workers/execution.py did not catch returncode 5 (reject artifacts)
and let it fall through to the generic nonzero_with_changes
branch with failure_class: success.

The PR that slipped through:
- worker_exit_code: 5 (Reject-Artefakte)
- run_outcome_worker_status: failed
- run_outcome_has_changes: True
- run_outcome_failure_class: success (LIE classification)
- run_outcome_delivery_status: pr_created
- Diff: a 5-LOC trivial dummy function
- Post-solve tests: not_run (timeout 300s)

§57 closed returncode 6 (partial_patch_failure). §60 closes
returncode 5 (reject_artifacts) and any future reject / partial-
state class. General principle: 'Any nonzero worker-returncode that
produces partial on-disk changes must be a hard stop. Commit +
push + PR-create must not run.'

Suggested scope: explicit allow-list (only returncode 0 may
proceed with has_changes=True) OR explicit deny-list extending the
§57 check; WorkerOutcome invariant test; unit tests for
returncode 5 hard-stop; possibly split nonzero_with_changes into
specific classes (reject_artifacts, partial_state, etc.).

Touches: workers/execution.py, workers/base.py,
workers/openrouter_worker.py, scripts/solve_issues.py, tests.
- open.md: §60 stubbed out as DONE-via-PR-#445 (squash 2549f0f).
  Cross-references §57 (also done) and the still-valid general
  principle for any future returncode class. Old §60 body
  (Measured/Suggested scope/Checks) trimmed from open.md.
- done.md: full §60 closure entry with bug description, repro
  (PR #444 / Issue #390 / gpt-4o / returncode 5 / reject artifacts),
  fix locations (workers/base.py + workers/execution.py +
  workers/openrouter_worker.py + scripts/solve_issues.py), scope
  discipline (NO refactor of nonzero_with_changes, per User
  directive), verification matrix (22+96+53+168 OK + GitHub CI
  green + user live review 'mergebereit'), and the general
  principle that §57 (returncode 6) and §60 (returncode 5) both
  implement.

Backlog is now: §59 watchlist (parked, Mode-C Threshold ≥3) +
§59-related notes only. §60 closed. All §51-§58 done via earlier
PRs.
* fix: Löse Issue #446 — Update README for current solver workflow

Automatisch gelöst mit AI Issue Solver (Modell: openrouter_direct)
Issue: #446

* docs(readme): extend #446 fix with dynamic-discovery + recently-removed-patterns guards

Extends PR #447 (liquid/lfm-2.5-1.2b-instruct:free initial fix that
hard-stopped cleanly after §57+§60, but covered only partial scope
of Issue #446). User reviewed and asked for two missing points:

1. **OpenCode Free Models (dynamisch)**: new block in README that
   explains scripts/model_catalog.py as the live source of truth
   for free-model discovery (with cache + static fallback,
   'missing' / 'stale' classification, --verify-opencode and
   --list-opencode-free-models CLI paths).

2. **Recently-Removed-Patterns-Guard**: new block that explains the
   docs/AGENTS.md 'Recently Removed Patterns' table the solve
   prompt now consults before re-introducing a recently-removed
   pattern. Lists the two current entries (static free_models
   removed by PR #439; $20/$20 cost-cap defaults replaced by
   $15/$50 + staggered warnings in PR #437). Notes that the
   guard is 'soft' (worker explains in the PR body if needed) and
   that maintainers update the table in every removal PR.

PR #447 also kept its earlier additions (Hard-Stop-Sicherheit
absatz + Backlog-Status-Update).

Closes #446 (now full scope).

* docs(readme): drop inaccurate 'Reviewer-Prompt uses model_catalog'-claim

User live-review feedback on PR #447 (2026-06-26, second pass):
the README block 'OpenCode Free Models (dynamisch)' claimed that
the Reviewer-Prompt also uses the model_catalog.py mechanism. Code
review shows scripts/review_pr.py does NOT consult model_catalog.py
— it loads reviewer prompt files and does role-routing only. The
claim was incorrect / overstated.

Fix: scope the statement to the solver helpers that do use
model_catalog.py ('--verify-opencode', '--list-opencode-free-models').
The reviewer path is unrelated and does not need a static model
list, but also does not actively consult the dynamic discovery.

---------

Co-authored-by: AI Issue Solver <ai@github.com>
Co-authored-by: MiniMax <MiniMax@local>
…k-Sweep

Adds a sequential benchmark runner that invokes solve_issues.py for
each of the 31 free models available via OpenRouter (26 models,
prompt=0/completion=0 per /models endpoint) and OpenCode (5 models,
dynamically discovered via 'opencode models'). Each run targets
Issue #446 (README-Update) and aggregates results into
reports/benchmarks/free-models-2026-06-26.json plus a log file.

Per-run classification (parsed from solver output):
- success_pr_created / success_no_pr
- patch_validation_failed_rc5 (reject artifacts; §60 territory)
- patch_mismatch_mode_c (worker writes diff against wrong base)
- no_patches (model produced no parseable diff)
- openrouter_400 (model/slug invalid)
- infrastructure_opencode_state_conflict (CLI/Serve version mismatch)
- infrastructure_or_unknown_failure

The benchmark sweep is invoked from Mavis after User directive
('Alle freien Modelle über OpenRouter und alle freien Modelle über
OpenCode'); it is not part of the normal solver workflow.
- open.md: §61 stubbed out as DONE-via-PR-#447 (squash 9b85570).
  Notes that the three README areas (dynamic OpenCode free-model
  discovery, recently-removed-patterns guard, partial/reject-patch
  hard-stop) are now covered and that PR #447 also includes a
  correction removing an overstated 'Reviewer uses model_catalog'
  claim.
- done.md: full §61 closure entry with the three-commit fix
  history (liquid/lfm-2.5-1.2b-instruct:free initial, Mavis
  extension, live-review correction), verification matrix
  (39+65+168 OK + GitHub CI green + user live review), and an
  out-of-scope note documenting the Free-Models-Benchmark-Sweep
  results (1/31 actually completed, 5 OpenCode fails on
  CLI/serve version conflict, methodology improvement tracked
  separately).
…' label

Backlog:
- §62 (priority/2, active) — Fix benchmark/open-PR workflow methodology.
  The current open-PR guard + missing GitHubClient.get_pull_requests()
  corrupted the 31-model Free-Models-Benchmark-Sweep (24 of 31 runs
  were silently aborted). Scope: --benchmark / --skip-pr combo +
  add get_pull_requests() method + 'foreign PR' distinction +
  update benchmark_free_models.py. §64 depends on this.
- §63 (priority/3, active but not 0.9.0-blocking) — OpenCode app-state
  conflict resolution. CLI 1.15.13 vs MiniMax Code.app bundle 1.14.28
  with app-launchd respawn. Scope: scripts/opencode_state_diagnostic.py
  + docs/OPENCODE_APP_STATE.md + option A/B/C for clean resolution.
  '--allow-opencode-state-conflict' stays as diagnostic tool.
- §64 (priority/4, parked) — Free-model robustness study. Depends on
  §62. Goal: turn 'Free-Models-Produktivität ist Einzeldatenpunkt'
  into 'belastbare Statistik' across 5 models × 5 issues each. README
  update with recommendation table + 'experimental / supervised /
  docs-only' labels. NO production-ready claim until data is in.

README:
- After §61 PR #447 added the 'OpenCode Free Models (dynamisch)'
  block, this commit adds a sibling block: 'Free-Models Status
  (Stand 2026-06-26): experimentell und supervised-only'. Explains
  that gpt-4o (paid OpenRouter) stays the default for strategic
  issues, Free-Models are for smoke-tests / low-risk / docs-only,
  and every Free-Model PR needs Guido live review before merge.
* fix(solver): add benchmark mode for open PR sweeps

* fix(benchmark): derive REPO from __file__ instead of hardcoded path

PR #448 CI was failing with FileNotFoundError on /Users/Guido/...
because scripts/benchmark_free_models.py hardcoded REPO to the
developer's local absolute path. Local tests passed (path exists),
CI failed (path does not exist in the GitHub runner).

Fix: REPO = Path(__file__).resolve().parent.parent, same pattern
as SCRIPT_DIR / PROJECT_ROOT in scripts/solve_issues.py.

Verified locally:
- test_benchmark_free_models: 1 OK
- test_solve_issues: 173 OK
- test_github_client: 25 OK

---------

Co-authored-by: MiniMax <MiniMax@local>
- open.md: §62 stubbed out as DONE-via-PR-#448 (squash 0d08679).
  Notes the three-commit history (Codex fix + Mavis portability fix +
  merge), the scope discipline (§62 = methodology only, no §59/§64
  mixed in), and that §64 is now unblocked.
- done.md: full §62 closure entry with the bug description
  (hardcoded /Users/Guido path + missing get_open_pull_requests +
  open-PR-guard aborting 24 of 31 benchmark runs), fix locations
  (github_client.py alias, solve_issues.py --benchmark flag,
  benchmark_free_models.py flags + REPO-from-__file__ portability
  fix), verification matrix (1+173+25 OK + GitHub CI green after
  the portability fix + user live review), and the §64-unblock
  note.

Backlog open.md is now: §59 (watchlist) + §63 (priority/3, active
but not 0.9.0-blocking) + §64 (priority/4, parked, abhängig von §62).
§64 is now unblocked for activation.
…sweeps

Parametrize scripts/benchmark_free_models.py so it can run a small
smoke-benchmark on any issue with any subset of free models,
instead of the hardcoded 31-model sweep on Issue #446.

New CLI:
- --issue N (default 446) — issue to benchmark
- --run-label LABEL — used in the aggregate/log filenames, so
  multiple benchmark runs can coexist without overwriting each
  other (smoke-free-models-2026-06-26.json vs the earlier
  free-models-2026-06-26.json for the 31-model sweep)
- --models 'openrouter_direct:foo:free,openrouter_direct:bar:free'
  — comma-separated provider:model list. If omitted, falls back
  to the full OPENROUTER_FREE_MODELS + OPENCODE_FREE_MODELS list
  (preserves the original behavior)

Test updated:
- test_run_one_uses_benchmark_skip_pr_flags now also passes the
  issue_number argument and asserts that --issue and the issue
  number appear in the spawned solver command
§63 (OpenCode app-state conflict resolution, full app-side fix)
is parked. §65 (narrow repo-side scope) carries the diagnostic +
docs subset that is valuable immediately.

Adds:

- scripts/opencode_state_diagnostic.py — pure-stdlib script that
  prints a clear report of:
  - which opencode binaries are reachable (PATH, ~/.opencode/bin,
    app bundles under /Applications/) with versions
  - the running opencode-serve process (pid, binary path, version)
  - which .app bundle owns the launchd respawn (matched from the
    running serve binary)
  - the configured $OPENCODE_BIN env-var
  - a Verdict line that calls out the version conflict when the
    serve binary does not match any discovered CLI version
  Also supports --json for machine-readable output.

- docs/OPENCODE_APP_STATE.md — explains the conflict (CLI 1.15.13
  vs Serve 1.14.28 in the MiniMax Code.app bundle), the
  app-launchd respawn mechanism, and the three resolution
  options (A: app update, B: rename app-bundled binary, C:
  project-side always-use-configured-OPENCODE_BIN). Also
  documents --allow-opencode-state-conflict as a diagnostic-
  only override.

- README: cross-reference to docs/OPENCODE_APP_STATE.md in the
  Free-Models section; also updates §64 reference to reflect its
  closure-with-smoke-evidence status (instead of 'parked,
  pending').

- tests/test_opencode_state_diagnostic.py — 8 tests covering the
  human/JSON output, semver-with-suffix extraction, app-owner
  detection, and OPENCODE_BIN env-var handling.

- docs/BACKLOG/open.md: §63 retitled to mark its diagnostic
  subset as moved to §65, with §65 active and narrow. §64
  closed with the smoke-benchmark evidence (4/4 attempted, 0/4
  PR-relevant, no further 5x5 sweep planned).

Verified:
- 8 new tests OK
- ./venv/bin/python scripts/opencode_state_diagnostic.py prints
  the expected report (2 binaries found, no running serve,
  $OPENCODE_BIN pointing at the app bundle, Verdict OK since
  no serve is currently running — conflict is latent)
PR #449 (squash e38c1f4) implemented dynamic OpenRouter free-model
discovery:

- new fetch_openrouter_free_models() in scripts/model_catalog.py,
  analog to fetch_opencode_free_models() (1h cache + XDG-aware path
  + OpenrouterModelCache dataclass with source='live|cache|fallback')
- pricing-based free-filter via pricing.prompt == '0' AND
  pricing.completion == '0' (-suffix is intentionally NOT a
  fallback signal)
- OPENROUTER_FALLBACK_FREE_MODELS tuple moved to model_catalog.py
  as single source of truth (replaces the old duplicated list in
  benchmark_free_models.py)
- benchmark_free_models.py default path now calls both catalog
  functions; explicit --models still bypasses dynamic filtering
- 5 new tests (3 in test_model_catalog.FetchOpenrouterFreeModelsTests,
  2 in test_benchmark_free_models.BenchmarkFreeModelsTests)
- README 'OpenRouter Free Models (dynamisch für Benchmarks)' block
  added between OpenCode and App-State sections

§66 body in open.md retitled to DONE; full closure entry appended
to done.md with repro + verification + scope-discipline note.

Verified locally: 6 tests OK, live fetch returns source='live'
with 26 models, git diff --check clean.
Triggered by 2026-06-26 Issue #450 benchmark sweep: all 4 worker
runs were failures (rc=1 or rc=2), but benchmark_free_models.classify()
returned 'success_no_pr' for all 4 because solve_issues.py exits
rc=0 even on no_changes status. Fall-through `return 'success_no_pr'`
is the inverse of the truth.

Repro: 4 worker runs on Issue #450 — liquid (rc=2, empty response
with 1387c Prosa), qwen (rc=1, 429), gemma (rc=1, 429), cohere
(rc=2, 0-char response, 135s). Aggregate showed 4× success_no_pr.

Fix-Plan: run-report summary.txt (worker_exit_code + has_changes +
status) as ground truth, log-text heuristics as fallback. New
classes: success_pr_skipped, no_changes, empty_response_rc2,
model_failure_rc1, patch_validation_failed_rc5, partial_patch_failure_rc6,
timeout_rc3, openrouter_429. ~140 LOC estimated, under 250 stop-
criterion. Handover to Codex preferred (pipeline-correctness).

User directive (2026-06-26 22:34): do §67 first (pipeline
correctness), then Issue #450 with gpt-4o. Not parallel.

Handover file at reports/handoffs/to-codex-20260626-2238-benchmark-classify-fix.md
…465)

§67 priority/1 — pipeline-correctness blocker. The previous
`classify()` had a fall-through `return "success_no_pr"` that masked
real worker-failures (rc=1, rc=2, 429 rate-limits) as successful runs.
The 2026-06-26 Issue #450 benchmark sweep confirmed the bug: all four
Free-Models ran with rc=1 or rc=2, but the aggregate showed
`success_no_pr` for all four.

This change makes classify() consult the per-run
`reports/runs/<ts>-<repo>-issue-<N>/summary.txt` (worker_exit_code +
run_outcome.has_changes + status) as ground truth. New canonical
classification classes replace the buggy fall-through:

  - success_pr_created
  - success_pr_skipped   (--skip-pr with has_changes=True)
  - no_changes           (rc=0 + has_changes=False; was success_no_pr)
  - empty_response_rc2
  - model_failure_rc1
  - patch_validation_failed_rc5
  - partial_patch_failure_rc6
  - openrouter_429

Log-text heuristics stay as a fallback for tests / mocked subprocess
calls that do not produce a run-report. Inline summary.txt parsing
keeps the module independent of the solver_reporting package, whose
bare `from utils import` lines fail in the test harness.

Adds 12 new tests covering each classification class, the
run-report-discovery helper, and the log-text fallback path.

Co-authored-by: mavis <Mavis@MiniMax.local>
Following the §66 / done-archive pattern from PR #449:

- docs/BACKLOG/open.md: §67 header retitled to **DONE in PR #465
  (squash 5fbc6f6)** (strikethrough).
- docs/BACKLOG/done.md: full closure entry added. Documents:
  * root cause (classify() fall-through returning success_no_pr
    for every rc=0+no-PR run)
  * fix shape (_find_run_report, _read_run_report_summary,
    classify() with Run-Report-Primary classification)
  * new canonical classes table (success_pr_skipped, no_changes,
    empty_response_rc2, model_failure_rc1, etc.)
  * test coverage (15 tests: 3 existing + 12 new)
  * verification (15 OK, 37 OK cross-module, CI 3.10+3.12,
    AIS code-review ready to merge)
  * scope discipline (§66/§59/§63/§65/gpt-4o default untouched)
  * unblocked: Issue #450 ready to solve.

No code changes in this commit; closure-only.
#466)

Update the `README.md` Verzeichnisstruktur block to reflect the current repo layout (Issue #450).

What changed:
- Removed stale `docs/docs/BACKLOG/open.md` → correctly `docs/BACKLOG/open.md` (now under a BACKLOG/ subdirectory block with open.md and done.md).
- Added missing directories: `workers/`, `prompts/`, `benchmarks/`.
- Expanded `scripts/` to include current model/benchmark/runtime-relevant files (`model_catalog.py`, `benchmark_free_models.py`, `opencode_state_diagnostic.py`, `verify_openrouter_slugs.py`, `solver_reporting.py`, `watchdog.py`, `validation_run.py`, `workflow_congestion.py`, `review_pr.py`, plus the rest of the live scripts).
- Expanded `docs/` to reference current core docs (`AGENTS.md`, `OPENCODE_APP_STATE.md`, `MODEL_OVERRIDE_POLICY.md`, `PLANNING_0.9.0.md`, `PRODUCT_VISION_1.0.md`, `ROADMAP.md`, `label_taxonomy.md`).
- Updated `tests/` block to reference the representative current test modules (`test_benchmark_free_models.py`, `test_model_catalog.py`, `test_solve_issues*.py`, `test_opencode_state_diagnostic.py`, `test_reviewer_runtime.py`).
- Added inline hints to `reports/` subpaths (`runs/`, `benchmarks/`, `preserved-worktrees/`) since those are produced by every solver run.
- Removed the duplicate `---` separator before `## Verzeichnisstruktur`.

Scope: README-only, no code/benchmark/backlog changes.

Acceptance criteria (all met):
- README no longer contains `docs/docs/BACKLOG/open.md`.
- Tree includes all current major directories (`.github/`, `.agents/`, `config/`, `scripts/`, `workers/`, `prompts/`, `templates/`, `reports/`, `docs/`, `tests/`, `benchmarks/`).
- `scripts/` includes the current model/benchmark/runtime-relevant files.
- `workers/` listed with adapter purpose.
- `docs/` references current core docs.
- Redundant separator removed.
- `git diff --check` passes.

Closes #450.
Closure entry for Issue #450 (README Verzeichnisstruktur
update). Documents:

- root cause (README tree had drifted from the repo layout)
- fix shape (README-only, +54/-31, single Mavis-as-dev commit)
- new directories added (workers/, prompts/, benchmarks/)
- expanded scripts/ and docs/ blocks
- redundant --- separator removed
- pre-merge live-review caught two path bugs the AIS
  documentation review missed (PLANNING_0.10.0_DESIGN_BRIEF.md
  untracked, test_review_pr.py → test_reviewer_runtime.py)
- acceptance criteria met
- scope discipline (README-only per User directive)

Original labels: kind/feature, theme/documentation, area/readme,
priority/2
@SaJaToGu
SaJaToGu merged commit 79fed20 into main Jun 27, 2026
4 checks passed
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.

1 participant