Skip to content

refactor(forge-loop): delete the dead and repeated code in nine over-gate stop, warm-start, config, KEEP and locked-loop functions - #2082

Merged
chaojhou merged 21 commits into
mainfrom
lean/shafeng/cc20-kf-loop-bd-pr
Oct 22, 2026
Merged

chaojhou merged 21 commits into
mainfrom
lean/shafeng/cc20-kf-loop-bd-pr

Conversation

@fengshaoyi-amd

@fengshaoyi-amd fengshaoyi-amd commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What this does

Nine functions in src/kernelforge/loop/ were over the code-metrics gate: the in-session gate's stop hook (InSessionGate._on_stop) and its python -c payload scanner (_python_command_payloads), the KB warm-start (kb_warmstart), the campaign-config factory (create_campaign_config), and five IterationLoop methods on the path from a pending KEEP to a measured iteration (_inspect_pending_keep, _reconcile_best_publication, _resolve_keep_sigma, run_one_iteration, _run_locked). This PR brings each one under every limit it broke, mostly by deleting what no caller needs, and changes two failure paths, described under Observable effect.

Production code: +700 / -767 lines (net -67) in 5 files. Tests: +123 / -4 in 4 files. Code-metrics baseline: 12 entries retired, none added.

What changes in each module

  • insession_gate.py: _on_stop keeps only the order of its checks; _stop_for_protected_harness, _stop_candidate_verdict and _stop_performance_verdict decide the stop, and _record_insession_bench publishes the bench the outer loop may reuse. _stop_performance_verdict clears the previous stop's measurements before it benches. _python_command_payloads ends a payload word through a table of three patterns (_PAYLOAD_WORD_BODY) instead of a hand-written quote state machine.
  • knowledge_integration.py: _WarmStartAttempt runs the stages kb_warmstart ran inline (load_solutions, measure_and_adopt, final_result), and the branch that took a numeric pristine aggregate as the median is deleted.
  • campaign_config.py: _require_dev_branch and _program_md_sha256 take the branch check and the program.md digest out of create_campaign_config; the kernel, driver and program_md_file aliases, a copy of the parsed targets and the str() around gpu_type are deleted.
  • runner.py: _remeasure_keep_sigma buys the extra benches _resolve_keep_sigma decides on; _emit_bench_scores, _clears_keep_bar and _decide_iteration_keep take the scoring and the KEEP decision out of run_one_iteration; _begin_locked_run, _price_round and _settle_locked_attempt take the session opening, the round pricing and each attempt's settlement out of _run_locked. Deleted: the changed_files and patch fields _reconcile_best_publication put in its pending record, which the publisher never read, with the published-patch read and the git calls that computed them; the iteration <= 0 guard and coercions in _inspect_pending_keep; isinstance(..., dict) checks on bench results that are always a dict; and in _run_locked, the getattr(self, "archive", None) guard, alias locals and the state_exists gate in front of reconcile_stale_running_session.
  • evaluation/correctness/pipeline.py: ValidationResult.status_label names a result's status (PASS, TIMEOUT, ERROR or FAIL), and the pipeline and the runner both ask the result, replacing the pipeline's _status_label function and the runner's own copy of the same ladder.

Functions brought under the gate

Baseline entries this PR retires, with their values at base:

  • campaign_config.py: create_campaign_config (lines 119)
  • insession_gate.py: _python_command_payloads (cognitive 42); InSessionGate._on_stop (lines 186)
  • knowledge_integration.py: kb_warmstart (cognitive 54, lines 234)
  • runner.py: IterationLoop._inspect_pending_keep (cognitive 33); IterationLoop._run_locked (cognitive 39, lines 256); IterationLoop.run_one_iteration (cognitive 39, lines 206); IterationLoop._reconcile_best_publication (lines 89); IterationLoop._resolve_keep_sigma (lines 96)

Observable effect

Two failure paths change:

  • Resume with a publication diff that cannot be generated. When the git diff for the best commit's publication fails on resume, the loop now logs "failed to publish best result", marks persistence degraded and continues the resume; it used to raise the git error out of the resume.
  • A stop whose benchmark fails case coverage or raises. The in-session gate no longer keeps the previous stop's mean case speedup after a case-coverage failure, or the previous stop's wall time and mean case speedup after a benchmark that raises. Those values reach the gate tag in the iteration's commit message (mean_case_speedup=, raw_mean=) and the session record, which no longer report a measurement this stop did not make. KEEP decisions are unaffected: the loop reuses the in-session bench only when the gate passed.

For every input the repo's own callers produce, campaign configs, session events, in-session allow and block decisions, warm-start envelopes, iteration measurements, KEEP decisions, rulings, persisted KEEP publications, archive digests, log lines and file formats are otherwise the same. Inputs that no in-repo caller produces now behave differently:

  • _remeasure_keep_sigma given a non-dict from measure_wallclock raises AttributeError, recorded as a crashed iteration, instead of treating the bench as failed.
  • A non-dict pristine aggregate in the warm-start is reported as a skipped warm-start (read_reason: "warm_start_error") instead of being taken as a median or as an unavailable baseline.
  • A pending-KEEP record missing base_head, iteration or patch_sha256 raises KeyError; _load_pending_keep refuses such a record first.
  • reconcile_stale_running_session is now also called when there was no prior run_state.json; on the fresh state it is a no-op.
  • _apply_iteration_planning_state now also runs when agent_fn is None after a sourced attempt, as it does in _abandon_iteration.
  • A non-string gpu_type raises AttributeError instead of being converted with str(); None still becomes "mi355x".

Checklist

  • Linked issue(s): none.
  • Tests:
    • test_insession_gate_logic.py adds test_performance_verdict_clears_stale_bench_state_on_coverage_failure, test_stop_clears_stale_bench_state_when_benchmark_raises and test_performance_verdict_keeps_current_wall_when_scoring_is_unavailable.
    • test_loop_runner.py adds test_resume_publication_diff_failure_degrades_persistence_without_propagating, which runs _reconcile_best_publication over a real git workspace whose publication diff raises GitError and asserts the degraded persistence, the recorded error and the warning; two fakes gain status_label.
    • test_pipeline.py adds test_validation_result_status_label_contract, one case per label.
    • test_pr_kb_cli_wiring.py: test_loop_appends_the_event_only_after_the_campaign_guard reads IterationLoop._begin_locked_run, where the campaign guard and the PR-knowledge append now live; its assertion is unchanged.
    • Linux CI is green at head f64dd4aa2.
  • Size/complexity triggers crossed: 1,467 changed production lines, over the 1,000-line limit, so the PR carries size-exception: most of it reworks IterationLoop in runner.py, the validate, bench and KEEP path and the locked loop that drives it, against one baseline file. No function this PR adds or changes crosses a gate limit. Over a review trigger and within the gate, each one ordered sequence: create_campaign_config (80 lines), _decide_iteration_keep (79), _begin_locked_run (77), _run_locked (77), _WarmStartAttempt.measure_and_adopt (77), run_one_iteration (72), _reconcile_best_publication (71), kb_warmstart (69) and _stop_performance_verdict (66). _inspect_pending_keep stays at cyclomatic 19, as at base: it is the one classifier of a pending KEEP against the commit graph. runner.py, knowledge_integration.py and insession_gate.py stay over the 1,200-line module limit they were already over, each shorter than at base.
  • If this simplifies or refactors: the mechanisms removed are listed under What changes in each module; campaign configs, session events, KEEP decisions and publications keep their contract apart from the two failure paths under Observable effect, shown by the loop suites and the tests above.
  • Observable effect: two failure paths change, a resume whose publication diff fails and the in-session measurements after a failed stop benchmark; see Observable effect.
  • Breaking changes: no. create_campaign_config, kb_warmstart and IterationLoop.run_one_iteration keep their signatures. The one added public name is the ValidationResult.status_label property; the _status_label function it replaces had no caller outside pipeline.py.
  • PR addresses single concern: yes, bringing the forge loop's nine over-gate functions under the gate; the two failure-path changes are in the code those functions run.
  • Root cause is upstream: no.

Commits

Squash-merged as 270158c2e from head f64dd4aa2.

  • 18a704786 refactor(forge-loop): end a python -c payload word by a table of three patterns
  • 52309831d refactor(forge-loop): inspect a pending KEEP without re-checking its caller
  • 243005300 refactor(forge-loop): reconcile a best publication without unused pending fields
  • eb6117f56 refactor(forge-loop): remeasure KEEP sigma as one settled resolution
  • c42d444b3 refactor(forge-loop): decide an in-session stop as three named verdicts
  • 474e6b3b8 refactor(forge-loop): finish a validated iteration through named bench steps
  • 86e95a10a refactor(forge-loop): decide one iteration as validate, bench, then KEEP
  • e6a8eedf8 refactor(forge-loop): run KB warm-start as one attempt object
  • 7976e6dc8 refactor(forge-loop): drop warm-start properties that only added C901
  • d205c8542 chore(metrics): drop forge-loop baseline entries now under the gate
  • 806b4c269 refactor(forge-loop): create a campaign config without input aliases
  • f917b5d1f refactor(forge-loop): drive the locked loop without archive guards or aliases
  • 518143dd3 refactor(forge-loop): drop leftover cyclomatic from the locked-loop helpers
  • 2f9d723b0 chore(code-metrics): drop the kf-loop-d baseline entries now under the gate
  • 15c40c031 refactor(forge-loop): restore the comments that state constraints
  • b0fe5d455 fix(forge-loop): clear stale stop measurements
  • 3b3b7abda refactor(correctness): let a validation result name its status
  • plus 4 merges of origin/main, the last by chaojhou

fengshaoyi-amd and others added 15 commits October 21, 2026 11:28
…e patterns

_python_command_payloads walked every character of a payload through a
hand-written three-way state machine (single-quoted, double-quoted with
backslash escapes, bare word), after a second loop that skipped the
whitespace between `-c` and the payload. Both loops are gone:

- `_PYTHON_COMMAND_PREFIX` now consumes the whitespace after `-c` itself
  (`-c\s+` instead of `-c(?=\s)`), so the match ends where the payload
  starts. Python's `\s` is the same class as `str.isspace()`, checked over
  every code point.
- `_PAYLOAD_WORD_BODY` maps the opening quote to the anchored pattern that
  finds the word's end, so the scan is one `match()` call.
- the unbalanced-quote test is one `startswith` at that end, and the
  single-use `raw` alias is inlined.

The scan stays linear: a 400,000-character unterminated payload takes
about 0.1 s, as before. A differential run of base against this code on
200,014 generated and edge commands (80,926 of them with payloads) gives
identical results.

Cognitive complexity 42 -> 16, cyclomatic 14 -> 6, 48 -> 27 lines.

Co-authored-by: Cursor <cursoragent@cursor.com>
…caller

_inspect_pending_keep re-checked and re-normalized fields that its only
caller, _plan_resume_recovery, already pins down from _load_pending_keep:

- the iteration <= 0 guard: _plan_resume_recovery (runner.py:2310-2312)
  raises the same "pending KEEP metadata is incomplete" when
  int(pending.get("iteration", 0) or 0) is not positive, before this
  function is reached.
- int(pending.get("iteration", 0) or 0) and the two
  str(pending.get(...) or "") coercions of base_head and patch_sha256:
  PENDING_KEEP_FIELDS plus _load_pending_keep's exact key-set check mean
  those keys are present, and the caller has already required a positive
  iteration and a non-empty base_head.

Cognitive complexity 33 -> 30 (at the limit), 58 lines unchanged in count.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ding fields

_reconcile_best_publication built two pending keys that _publish_best_result
never reads, and restated why describes_current_best short-circuits:

- pending["changed_files"] and pending["patch"], including the published-patch
  file read that only fed the latter: _publish_best_result
  (runner.py:1637-1644) takes publication_changed_files / publication_patch
  and otherwise calls _publication_changed_files / _publication_patch on the
  KEEP commit, so the assembled values were discarded. The git lookups that
  filled those keys therefore ran a second time inside publish.
- the three-line comment above describes_current_best: it narrated the resume
  session_index / experiment_id divergence the call itself encodes.

89 -> 68 lines, cyclomatic 10 -> 8, cognitive 20 -> 14.

Co-authored-by: Cursor <cursoragent@cursor.com>
_resolve_keep_sigma inlined a re-measure loop whose only job is to buy
extra benches until the dominant case's sigma settles, then fold that
into the KEEP resolution. That loop is now _remeasure_keep_sigma, which
returns the finished SigmaResolution so the caller does not unpack a
3-tuple.

Also deleted:
- the isinstance(extra, dict) guards around measure_wallclock's result:
  measure_wallclock is annotated and always returns a dict
  (evaluation/benchmark/runner.py:256).
- the three-line comment on variance share, which narrated a property of
  the attribution math rather than a local invariant.

96 -> 50 lines (helper 50). Cyclomatic 12 -> 6, cognitive 23 -> 9.

Co-authored-by: Cursor <cursoragent@cursor.com>
InSessionGate._on_stop mixed harness protection, the block-budget hand-off,
correctness, and the KEEP-bar bench into one 186-line method. Those are now
three complete jobs plus a short recorder:

- _stop_for_protected_harness: the harness-tamper allow-or-block
- _stop_candidate_verdict: JIT rebuild, correctness, and the correctness-only
  allow; delegates the KEEP-bar bench to _stop_performance_verdict
- _stop_performance_verdict: three-measurement bench and the allow/block on
  the mean-case score
- _record_insession_bench: the bench fields the outer loop reuses on allow

Also deleted the pre-clear of last_bench_result before measure_wallclock: every
path already sets last_bench_result (to the bench or to None) before returning.

186 -> 27 lines. The helpers are each under the gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
…h steps

run_one_iteration inlined build, validate printing, bench obtain, score
printing, registers, the KEEP bar and canonical acceptance. After deleting
what it rewrote for no reader, those jobs are named helpers:

- pmc_diagnosis / pmc_full local "" constants: IterationResult defaults are "".
- vgpr = None before the only assignment that sets it.
- isinstance(bench_result, dict) on both IterationResult paths: every producer
  here returns a dict (reuse copies one; measure_wallclock returns one).
- the duration alias of time.time() - iter_start.
- the Step N / profiling / variance-bar narrating comments.
- the nested validate status ternary: one table lookup, _VALIDATE_FAIL_STATUS.

Helpers: _emit_validation_stages, _iteration_bench_result, _emit_bench_scores,
_decide_iteration_keep. Side-effect order on the success path is unchanged
(observe after bench; score print before registers; canonical observe before
the failure return).

206 -> 66 lines, cognitive 39 -> 6.

Co-authored-by: Cursor <cursoragent@cursor.com>
run_one_iteration mixed build, validation printing, bench reuse, scoring
prints, registers, the KEEP bar and the assembly canonical suite. Those
are now named jobs, and three mechanisms are gone:

- the nested PASS/TIMEOUT/ERROR/FAIL ternary: one _VALIDATE_FAIL_STATUS
  table plus _emit_validation_stages
- the empty pmc_diagnosis / pmc_full locals: IterationResult already
  defaults both to ""
- the isinstance(bench_result, dict) guards on bench_detail: both the
  reused in-session path and measure_wallclock return a dict

_decide_iteration_keep still captures the bench-failure tail before
_apply_mean_case_speedup_metric, which may rewrite success and message.

206 -> 57 lines (cognitive 39 -> 5). Helpers each under the gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
kb_warmstart inlined reading the ranked field, the dirty-workspace
reference-only return, pristine measurement, adoption, and the success
envelope. Those jobs now live on _WarmStartAttempt:

- load_solutions: read_top_solutions plus the claimed-speedup floor filter
- dirty_result / empty_result: the two early returns that clear or reject
  the reference ledger
- measure_and_adopt: pristine bench, field measurement, and winner adopt
- final_result: the success envelope the loop persists

Also deleted the isinstance(pristine_bench, (int, float)) branch when
reading median_ms: aggregate_benchmark_measurements always returns a dict
(evaluation/benchmark/runner.py:157).

Cognitive 54 -> 6, 234 -> 44 lines.

Co-authored-by: Cursor <cursoragent@cursor.com>
_WarmStartAttempt.workspace_dir / applied / applied_rank were one-line
properties counted as cyclomatic 1 each. empty_result and dirty_result were
straight-line returns. Inlining them (and the pristine_ms ternary into the
existing None branch) brings the three-file C901 total back to the base
1198 while cognitive stays lower. run_one_iteration also takes back the
validate-print and bench-obtain steps that were pure relocation.

Co-authored-by: Cursor <cursoragent@cursor.com>
The seven over-gate functions in kf-loop-b are within every limit they
broke. Removes their cognitive-complexity and function-lines baseline
entries, and lowers knowledge_integration.py module-lines 1457 -> 1455.

Co-authored-by: Cursor <cursoragent@cursor.com>
Delete the kernel/driver/program_md aliases, the redundant list() copy of
parsed targets and the str() wrap around gpu_type. The clean-branch gate and
the program.md digest each name one job, so create_campaign_config stays under
the line limit without re-checking inputs its callers already hold, and its
validation errors are raised in the same order as before.

Co-authored-by: Cursor <cursoragent@cursor.com>
… aliases

Delete the getattr guard on archive (always set by _initialize_campaign_runtime),
the unused actor unpacking in session setup, the best_before locals that only
fed replace(), and the narrating comments that restated what the call sequence
already says. Session open and attempt settlement each name one job so
_run_locked stays under every gate limit.

Co-authored-by: Cursor <cursoragent@cursor.com>
…elpers

Test the workspace branch against one set, drop the state_exists gate on
stale-session reconcile (a fresh RunState is never SESSION_RUNNING), build the
benchmark_measurement keyword inline so a None measurement still leaves it off
the run_one_iteration call, and always record planning state the way
_abandon_iteration already does.

Co-authored-by: Cursor <cursoragent@cursor.com>
…e gate

create_campaign_config and IterationLoop._run_locked are within every gate
limit after the deletion-first cleanup.

Co-authored-by: Cursor <cursoragent@cursor.com>
Put back the comments that state ordering, rebuild, scoring, noise and budget rules the code cannot show; step
numbers, section banners and comments that only restate the next line stay deleted. The KEEP bar and the round
pricing move into _clears_keep_bar and _price_round, whose docstrings carry their rules, _settle_locked_attempt
reads the session sink from its frame, and the in-session gate's verdict strings are rewrapped unchanged, so every
changed function stays within 80 lines and no module ends longer than it was.

Co-authored-by: Cursor <cursoragent@cursor.com>
@fengshaoyi-amd fengshaoyi-amd added the size-exception Waives the PR diff budget; added by a maintainer with a reason in the PR description label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Code metrics gate: PASSED

Judged: the 10 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.

Dimension Limit New Worse Baseline out of date Baseline entries vs base
Cyclomatic complexity fails > 20 0 0 0 22 +0
Cognitive complexity fails > 30 0 0 0 111 -5
Function length (lines) fails > 80 0 0 0 255 -7
Nested block depth fails > 5 0 0 0 13 +0
Parameter count fails > 5 0 0 0 88 +0
Module length (lines) warns > 800, fails > 1200 0 0 0 47 +0
Duplicated lines per file fails > 0 0 0 0 95 +0
Dead code (vulture) fails > 0 0 0 0 2 +0
Check Findings Effect
Comments 0
English only 0
Production code imports test code 0
Repeated literals 0
A1 Private name of another package 0
A8 Flag argument 0
A9 Tuple return 0
A10 Raw vocabulary comparison 0
A11 Hidden global write 0
A12 Pure forwarder 0
A12 Single-implementation abstraction 0

Already over at the base (backlog, not this change's): 10

Unit Dimension Base Now
src/kernelforge/loop/runner.py:5788 IterationLoop._collect_session_candidate Cognitive complexity 44 44
src/kernelforge/loop/runner.py:5391 IterationLoop._rule_on_attempt Cognitive complexity 38 38
src/kernelforge/loop/runner.py:5494 IterationLoop._close_iteration Function length (lines) 117 117
src/kernelforge/loop/runner.py:5788 IterationLoop._collect_session_candidate Function length (lines) 185 185
src/kernelforge/loop/runner.py:6016 IterationLoop._implementer_history Function length (lines) 112 112
src/kernelforge/loop/runner.py:5391 IterationLoop._rule_on_attempt Function length (lines) 102 102
src/kernelforge/loop/runner.py:5612 IterationLoop._source_attempt Function length (lines) 175 175
src/kernelforge/loop/insession_gate.py:1 Module length (lines) 1270 1260
src/kernelforge/loop/knowledge_integration.py:1 Module length (lines) 1460 1459
src/kernelforge/loop/runner.py:1 Module length (lines) 6545 6510
72 findings in files this change does not touch

Outside this change (informational): 72

Unit Dimension Baseline Now
src/hyperloom/orchestrator/state/shared_state.py resolve_graded_comparison Cognitive complexity absent 34
src/hyperloom_kb/collect/mapping.py _experience Cognitive complexity absent 31
src/kernelforge/cli/forge_loop_command.py forge_loop Cognitive complexity absent 31
src/hyperloom/inference_optimizer/cli/__init__.py _reexport_session_environment Function length (lines) absent 107
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_agent_lineup Function length (lines) absent 89
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_run_topology Function length (lines) absent 92
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _build_parser Function length (lines) absent 84
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _spawn_rank_actors Function length (lines) absent 84
src/hyperloom_kb/sync.py GlobalSync.pull Function length (lines) absent 84
src/hyperloom_kb/http_service.py Module length (lines) absent 1525
src/kernelforge/cli/forge_loop_command.py Module length (lines) absent 2003
src/hyperloom/orchestrator/actions/executors/_geak_sweep.py sweep_via_geak Cognitive complexity 67 70
src/hyperloom/orchestrator/actions/executors/_grid_runner.py run_grid Cognitive complexity 177 180
src/hyperloom/orchestrator/actions/executors/explore.py ExploreExecutor.__call__ Cognitive complexity 369 383
src/hyperloom/orchestrator/phases/kernel.py KernelPhase._run_geak_kernel_phase Cognitive complexity 122 130
src/hyperloom/orchestrator/specialists/dispatch.py SpecialistDispatchCollaborator.warm_specialist_params Cognitive complexity 93 94
src/hyperloom/orchestrator/actions/executors/_grid_runner.py run_grid Cyclomatic complexity 55 56
src/hyperloom/orchestrator/actions/executors/explore.py ExploreExecutor.__call__ Cyclomatic complexity 76 83
src/hyperloom/orchestrator/phases/kernel.py KernelPhase._run_geak_kernel_phase Cyclomatic complexity 49 52
src/hyperloom/agents/quantization/driver/retry.py quantize_via_prompt Function length (lines) 102 103
src/hyperloom/inference_optimizer/breakdown/exporter.py build Function length (lines) 132 136
src/hyperloom/inference_optimizer/cli/__init__.py _run_optimize Function length (lines) 273 279
src/hyperloom/inference_optimizer/cli/kb.py _bootstrap_recipe_kb Function length (lines) 91 92
src/hyperloom/inference_optimizer/cli/preflight.py _preflight Function length (lines) 203 208
src/hyperloom/inference_optimizer/multi_node/scripts/apply_tracelens_patch_multinode.py _apply_on_pod Function length (lines) 101 102

...and 47 more; the job log lists every row.

Verdict computed by the gate scripts in /home/runner/work/_temp/code-metrics-gate, not by this tree's copy.

Thresholds and sources: [tool.hyperloom.code_metrics] in pyproject.toml. Tools: complexipy 8.0.1, jscpd 5.4.1, ruff 0.16.2, vulture 2.16.

…loop-bd-pr

Correction to 2430053 (reconcile a best publication without unused pending fields): the
three-line comment above describes_current_best is not narration. It states why that
short-circuit exists - a resumed session recomputes session_index and experiment_id, so
republishing an already-current best would trip the same-iteration conflict guard and report
persistence_degraded over a correct bundle - and 15c40c0 restored it unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>

@ZhengGong-amd ZhengGong-amd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please reconsider these points before merge.

  1. src/kernelforge/loop/insession_gate.py:1103-1145: the reset of last_bench_result was removed on the claim that every path sets it, but on case-coverage failure (:1127) or a bench exception it keeps the previous stop's value (base: None, head: stale result). KEEP is unaffected today only because runner.py:5964 also requires gate_passed.
    Action: reset the last_* attributes once at the start of the verdict and drop the per-branch resets; fix the claim in the commit/description.
  2. src/kernelforge/loop/runner.py:2517-2523 _reconcile_best_publication: a failed publication diff on resume used to abort (CalledProcessError); it is now caught, marked persistence_degraded with a warning, and resume continues. The direction is fine, but the description says exception behaviour is unchanged. AGENTS.md Every change states its observable effect.
    Action: list this change in the description and add a test that pins it.
  3. src/kernelforge/loop/runner.py:316-320 _VALIDATE_FAIL_STATUS duplicates _status_label in src/kernelforge/evaluation/correctness/pipeline.py:103-110. AGENTS.md Clean design.
    Action: make status_label public and call it; delete the new table.

Also: this PR conflicts with #2083 in runner.py (6 large hunks); please agree on merge order.

fengshaoyi-amd and others added 4 commits October 21, 2026 19:20
Reuse correctness status labels and pin degraded best-publication reconciliation on resume.

Co-authored-by: Cursor <cursoragent@cursor.com>
…loop-bd-pr

Co-authored-by: Cursor <cursoragent@cursor.com>
The standalone status_label(outcome, passed) branched on a flag; the result owns both fields, so the pipeline and the runner now ask it.

Co-authored-by: Cursor <cursoragent@cursor.com>
…loop-bd-pr

Co-authored-by: Cursor <cursoragent@cursor.com>
@fengshaoyi-amd

Copy link
Copy Markdown
Contributor Author

Addressed the review.

  1. _stop_performance_verdict now clears last_* once at the start of the verdict; coverage failure and bench exception no longer keep a stale bench result. Tests cover both paths plus the scoring-unavailable wall retention.
  2. Resume publication-diff failure now degrades persistence without propagating; added a test with a real base→descendant best commit and a GitError at patch generation. PR description will spell that out.
  3. Did not add a public status_label(outcome, passed): A8 rejects a public function that branches on a boolean flag. ValidationResult now owns a status_label property; pipeline and runner ask the result. The old _VALIDATE_FAIL_STATUS is gone.

Merge order vs #2083: prefer landing this PR first (reviewed, behaviour fixes), then re-splitting #2083 on the new main — it is move-only / AST-equivalent and already conflicts in runner.py.

@fengshaoyi-amd
fengshaoyi-amd requested review from ZhengGong-amd and removed request for ZhengGong-amd October 22, 2026 02:19
Resolve scripts/code_metrics_baseline.txt by key: keep main's removals and
this branch's removals; no entry was changed on both sides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verified at f64dd4a: last_* are reset at the start of the stop verdict, the resume publication-diff degradation is pinned by a real-git test, and the duplicated status table is replaced by ValidationResult.status_label. I merged origin/main into the branch (the conflict was only scripts/code_metrics_baseline.txt, resolved by key: both sides' removals kept, no entry changed on both sides); code-metrics and every other check are green on the merge.

Please refresh the PR description against this head (counts, commit table and the statements the reviewers listed): the release notes are built from it.

LGTM

@chaojhou
chaojhou merged commit 270158c into main Oct 22, 2026
40 checks passed
@chaojhou
chaojhou deleted the lean/shafeng/cc20-kf-loop-bd-pr branch October 22, 2026 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size-exception Waives the PR diff budget; added by a maintainer with a reason in the PR description

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants