Repository navigation
Conversation
Code metrics gate: PASSED with waived findings (
|
| Dimension | Limit | New | Worse | Baseline out of date | Baseline entries | vs base |
|---|---|---|---|---|---|---|
| Cyclomatic complexity | fails > 20 | 0 | 0 | 0 | 6 | +0 |
| Cognitive complexity | fails > 30 | 0 | 0 | 0 | 54 | +0 |
| Function length (lines) | fails > 50 | 0 | 7 | 0 | 544 | +0 |
| Nested block depth | fails > 4 | 0 | 0 | 0 | 44 | +0 |
| Parameter count | fails > 5 | 0 | 0 | 0 | 80 | +0 |
| Module length (lines) | warns > 800, fails > 1200 | 1 | 3 | 0 | 44 | +0 |
| Duplicated lines per file | fails > 0 | 0 | 0 | 0 | 95 | +0 |
| Dead code (vulture) | fails > 0 | 0 | 0 | 0 | 2 | +0 |
| Directory size (entries) | fails > 50 | 0 | 0 | 0 | 2 | +0 |
| Check | Findings | Effect |
|---|---|---|
| Comments | 0 | |
| Broad catch without a reason | 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 |
New violations (not in the baseline) (waived by the baseline-raise label): 1
| Unit | Dimension | Value | Limit |
|---|---|---|---|
src/kernelforge/cli/forge_loop_command.py:1 |
Module length (lines) | 2026 | fails > 1200 |
Worse than the baseline (waived by the baseline-raise label): 10
| Unit | Dimension | Baseline | Now |
|---|---|---|---|
src/kernelforge/cli/forge_loop_command.py:1512 forge_loop |
Function length (lines) | 421 | 423 |
src/kernelforge/config.py:300 Config.from_env |
Function length (lines) | 67 | 69 |
src/kernelforge/orchestrator/agent.py:397 make_agent_fn |
Function length (lines) | 263 | 266 |
src/kernelforge/orchestrator/analysis.py:1311 AnalysisAgentService._run_analysis_session |
Function length (lines) | 81 | 83 |
src/kernelforge/orchestrator/orchestration.py:1769 make_orchestration_service |
Function length (lines) | 63 | 66 |
src/kernelforge/orchestrator/plan_critic.py:408 PlanCriticAgent.review |
Function length (lines) | 66 | 68 |
src/kernelforge/orchestrator/specialists.py:475 SpecialistAgent.run |
Function length (lines) | 95 | 98 |
src/kernelforge/loop/runner.py:1 |
Module length (lines) | 6051 | 6554 |
src/kernelforge/orchestrator/analysis.py:1 |
Module length (lines) | 1952 | 1957 |
src/kernelforge/orchestrator/orchestration.py:1 |
Module length (lines) | 1831 | 1843 |
How to fix
- New or worse: split the unit (extract a function, a class, a module) until it is back under the limit, or no worse than its recorded value. The baseline cannot absorb a new violation: CI refuses any baseline entry added or raised relative to the base branch.
- Baseline out of date: something improved. Run
python scripts/code_metrics.py --update-baselineand commitscripts/code_metrics_baseline.txt; the command only lowers or removes entries. - Override: the
baseline-raisePR label waives new, worse and baseline-growth findings and the failing design checks (they stay listed here). Use it only with the reason in the PR description; it waives nothing else. - Tools for a local run:
pip install ruff==0.16.2 complexipy==8.0.1 vulture==2.16andnpm install -g jscpd@5.4.1(or pointCODE_METRICS_JSCPDat another jscpd command, such asnpx --yes jscpd@<version>).
Module length warning: over 800 lines, within the 1200-line failure limit (not failing): 1
| Module | Lines | Limit |
|---|---|---|
src/kernelforge/orchestrator/agent.py |
964 | warns > 800 |
Already over at the base (backlog, not this change's): 3
| Unit | Dimension | Base | Now |
|---|---|---|---|
src/kernelforge/cli/forge_loop_command.py:1512 forge_loop |
Cognitive complexity | 31 | 31 |
src/kernelforge/loop/runner.py:5832 IterationLoop._collect_session_candidate |
Cognitive complexity | 44 | 44 |
src/kernelforge/loop/runner.py:5441 IterationLoop._rule_on_attempt |
Cognitive complexity | 38 | 38 |
24 findings in files this change does not touch
Outside this change (informational): 24
| Unit | Dimension | Baseline | Now |
|---|---|---|---|
src/hyperloom/orchestrator/actions/executors/_integrate_apply.py ApplyStageMixin._stage_apply |
Cognitive complexity | absent | 32 |
src/hyperloom_kb/collect/mapping.py _experience |
Cognitive complexity | absent | 31 |
src/hyperloom/orchestrator/loop/_keep_patch_column.py AgentKeepStagingMixin._stage_agent_keep |
Parameter count | absent | 6 |
src/hyperloom/orchestrator/phases/kernel.py KernelPhase._record_geak_delegation_timeline |
Parameter count | absent | 7 |
src/hyperloom/orchestrator/loop/_baseline_failures.py BaselineFailureMixin.handle_unpromotable_result |
Function length (lines) | absent | 55 |
src/hyperloom/orchestrator/loop/_resume_recovery.py ResumeRecoveryMixin._resume_recover_pending_warm_replay |
Function length (lines) | absent | 75 |
src/hyperloom_kb/http_service.py |
Module length (lines) | absent | 1525 |
src/hyperloom/orchestrator/specialists/dispatch.py SpecialistDispatchCollaborator.warm_specialist_params |
Cognitive complexity | 93 | 94 |
src/hyperloom/inference_optimizer/cli/preflight.py _probe_rocm_build |
Function length (lines) | 54 | 57 |
src/hyperloom/inference_optimizer/breakdown/schema.py |
Module length (lines) | 2681 | 2686 |
src/hyperloom/inference_optimizer/cli/__init__.py |
Module length (lines) | 2569 | 2739 |
src/hyperloom/orchestrator/actions/executors/_kv_metrics.py |
Module length (lines) | 1578 | 1579 |
src/hyperloom/orchestrator/actions/executors/roofline.py |
Module length (lines) | 1303 | 1305 |
src/hyperloom/orchestrator/kernel/conc_sweep.py |
Module length (lines) | 1429 | 1449 |
src/hyperloom/orchestrator/phases/kernel.py |
Module length (lines) | 4102 | 4199 |
src/hyperloom/orchestrator/phases/prelude.py |
Module length (lines) | 2504 | 2506 |
src/hyperloom/orchestrator/prompts/prompt_builder.py |
Module length (lines) | 1205 | 1212 |
src/hyperloom/orchestrator/prompts/specialist_prompt_builder.py |
Module length (lines) | 2556 | 2567 |
src/kernelforge/fusion/command.py |
Module length (lines) | 1584 | 1708 |
src/kernelforge/fusion/validate.py |
Module length (lines) | 1513 | 1514 |
src/kernelforge/gemm_tune/tuners/_aiter_dense_common.py |
Module length (lines) | 1399 | 1463 |
src/kernelforge/loop/insession_gate.py |
Module length (lines) | 1218 | 1260 |
src/kernelforge/loop/knowledge_integration.py |
Module length (lines) | 1457 | 1459 |
src/kernelforge/loop/task_preparer.py |
Module length (lines) | 1299 | 1557 |
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.
Hyperloom Forge E2E — ✅ Succeeded
|
|
The two Forge E2E checks ( |
727921f to
5821319
Compare
|
/review |
|
Automated review started for head 5821319. Progress: https://github.com/AMD-AGI/Hyperloom/actions/runs/39652856224 |
There was a problem hiding this comment.
PR #2034 -- feat(kernelforge): offer a configured knowledge base to every forge role and record why each step was taken
What it does: Gives every tool-bearing forge role (Implementer, Analysis, dispatch/synthesis planners, specialists, plan critic, supervisor) read-only access to a configured KernelWiki knowledge base as four stdio MCP tools -- off unless FORGE_KNOWLEDGE_WIKI_ROOT is set, refused on unusable settings or a backend without MCP, every call logged with its role and a mandatory one-line reason -- and records each iteration's decision rationale on the iteration_result event, with a fan-out lane keeping its own rationale through the queue; a declared target may now escape the test-name protection patterns, and --target-mean-case-speedup stops a campaign once the incumbent's mean per-case speedup over the baseline reaches the operator's target (termination reason speedup_target_met).
Blocking issues: 1
- [X3] The documented refusal band for
--target-mean-case-speedupis not enforced below zero [verified]
Problem: Four places this PR adds state "a value at or below 1.0 is refused" -- the cli.md row (docs/kernelforge/reference/cli.md:118), the option's--helptext (src/kernelforge/cli/forge_loop_command.py:1369), the validator's own docstring (src/kernelforge/cli/options.py:124-126) and PR body item 4 -- but_validate_speedup_targetreturns 0.0 without raising for every value <= 0 (src/kernelforge/cli/options.py:129-130), silently disabling the gate.
Impact: An operator passing a negative value (e.g.--target-mean-case-speedup -3.15, one keystroke away from3.15) gets a campaign with no speedup stop and no refusal message -- exactly the "campaign quietly ignoring the limit its operator thought they had set" failure that the roofline sibling's refusal exists to prevent (options.py:100-104).
Action: Raise for values below zero (keeping 0.0 as the documented off default), or narrow the four doc claims to the band the code enforces.
SKIPPED: V4 -- "# PARTIAL: #1276 changes 1311 files, only its first 100 were compared": overlap with #1276's remaining files unchecked.
SKIPPED: V4 -- "# PARTIAL: #1032 changes 1075 files, only its first 100 were compared": overlap with #1032's remaining files unchecked.
SKIPPED: V4 -- "# PARTIAL: #1869 changes 540 files, only its first 100 were compared": overlap with #1869's remaining files unchecked.
SKIPPED: V4 -- "# PARTIAL: #1154 changes 1232 files, only its first 100 were compared": overlap with #1154's remaining files unchecked.
Checked: knowledge/wiki_tool/{init,contract,mcp_server,tool}.py against tools/mcp_stdio.py (serve, dispatch_envelope, InvalidParamsError) and agent_backends/base.py (StdioMcpServer fields, AgentCapabilities.mcp; claude.py:379, codex.py:331); the six role attach sites (agent.py:540, analysis.py:576, orchestration.py:1162/1794, plan_critic.py:423, specialists.py:510, supervisor.py:254) and supervisor.py's except path; runner.py _source_attempt branches, _stop_condition_met, _iteration_result_event, _persist/_restore_lane_queue, and the agent_rationale threading (pre-existing at base runner.py:694/5937/6453); workspace_policy.py PROTECTED_GLOBS vs TARGET_RECLAIMABLE_GLOBS and the pre-existing workspace_guard._target_exempt (base:125); iteration_result readers (run_state.py:705/727, prompt_view.py:135, reporting.py:537) and termination_reason readers tree-wide; .env.template scope; docs rows against contract.py SCRIPTS; the body's 41-new-tests and 452-package-lines claims against counted diff lines and the unit table against wc -l; tree-wide greps for every new identifier's writers and readers | Ran: _validate_speedup_target(-2.0/0.0/0.5) and _validate_roofline_target(-2.0) at head; pytest at head: test_workspace_policy.py (7), test_forge_loop_resume.py -k speedup (3), test_loop_runner.py -k "rationale or speedup_target or declared_target_named" (6), test_knowledge_wiki_tool.py (46, the stdio-session test run separately with an absolute PYTHONPATH for the child env), test_fanout.py -k rationale (1), test_lane_plan_persistence.py -k rationale (2), test_insession_gate_protection.py -k colocated (1), tests/loop/test_knowledge_integration_helpers.py -k named_like (1) -- all passed | Base: bb2da63 | Head: 5821319
|
/review |
|
Automated review started for head 7e2fdcd. Progress: https://github.com/AMD-AGI/Hyperloom/actions/runs/39660361084 |
There was a problem hiding this comment.
PR #2034 -- feat(kernelforge): offer a configured knowledge base to every forge role and record why each step was taken
What it does: The forge planning roles run without a shell, so a kernel knowledge base mounted as a checkout never reached the agents that choose the optimization direction, and a run's trajectory artifacts recorded no reason for any step. This PR serves that checkout to every tool-bearing forge role (Implementer, Analysis, dispatch/synthesis planners, specialists, plan critic, supervisor) as four read-only stdio MCP tools (mcp__knowledge_wiki__{search,page,grep,guide}), each call logged with its role and a mandatory one-line reason; it is off unless FORGE_KNOWLEDGE_WIKI_ROOT is set, and unusable settings and backends without MCP support are refused rather than run with the tools silently missing. It also records each iteration's decision rationale on the iteration_result event (a fan-out lane keeps its own rationale through the lane queue), lets a declared target escape the test-name protection patterns so a ROCmBench kernel colocated with its tests is editable again, and adds --target-mean-case-speedup, which ends a campaign once the incumbent's mean per-case speedup over the baseline reaches the operator's target (termination reason speedup_target_met).
Blocking issues: 1
- [X3] The comment on the new
iteration_result.rationalefield states absence conditions no production path produces [verified]
Problem: The added comment says the field is "absent when no agent ran (baseline) or it returned nothing" (src/kernelforge/loop/runner.py:3834), but every producer ofresult.agent_rationaleyields a non-empty string: the no-agent branch assignsrationale = "no-agent (baseline measurement)"(src/kernelforge/loop/runner.py:5801), which reachesresult.agent_rationale = rationale(src/kernelforge/loop/runner.py:6487) and the event emitted at src/kernelforge/loop/runner.py:3769, and the implementer channel returns(gate tag + (full or "no rationale provided"))[:200](src/kernelforge/orchestrator/agent.py:903-923), so an agent whose final message was empty also emits a present field carrying that placeholder; the device-held, queued-lane and staged-merge branches likewise assign their own non-empty rationale. The only path that leaves the field absent is a pending keep persisted before the field existed, restored asstr(pending.get("rationale") or "")(src/kernelforge/loop/runner.py:1793).
Impact: The comment is the only statement of the new persisted field's presence semantics, and it is inverted on both of its clauses -- a reader of the events JSONL (the trajectory comparison this PR exists to enable, and the #2036 wiki-guided planning mode stacked on it) expects a missingrationalekey on baseline and empty-message rows and finds placeholder strings instead, so any consumer branching on absence misreads those rows.
Action: Correct the comment to the real condition (present, with a placeholder, on every production path; absent only for a pre-rationale pending keep or an injected agent_fn returning nothing), or make the no-agent and empty-final-message paths leaveagent_rationaleunset if absence is the intended contract.
SKIPPED: V4 -- "# PARTIAL: #1276 changes 1311 files, only its first 100 were compared": overlap with #1276's remaining files unchecked.
SKIPPED: V4 -- "# PARTIAL: #1032 changes 1075 files, only its first 100 were compared": overlap with #1032's remaining files unchecked.
SKIPPED: V4 -- "# PARTIAL: #1869 changes 540 files, only its first 100 were compared": overlap with #1869's remaining files unchecked.
SKIPPED: V4 -- "# PARTIAL: #1154 changes 1232 files, only its first 100 were compared": overlap with #1154's remaining files unchecked.
Checked: knowledge/wiki_tool/{init,contract,mcp_server,tool}.py (handle_tool_call/_run/_log/script_arguments, entry argv/env, tool names) against tools/mcp_stdio.py (serve, dispatch_envelope, InvalidParamsError) and agent_backends/base.py (StdioMcpServer fields, AgentCapabilities.mcp), claude.py:_apply_mcp_servers and codex.py:_mcp_server_overrides; the six role attach sites and require_mcp_backend refusals (agent.py:540, analysis.py:576, orchestration.py:1162/1794, plan_critic.py:387, specialists.py:461, supervisor.py:254 and its except path); cli/options.py:_validate_speedup_target beside _validate_roofline_target; runner.py IterationConfig, _stop_condition_met, _is_speedup_target_met, _iteration_result_event and the agent_rationale producers (the _source_attempt branch chain 5675-5801, result assignment sites 5971/6487, pending-keep restore 1793), _persist/_restore_lane_queue, _lane_rejection, _target_source_files, and the base's agent_rationale threading (base runner.py:694/5937/6453/6524) and placeholder return (base agent.py:895-915); the gate tag at agent.py:315-324; fanout.py:_one's error branch; llm/workspace_policy.py TARGET_RECLAIMABLE_GLOBS vs PROTECTED_GLOBS/PROTECTED_DIRS and every editable_paths consumer, beside the pre-existing workspace_guard._target_exempt (base:125) and runner.py:1266 commit gate; iteration_result readers (run_state.py:705/727, prompt_view.py:135, reporting.py:537); .env.template scope; docs rows against contract.py SCRIPTS; the body's size table against numstat and the code-metrics report at head; tree-wide greps for every new identifier's writers and readers | Ran: at head 7e2fdcd -- pytest test_forge_loop_resume.py -k speedup (7 passed), test_knowledge_wiki_tool.py (46 passed, including a real stdio session), test_loop_runner.py -k "rationale or speedup_target or declared_target" (6 passed), test_fanout.py + test_lane_plan_persistence.py -k rationale (3 passed), test_workspace_policy.py + test_insession_gate_protection.py -k "declared_target or colocated or escapes or inventory" (6 passed), a six-module selection -k named_like (2 passed, both this PR's new tests); ruff --select C901 (max-complexity 20) and --select PLR0913 (max-args 5) over the changed non-test files -- both pass | Base: bb2da63 | Head: 7e2fdcd
--target-mean-case-speedup returned 0.0, the off value, for every target at or below zero, so a mistyped -3.15 ran the campaign with no speedup stop and no message, although the help, the reference and the validator all said a value at or below 1.0 is refused. Zero alone now turns the target off; any other value at or below 1.0, and one that is not finite and so can never be reached, is refused. Co-authored-by: Cursor <cursoragent@cursor.com>
The comment beside the new field said it was absent when no agent ran
or the agent returned nothing, but both paths record a placeholder
("no-agent (baseline measurement)", "no rationale provided"), as do the
held-device, queued-lane and merge paths. The key is omitted only for a
pending KEEP recorded before the field existed; the comment now says so.
Co-authored-by: Cursor <cursoragent@cursor.com>
The escape released the test-name patterns for the whole declared file, and every caller decides on the whole file. In a ROCmBench layout that file also holds the correctness tests, so an Implementer could rewrite the in-file reference and tolerance together with the kernel: the in-session gate allowed it, the file left the tamper snapshot, the lane check passed it, and the canonical suite ran the tampered tests, so a wrong kernel could be kept and reported as a speedup. Protecting only what lies outside the declared kernel symbols is its own change; until then a test-named file stays protected, as at the base. This reverts commit 7e9005c. Co-authored-by: Cursor <cursoragent@cursor.com>
The prompt asks the Implementer for a rationale specific enough to compare runs by, but agent_fn prefixed the in-session gate tag (about 94 characters) and returned the first 200, so about 106 characters of the agent's reasoning reached iteration_result and the lane queue. agent_fn now returns the agent's own words in full; every reader already caps what it records (the event at 500 characters, the history line at 80, the commit subject at 72). The gate's figures stay where they already were: the session sink and the session-end log line. A KEEP's commit subject now starts with the rationale rather than the gate tag. Co-authored-by: Cursor <cursoragent@cursor.com>
The orchestration-failure branch built its IterationResult without one, so the iteration_result of an ORCHESTRATION_ERROR iteration carried no rationale key, against the field's stated contract. It now records "planning failed; no Implementer session: <error>", as the held-device, merge and no-agent paths record theirs. Co-authored-by: Cursor <cursoragent@cursor.com>
page relied on the checkout's script for containment, and KernelWiki's get_page.py reads a lookup containing a slash as a path under its root with no check, so page_id="/etc/hostname" or "../../../etc/hostname" returned the file to a planning role that has no shell. The server now refuses an absolute page path or one that resolves outside the checkout before the script runs. The call log records only the arguments each tool declares, so an extra key cannot bloat a line and the reason is logged once. mcp_stdio's docstring names the third server it carries. Co-authored-by: Cursor <cursoragent@cursor.com>
agent_fn returned run_result.text as the rationale. Codex reports its
last message there, but Claude reports every text block of the session
joined by newlines, so on Claude, the default backend, every reader's
leading cut recorded the session's opening remarks ("I'll start by
reading the kernel...") and none of the final reasoning; the gate prompt,
which forge-loop always uses, never asked for a rationale at all.
Both Implementer prompts now ask for one `RATIONALE:` line just before
the `PLAN:` line, and the gate's resume feedback asks for both again.
agent_fn parses it as PLAN is parsed (the last such line, one line,
capped at the 500 characters the iteration_result event records) and
records "no rationale provided" when there is none.
Co-authored-by: Cursor <cursoragent@cursor.com>
A resume compares the subject git recorded for a pending KEEP's commit with the first line of the message it built. git folds a multi-line first paragraph into one subject and trims trailing whitespace, so a rationale with a newline in its first 72 characters, or one whose cut ended on a space, made the resume refuse the campaign with "pending KEEP commit message mismatch". The gate tag hid this while it led every rationale. The message is now built as one line with no trailing space, and that line is the expected subject. Co-authored-by: Cursor <cursoragent@cursor.com>
main added the DeepSeek Harness backend (#2063), which declares no MCP support, so a campaign that sets FORGE_KNOWLEDGE_WIKI_ROOT refuses it as it refuses any backend that would drop the tools. The variable's entry named only the backends that do support MCP. Co-authored-by: Cursor <cursoragent@cursor.com>
KnowledgeWikiTool.from_config and require_mcp_backend read typed Config fields and AgentBackend attributes through getattr defaults, which only test doubles without those fields needed: with plain attributes, 18 Analysis and orchestration-factory tests failed on a SimpleNamespace config. Both now read the attributes, and those doubles carry the two fields, as main already did for the probe fields. require_mcp_backend also takes a registered AgentProvider, which declares the same name and capabilities. The four variables a `python -m kernelforge...` MCP server needs to start were listed twice, once for the probe server and once for the knowledge base server; they are now one PYTHON_LAUNCH_ENV_VARS beside StdioMcpServer, and each server adds only what its own child needs. Co-authored-by: Cursor <cursoragent@cursor.com>
The supervisor builds its backend on its first pass, so the MCP check ran there, inside the pass's best-effort handler: with a knowledge base set and a provider without MCP, every pass was skipped with a log line and the campaign ran on without a supervisor. The provider registry declares each provider's capabilities, so make_supervisor_fn now refuses such a provider when it is built, as every other role is refused at startup. Co-authored-by: Cursor <cursoragent@cursor.com>
The server re-derived whether its root was a knowledge base checkout and answered every call with an error when it was not, but only the session side launches it, always with the root, log and role set, and Config refuses a root without the scripts before any session starts. The server now reads the three variables directly, and a missing script fails the call through the script's own exit status. It also builds a call's script arguments once instead of validating and then rebuilding them. Co-authored-by: Cursor <cursoragent@cursor.com>
agent_fn capped the declared rationale at 500 characters and the iteration_result event cut it at a separate literal 500, held together by a comment. The event now uses agent.RATIONALE_MAX_CHARS. The speedup target check also stops re-coercing a float field the CLI has already validated, and _lane_rationale names the LanePlan it reads. Co-authored-by: Cursor <cursoragent@cursor.com>
The supervisor builds its backend on its first pass, and create_registered_backend returns the fallback provider's backend when the primary is unavailable. The startup check read only the primary provider's declared capabilities, so with a knowledge base set, codex as the primary and deepseek as the fallback, a host without codex ran every supervisor pass on deepseek with the knowledge base tools silently dropped. require_mcp_runtime now refuses a runtime when its provider or its fallback declares no MCP, and make_supervisor_fn uses it, so the campaign is refused at startup instead. Co-authored-by: Cursor <cursoragent@cursor.com>
… own main now fails a function over 50 lines, and this change had grown Config.__post_init__ and IterationLoop._stop_condition_met to 55 each. The knowledge base path checks move to Config._normalize_knowledge_wiki_paths, and the speedup target's stop to _stop_at_speedup_target, which sets the termination reason itself and replaces the _is_speedup_target_met query. Behaviour is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
main's function-length count records IterationLoop._source_attempt at 148 and make_agent_fn.agent_fn at 142; this change shortens them to 142 and 140, so their entries are lowered to match. Co-authored-by: Cursor <cursoragent@cursor.com>
d183e78 to
6a154d0
Compare
|
The review on 523cda3 is addressed at head 6a154d0, rebased onto main d0d690b. Blocking: the supervisor fallback (fixed in c1cd81d). I took a variant of the second option.
Also in this push, after the rebase onto main's stricter function-length rule (#2127):
At 6a154d0, these all pass:
CI and e2e will run on this push. |
|
/review |
|
Automated review started for head 6a154d0. Progress: https://github.com/AMD-AGI/Hyperloom/actions/runs/40002707111 |
KnowledgeWikiServer read the three launch variables from os.environ unless handed a dict, and only the tests ever handed one. It now takes root, log_path and role, and _serve, the module's entry point, reads them from the environment the session side launches it with. Co-authored-by: Cursor <cursoragent@cursor.com>
|
On the heads-up of 10-23 12:04, every point holds at head 5e83f0d. The branch sits on main d0d690b and is 0 commits behind.
|
|
Automated review: no verdict applied -- the PR head moved from 6a154d0 to 5e83f0d during the review. Comment PR #2034 -- feat(kernelforge): offer a configured knowledge base to every forge role and record why each step was takenWhat it does: The forge planning roles run their sessions without a shell, so a kernel knowledge base laid out as a checkout never reached the agents that choose the optimization direction, and a run left no record of why any step was taken. This PR serves that checkout to every tool-bearing forge role (Implementer, Analysis, the dispatch and synthesis planners, the specialists, the plan critic, the supervisor) as four read-only stdio MCP tools ( Blocking issues: none SKIPPED: V4 -- "# PARTIAL: #1869 changes 695 files, only its first 100 were compared": overlap with #1869's remaining files unchecked. Checked: knowledge/wiki_tool/{init,contract,mcp_server,tool}.py (handle_tool_call/_run/_log/script_arguments/_require_inside_root/_ARGUMENT_KEYS, TOOL_DEFINITIONS, serve entry) against tools/mcp_stdio.py (serve, dispatch_envelope, InvalidParamsError -> -32602) and agent_backends/base.py (PYTHON_LAUNCH_ENV_VARS, StdioMcpServer, AgentCapabilities.mcp; claude.py _apply_mcp_servers:558, codex.py _mcp_server_overrides:506, registry.py _prepare_backend setattr:303 and create_registered_backend primary-or-single-fallback:239-289); the six role attach sites and their refusals (agent.py:540-541/827, analysis.py:587/1371-1374, orchestration.py:314/1166-1187/1797-1815, plan_critic.py:396/436-439, specialists.py:467/510-517, supervisor.py:235-237/290-293); cli/options.py:_validate_speedup_target (122-149) beside _validate_roofline_target; forge_loop_command.py (the option, target_mean_case_speedup into IterationConfig:1740, _announce_knowledge_base:152/1945); config.py (_normalize_knowledge_wiki_paths:239-255, from_env:374-375); runner.py (IterationConfig:544, _stop_at_speedup_target:3398-3410 inside _stop_condition_met:5272 at the top of _run_locked:6422 after best is hydrated at 3536; _iteration_result_event:3859 and every emitter traced -- _settle_locked_attempt's result.agent_rationale=rationale at 6394 covering the hazard/merge/no-agent branches, _collect_session_candidate's at 5987, _planning_failure:744, _pending_keep_result:1810; the caps: event RATIONALE_MAX_CHARS, console 5790, history 6533, subject 1061; _inspect_pending_keep's subject comparison:1163; _persist/_restore_lane_queue:4568/4593); fanout.py (LaneResult.rationale:343, session type:276, _one's error path); the agent_fn return's every caller (runner.py:5788/1979, cli/lanes.py:181, port_loop.py:222); iteration_result and termination_reason readers tree-wide (run_state.py:229/365/369 make_event drops None, prompt_view.py:135, reporting.py:519, langfuse_emitter.py:266, forge_fusion.py:202 — no closed-set reader); base-tree comparisons for every pre-existing shape (pending-keep keys, agent_fn's gate-tag return at base agent.py:895-915, the lane-queue and signature-introspection excepts); docs rows against the executed band; tree-wide occurrence counts for all 23 new identifiers; all six prior blocking findings re-verified fixed from the code at this head | Ran: at head 6a154d0 -- pytest test_knowledge_wiki_tool.py (49 passed, real stdio MCP session included), test_forge_loop_resume.py -k speedup (7 passed), test_loop_runner.py -k "rationale or speedup_target or lane" (21 passed), test_claude_resume.py rationale test + test_codex_backend.py -k rationale (3 passed), test_fanout.py + test_lane_plan_persistence.py -k rationale (3 passed), test_campaign_cross_process.py resume KEEP variants (3 passed), test_specialist_probe.py + test_forge_orchestration.py + test_analysis_agent.py (182 passed); ruff --select C901 (max-complexity 20) and --select PLR0913 (max-args 5) over the changed non-test files -- both pass | Base: d0d690b | Head: 6a154d0 |
|
/review |
|
Automated review started for head 5e83f0d. Progress: https://github.com/AMD-AGI/Hyperloom/actions/runs/40010067668 |
|
Automated review: no verdict applied -- a clean card cannot defer a surviving finding. Comment PR #2034 -- feat(kernelforge): offer a configured knowledge base to every forge role and record why each step was takenWhat it does: The forge planning roles run their sessions without a shell, so a kernel knowledge base laid out as a checkout never reached the agents that choose the optimization direction, and a run left no record of why any step was taken -- Blocking issues: none deferred: V4 -- advisory by rules.md's severity contract: #2037 (Experience KB, another author) rewrites the same units from the same base values (make_agent_fn.agent_fn 142->132 there vs 142->140 here; _source_attempt 148->146 vs 148->142; both reshape the run_lanes session callable), so the second to land rebases on a moved floor; not a blocking finding, so no numbered entry SKIPPED: V4 -- "# PARTIAL: #1869 changes 695 files, only its first 100 were compared": overlap with #1869's remaining files unchecked. Checked: knowledge/wiki_tool/{init,contract,mcp_server,tool}.py (handle_tool_call/_run/_log/script_arguments/_require_inside_root/_ARGUMENT_KEYS, TOOL_DEFINITIONS, serve entry; AST-verified that the session side never imports the |
|
/review |
|
Automated review started for head 5e83f0d. Progress: https://github.com/AMD-AGI/Hyperloom/actions/runs/40020248470 |
PR #2034 -- feat(kernelforge): offer a configured knowledge base to every forge role and record why each step was takenWhat it does: The forge planning roles run their sessions without a shell, so a kernel knowledge base laid out as a checkout never reached the roles that choose the optimization direction, and a run left no record of why each step was taken -- Blocking issues: none Checked: knowledge/wiki_tool/{init,contract,mcp_server,tool}.py (handle_tool_call/_run/_log/script_arguments/_require_inside_root/_ARGUMENT_KEYS, TOOL_DEFINITIONS, the serve entry) against tools/mcp_stdio.py (serve, dispatch_envelope, InvalidParamsError) and agent_backends/base.py (PYTHON_LAUNCH_ENV_VARS, StdioMcpServer, AgentCapabilities.mcp), claude.py:374-383/codex.py:327-334/deepseek.py:162-171 capability declarations, registry.py create_registered_backend's primary-or-single-fallback; the six role attach sites and their refusals (agent.py:540/827/898, analysis.py:584/1353-1374, orchestration.py:311/1166-1187/1797-1838, plan_critic.py:387/423-439 and the tool-less repair at :523, specialists.py:461/510-517, supervisor.py:235-237/290-293); cli/options.py:_validate_speedup_target beside _validate_roofline_target; forge_loop_command.py (the option, IterationConfig wiring at :1740, _announce_knowledge_base at :152/1945, Config.from_env at :1657); config.py (_normalize_knowledge_wiki_paths, from_env); runner.py (IterationConfig:544, _stop_at_speedup_target:3398 inside _stop_condition_met:5272 at the top of _run_locked:6422 after best hydration at :3536, _iteration_result_event:3859 and every IterationResult producer traced -- hazard/queued-lane/merge/agent/planning-failure/no-agent paths and the crash path, the caps: event RATIONALE_MAX_CHARS at agent.py:57, console [:200] at runner.py:5793, history [:80] at :6533, subject [:72] at :1061, _lane_rationale:240, _planning_failure:733, _persist/_restore_lane_queue:4568/4593, _build_pending_keep's one-line subject and _inspect_pending_keep's git %s comparison at :1061/:1103/:1162-1164); fanout.py (LaneResult.rationale, the widened session Callable, _one's rationale and error paths); the agent_fn return's every caller tree-wide (runner.py:5788, runner.py:1979, cli/lanes.py:190, port_loop.py:222 which discards it); iteration_result and termination_reason readers tree-wide (make_event drops None at run_state.py:377, no closed-set reader; fusion/command.py:512 checks a failure subset speedup_target_met is not in); base-tree comparisons for every pre-existing shape (the gate-tag return at base agent.py:895-915, agent_rationale threading at base runner.py:696/667/6489, PENDING_KEEP_FIELDS pre-including rationale and commit_subject, the probe= parameter and its one production caller); docs rows against contract.py SCRIPTS and the executed validator band; tree-wide occurrence counts for every new identifier (no writer without a reader, no removed caller with a surviving helper, _gate_outcome_tag zero survivors); per-file comment/code ratios measured from diff.txt; ruff C901 (max-complexity 20) and PLR0913 (max-args 5) over the 17 changed non-test src files -- both pass | Ran: at head 5e83f0d -- pytest src/kernelforge/tests/test_knowledge_wiki_tool.py (49 passed, real stdio MCP session included), test_forge_loop_resume.py -k speedup (7 passed), test_loop_runner.py -k "rationale or speedup_target" (5 passed), test_claude_resume.py (36 passed) and test_codex_backend.py -k rationale (2 passed), test_fanout.py (33 passed) and test_lane_plan_persistence.py (36 passed), test_campaign_cross_process.py (13 passed), test_specialist_probe.py + test_forge_orchestration.py + test_analysis_agent.py (182 passed); _validate_speedup_target executed directly over 0.0/-0.0/0.5/1.0/3.15/-3.15/inf/-inf/nan (only 0 off, 3.15 accepted, the rest refused with the documented messages); the real KnowledgeWikiServer probed over a fake checkout (a page inside the root served, a symlink pointing out refused, /etc/hostname, ../../../etc/hostname and scripts/../../x refused before the script runs, a leading-dash query staying a query word after --, a 100KB undeclared key kept out of the log); against the base tree d0d690b -- the claude rationale test reproduced failing with the session's opening text, the two hard resume-KEEP variants reproduced failing with LGTM |
Description: what and why
The forge planning roles (dispatch, specialists, synthesis, plan critic, supervisor) have no shell, so a kernel knowledge base reachable only through Bash never reaches the agents that choose the optimization direction, and a run leaves no record of why each step was taken. This PR adds three foundations, each its own commit:
FORGE_KNOWLEDGE_WIKI_ROOTnames a KernelWiki-layout checkout. Every session that has tools then gets the same stdio MCP server (kernelforge.knowledge.wiki_tool.mcp_server, on the sharedtools.mcp_stdiotransport) withsearch,page,grepandguide, which run the checkout's own scripts. Apagepath that is absolute or resolves outside the checkout is refused before the script runs. Each call takes a required one-linereasonand is appended, with the calling role, the reason and only the arguments the tool declares, toFORGE_KNOWLEDGE_WIKI_LOG(knowledge_wiki_calls.jsonl). Unset, no session changes. Set, a root that is not an absolute path to such a checkout, or a log path that is not absolute, is refused when the campaign starts, and so is a backend without MCP support, which would silently drop the tools: when each role is built at startup, the supervisor, whose backend is built on its first pass, by what its provider and its fallback provider declare. The built-inclaudeandcodexbackends support MCP;deepseekdoes not, so it is refused while a knowledge base is set. Both variables are documented indocs/reference/environment-variables.md;FORGE_KNOWLEDGE_WIKI_ROLEis set by forge for each session's server and is internal.RATIONALE:line just before thePLAN:line: the direction and why it should beat best, or why a direction is exhausted.agent_fnrecords that line, parsed asPLAN:is (the last such line, at most 500 characters), andno rationale providedwhen there is none; before, it returned the gate tag and the first 200 characters of the session text, which on Claude is every text block of the session, so the opening remarks were recorded instead of the reasoning. The rationale goes on theiteration_resultevent, and each reader keeps its own cap: 200 characters on the console line, 80 in the history line, 72 in a KEEP's commit subject. The gate's figures stay where they already were, in the session sink and on the[session-end]line. A KEEP is committed under one line with no trailing space, which is the subject a resume compares with the one git recorded. A fan-out lane keeps its own rationale through the lane queue (a queue written before this restores an empty one), so the iteration that measures it recordslane N of a fan-out round: <rationale>instead of the bare lane number. An iteration whose planning failed recordsplanning failed; no Implementer session: <error>.--target-mean-case-speedup X. Ends the campaign before the next round once the incumbent's mean per-case speedup over the baseline (the aggregate a KEEP is scored by) reachesX, with termination reasonspeedup_target_met; a resume whose recorded best already meets it ends without another round. Zero (default) disables it; any other value at or below 1.0 (a negative one included), or one that is not finite, is refused; and it never enters the KEEP decision.Why one PR: part 1 is what the wiki-guided planning mode (feat(kernelforge): spend the first round on a knowledge base batch with --wiki-guided #2036, a draft stacked on this one) builds on in code. Parts 2 and 3 are not referenced by feat(kernelforge): spend the first round on a knowledge base batch with --wiki-guided #2036's diff; they are what its evaluation runs used, to compare trajectories by why each step was taken and to stop at a set speedup. Each is a separate commit and can be reviewed on its own. An earlier revision also let a declared target named like a test escape the test-name protection; that is dropped from this PR, because the tests in such a file would leave the tamper snapshot, and will come separately with symbol-scoped protection.
Linked issue(s): none
Tests: added/updated? commands run?
39 new tests. Through the real call paths:
test_knowledge_wiki_tool.py(the MCP server over stdio, the reason requirement and log record, apagepath outside the checkout refused before the script runs, a log line that records only the declared arguments, which roles get the server throughOrchestrationAgent._run_spec,SpecialistAgent,PlanCriticAgent.review,AnalysisAgentService.ensure_bundle,make_agent_fnandmake_supervisor_fn, that a tool-less formatting turn gets none, unusable settings refused before any session starts, and a supervisor refused when it is built if its provider or its fallback provider declares no MCP);test_claude_resume.py(a realClaudeBackendover a stream that opens with other text records the declaredRATIONALE:line, and its prompt asks for one);test_codex_backend.py(with the gate on, the declared line comes back whole, longer than the old cut and without the gate tag, and a session that declares none records the placeholder);test_campaign_cross_process.py(a KEEP committed before the state checkpoint is recovered on resume when its rationale has a newline in the first 72 characters or a cut that ends on a space);test_loop_runner.py(rationale oniteration_result, the lane rationale through a fan-out round, the rationale of an iteration whose planning failed, a resumed best at, short of and without the speedup target);test_forge_loop_resume.py(the speedup option reaches the loop config, is off by default, and refuses a value the baseline meets, a negative one included, and one that can never be reached);test_lane_plan_persistence.py(lane rationale persisted and restored across processes, the old queue format).test_specialist_probe.pyandtest_forge_orchestration.pynow buildSpecialistAgent(tools=SpecialistTools(probe=...)); their assertions are unchanged.Run locally:
pytest src/kernelforge -n 16(6101 passed, 19 skipped, on main at d0d690b),ruff checkandruff format --check,lint-imports,python scripts/code_metrics.py --base-ref origin/main,python scripts/pr_hygiene.py size,python scripts/check_cli_references.py.Size/complexity triggers crossed: split, or why the shape is right:
745 production lines (size/XL, over the 400-line review trigger, under the 1000-line limit); 464 of them are the new
knowledge/wiki_tool/package (the MCP server alone is 314). Review commit by commit.Requesting
baseline-raise. The new logic lives in a new package under every limit (kernelforge/knowledge/wiki_tool/: the session side, the MCP server and the contract between them); what remains is a few lines of wiring in units and modules that were already over their limits at the base:forge_loop(function lines)make_agent_fn(function lines)SpecialistAgent.run(function lines)AnalysisAgentService._run_analysis_session(function lines)Config.from_env(function lines)PlanCriticAgent.review(function lines)make_orchestration_service(function lines)loop/runner.py(module lines)cli/forge_loop_command.py(module lines, over the limit at the base but not baselined)orchestrator/orchestration.py(module lines)orchestrator/analysis.py(module lines)The lane-rationale text, the planning-failure result and the knowledge-base banner were moved into helpers, so
_source_attemptis shorter than at the base (142 lines, from 148) and its andforge_loop's cognitive complexity stay at their base values. The knowledge base path checks and the speedup target's stop are units of their own (Config._normalize_knowledge_wiki_paths,_stop_at_speedup_target), soConfig.__post_init__and_stop_condition_metstay under the 50-line limit.make_agent_fn's cognitive complexity drops from 72 to 70 andagent_fnfrom 142 to 140 lines, and their baseline entries are lowered to match.SpecialistAgenttakes the probe and the knowledge base as oneSpecialistToolsvalue to stay at five parameters. Splitting these 60- to 6000-line units is outside a feature PR. Every other check (comments, broad catches, English only, repeated literals, A1 and A8 to A12, duplication, dead code) reports nothing.If this simplifies or refactors: which mechanism was removed, which contract was preserved, and the tests that show it:
Not a refactor. One internal signature changed:
SpecialistAgent(probe=...)is nowSpecialistAgent(tools=SpecialistTools(probe=..., knowledge_wiki=...)); its one production caller, the orchestration factory, is updated. The four variables apython -m kernelforge...MCP server needs to start are now onePYTHON_LAUNCH_ENV_VARSbesideStdioMcpServer, which the probe server and the knowledge base server share; the probe child's environment is unchanged.Observable effect: what an operator will now see, and what the old behaviour cost them:
With
FORGE_KNOWLEDGE_WIKI_ROOTset, the run banner printsKnowledge base: <root> (calls logged to <log>), and every role's knowledge-base call appears in the log with its role and reason; before, the planning roles could not read the knowledge base at all. A root or log path that is not absolute, a root that is not a knowledge base checkout, or a backend without MCP support is refused, as item 1 describes, instead of running with the tools silently missing. Everyiteration_resultevent carriesrationale, the line the Implementer declared, and a lane's iteration names its own reason; before, a run could not be compared by why each step was taken, the multi-lane iterations, often the ones that move the score most, logged no reason, and on Claude the recorded text was the session's opening remarks. A KEEP's commit subject (iter-N: ...) and the Implementer's history line now carry that rationale instead of the gate tag[gate edits=... pass=... end=...; the gate's edit count, pass and end reason remain on the[session-end]line, and the history line still shows the speedup of each measured iteration. The subject is one line with no trailing space, so a campaign interrupted after a KEEP commit still resumes; a subject that git folded or trimmed would have been refused withpending KEEP commit message mismatch.--target-mean-case-speedupends a run withspeedup_target_met; before, the only target was the roofline one, which needs an estimated ceiling and refuses to rule while a cache-resident case beats it.Breaking changes: no (no CLI, configuration or event field removed; the
SpecialistAgentconstructor change is internal; a resume compares a pending KEEP's commit subject with the one stored when it was built, so the new wording is stored and compared together, and a pending KEEP written before this keeps the subject it stored)PR addresses single concern: no. It bundles the three foundations above: the knowledge base tools the wiki-guided planning PR builds on, and the rationale and speedup target its evaluation runs used; each is a separate commit.
Root cause is upstream (Magpie/TraceLens/GEAK/IntelliKit/AgentKernelArena), ticket filed: no