Repository navigation
1nzr: reorganise flat root into src/{bridge,core,crew,review,sensors,tools} - #152
Conversation
…,tools} Move 93 root-level modules into a layered src/ package. Keep the six entry points at the repo root: conductor, issue_bridge, bookbag, onboard, review_packet, a2a_bridge. Evidence: - 109 modules import clean (root entry points + src/**) — importlib probe over every module, 0 failures. - Full suite: 4 failed / 2475 passed / 34 skipped / 1 xfailed. Baseline before the reorg was 34 failed / 2438 passed / 7 errors. The 4 remaining failures are NOT reorg regressions: 3x test_omni_relay_live (upstream HTTP 500) and 1x test_timeout_marker (pytest ini option unregistered mid-run). Diffed against HEAD, every failing code path is IDENTICAL. - Blast radius via codegraph: run_smoke -> recovery.py, run_bootstrap -> recovery.py, bridge_issues -> 81 callers / 9 tests, create_pr_for_issue -> pr_creator.py. All resolve. Bundled here (splitting would break imports): - onboard.py, a2a_bridge.py: new root entry points referenced by the modified issue_bridge.py:3344 (--onboard passthrough). - tests/test_onboard.py, tests/test_a2a_bridge.py: their tests. NOT addressed by this change: - README.md test counts remain stale (still claims 942 passing). - pytest-timeout ini registration still unregistered mid-run.
The reorg commit rewrote every intra-repo import to the new src/<area>/<mod> layout. Cherry-picking it onto main (which is still flat) took main's flat-path import block for four files, so they resolved on the old layout and broke on the new one. Measured before the fix (honest probe: ONLY repo root on sys.path, import every module by dotted path): src.core.director ModuleNotFoundError: No module named 'scoring' src.core.leaf ModuleNotFoundError: No module named 'scoring' src.crew.autonomous_loop ModuleNotFoundError: No module named 'scoring' src.tools.cli ModuleNotFoundError: No module named 'scoring' src.tools.mcp_server SystemExit: 1 => 98/103 src/ modules imported, 5 failed tests/test_leaf.py failed at COLLECTION for the same reason (from src.core.director import run_task -> director.py:23). issue_bridge.py was broken the same way and was NOT covered by the probe above (it is a root entry point, not under src/): 10 flat imports of moved modules, e.g. `from scoring import ScoreStore`. After the fix, same honest probe over src/ + root entry points: 109 modules, 109 OK, 0 FAIL Why the earlier "109/109 import clean" claim was wrong: that probe put each module's own directory on sys.path, which makes a flat import resolve regardless of the real layout. It could not fail. The probe in this commit inserts only the repo root. Files: issue_bridge.py (10 imports), src/core/director.py (16), src/core/leaf.py (4), tests/test_issue_bridge.py (3).
CI's lint step now lints the canonical source tree instead of "core modules + files changed in this PR". A diff-scoped gate assumes a changed file is a file someone just wrote; the 1nzr reorg rewrote intra-repo imports across the repo, so ~280 files were "changed" while only their import lines moved -- pulling in files that were never lint-clean and turning the gate red for a change that added no defects. MEASURED (python3 -m ruff check, exact CI commands): canonical scope (core entry points + src/ + scripts/): 107 errors -> 0 tests/ with --select F821,F811,F822: 2 errors -> 0 FIXED (real defects this exposed in previously-unlinted files): - src/core/persistence_contract.py:466 exported AUDIT_IDENTITY_FIELDS in __all__; the real name is _AUDIT_IDENTITY_FIELDS, so `import *` would raise AttributeError. Phantom export removed. - src/core/director.py, src/core/leaf.py, src/crew/autonomous_loop.py: flat `from scoring import ScoreStore` -- a stale artifact of the pre-reorg flat layout. These modules could not import at all after the 1nzr reorg. The earlier "109 modules import clean" verification used a probe that inserted each module's own directory on sys.path, which resolves a flat import regardless of the real layout and therefore cannot fail. Honest probe (repo root only, dotted import, SystemExit guarded): 5 failed -> now 109/109 OK. - scripts/candidate_pr_contract.py used CandidateStore and SimpleNamespace without importing them (real F821; runtime calls, not annotations). CandidateStore lives in src/bridge/candidate_manifest.py -- NOT candidate_binding.py, where a first guess put it. - The 12 remaining F821 were false positives (quoted/lazy annotations behind `from __future__ import annotations`); they now use TYPE_CHECKING imports, which also breaks import cycles. DELIBERATELY NOT DELETED (lint findings that are missing features, not stale code -- kept and flagged with a reason): - src/tools/generate_activity_dashboard.py: `data = json.dumps(...)` is unused, but the docstring promises "all data baked in". The embed is missing; deleting the line would cement the gap. - bookbag.py: `BookbagSignal(bead, repo)` is constructed but never read; the polling loop reads the bookbag JSON instead. The flag-file optimisation was clearly the intent. WHAT THIS DOES NOT FIX: the test suite has not yet been re-run since these edits; the last full run (4F/2475P/34S) was on the pre-reorg audit branch and does not transfer. No runtime behaviour was changed -- all edits are imports, dead-statement removal, and comments.
…ew layout The reorg (b372f7b) moved modules into src/{core,tools,...} but left the tests reaching them by their pre-move flat names. Three files, 16 failures: tests/test_leaf.py 5 failed import leaf as leaf_module (x4) patch("leaf.X") (x12) tests/test_anchor_loader.py 6 failed import prompt_composer (x6) tests/test_issue_bridge.py 5 failed patch("repo_reader.X") (x13) patch("director.X") (x2) Two distinct traps, both worth naming: 1. `import src.core.leaf` binds `src`, not `leaf`. The test bodies reference the bare name, so the first rewrite of test_anchor_loader.py produced `NameError: name 'prompt_composer' is not defined` rather than fixing anything. The alias must survive: `import src.tools.prompt_composer as prompt_composer`. Same shape as the leaf case (`as leaf_module`). 2. `patch("module.fn")` is a STRING target, so an import sweep cannot see it. It resolves the flat module name at patch time and raises ModuleNotFoundError only when the test runs -- the file still collects, so collection-time checks stay green. MEASURED: tests/test_leaf.py 5 failed -> 42 passed tests/test_anchor_loader.py 6 failed -> 66 passed (both files together) tests/test_issue_bridge.py 5 failed -> 158 passed in 215s Sweep coverage: a whole-repo import sweep found ZERO flat imports of moved modules outside .scratch/ (my own scratch). A whole-repo sweep for string references found 72 sites, of which 67 were false positives -- evidence strings ("crew_dispatch.py:123"), argv (["teacher.py", ...]), filenames ("leaderboard.html", "entire_review.md"), docstrings, and a vendored repo's own `trajectory.json`. Each was checked individually rather than bulk-rewritten. NOT fixed here, deliberately: tests/test_a2a_bridge.py::test_raises_on_malformed_json fails in the full suite but PASSES in isolation (1 passed in 1.11s), and this branch does not touch a2a_bridge.py (empty diff vs b372f7b). That is an order-dependent test-isolation issue; confirming it predates this work needs a full-suite run on origin/main.
|
Sorry @branben, your pull request is larger than the review limit of 150,000 diff characters |
|
ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing |
| from src.tools.repo_default import default_repo | ||
| if repo == default_repo(): | ||
| return Path(__file__).resolve().parent |
There was a problem hiding this comment.
Local reviews check wrong folder
The current-checkout fallback in _resolve_repo_path now returns src/core, not the checkout root. A local task for the current repository with no cached clone therefore freezes and verifies that subtree, missing the root project_verify.yaml and other source files. Return the checkout root here.
| from src.tools.repo_default import default_repo | |
| if repo == default_repo(): | |
| return Path(__file__).resolve().parent | |
| from src.tools.repo_default import default_repo | |
| if repo == default_repo(): | |
| return Path(__file__).resolve().parent.parent.parent |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/core/director.py
Line: 1861-1863
Comment:
**Local reviews check wrong folder**
The current-checkout fallback in `_resolve_repo_path` now returns `src/core`, not the checkout root. A local task for the current repository with no cached clone therefore freezes and verifies that subtree, missing the root `project_verify.yaml` and other source files. Return the checkout root here.
```suggestion
from src.tools.repo_default import default_repo
if repo == default_repo():
return Path(__file__).resolve().parent.parent.parent
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| adversarial_review["verdict"] = "FAIL" | ||
| adversarial_review["score"] = 0.0 |
There was a problem hiding this comment.
Critical findings still allow publication
The new Entire veto only changes adversarial_review. It does not reject canonical_packet or stop the loop. With passing build checks and judges, a CRITICAL sensor finding still reaches grading and PR publication, while the saved packet remains accepted. Reject the authoritative result and stop before those side effects.
Prompt To Fix With AI
This is a comment left during a code review.
Path: issue_bridge.py
Line: 2713-2714
Comment:
**Critical findings still allow publication**
The new Entire veto only changes `adversarial_review`. It does not reject `canonical_packet` or stop the loop. With passing build checks and judges, a CRITICAL sensor finding still reaches grading and PR publication, while the saved packet remains accepted. Reject the authoritative result and stop before those side effects.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| try: | ||
| locked_update_bookbag( | ||
| leaf.bead, | ||
| repo=target_repo or "__global__", | ||
| lock_timeout=10.0, | ||
| entire_findings=entire_findings, | ||
| entire_status=entire_status, | ||
| ) |
There was a problem hiding this comment.
The new persistence step ignores locked_update_bookbag returning None, its documented lock-timeout result. If this write times out and the lock later clears, _persist_acceptance can read no Entire findings and accept both passing judges despite a CRITICAL sensor result. Check the return value and prevent acceptance when the result was not saved.
Prompt To Fix With AI
This is a comment left during a code review.
Path: conductor.py
Line: 657-664
Comment:
**Lock timeout drops the veto**
The new persistence step ignores `locked_update_bookbag` returning `None`, its documented lock-timeout result. If this write times out and the lock later clears, `_persist_acceptance` can read no Entire findings and accept both passing judges despite a CRITICAL sensor result. Check the return value and prevent acceptance when the result was not saved.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| url = source | ||
| if "github.com/" in source and not source.startswith(("http", "git@")): | ||
| url = f"https://github.com/{source}.git" | ||
| if url.startswith("http") or url.startswith("git@"): |
There was a problem hiding this comment.
New dependencies cannot be locked
_infer_dependencies stores sources as owner/repo, but this URL guard only expands strings containing github.com/. Collected sources therefore never reach the online lookup. A new dependency using a branch or tag gets commit: null unless an existing lock supplies it, even online, and fails the documented lock check. Normalize the slug into a clone URL and use the slug for the commit lookup.
Prompt To Fix With AI
This is a comment left during a code review.
Path: onboard.py
Line: 1310-1313
Comment:
**New dependencies cannot be locked**
`_infer_dependencies` stores sources as `owner/repo`, but this URL guard only expands strings containing `github.com/`. Collected sources therefore never reach the online lookup. A new dependency using a branch or tag gets `commit: null` unless an existing lock supplies it, even online, and fails the documented lock check. Normalize the slug into a clone URL and use the slug for the commit lookup.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| if str(locked.get("name")) == name or locked.get("source", "").endswith(source): | ||
| return { | ||
| "name": name, | ||
| "source": str(locked.get("source") or source), | ||
| "ref": str(locked.get("ref") or ref), | ||
| "commit": str(locked.get("commit") or ""), |
There was a problem hiding this comment.
Dependency pins cross repositories
Existing lock entries are reused by basename alone, without checking the owner or requested ref. A target's acme/ripwire dependency matches the orchestrator's redhat-et/ripwire entry and copies that other repository's source and commit. A changed ref for the same repository also reuses the old pin. Match the full normalized source and requested ref before reusing a lock.
Prompt To Fix With AI
This is a comment left during a code review.
Path: onboard.py
Line: 1301-1306
Comment:
**Dependency pins cross repositories**
Existing lock entries are reused by basename alone, without checking the owner or requested ref. A target's `acme/ripwire` dependency matches the orchestrator's `redhat-et/ripwire` entry and copies that other repository's source and commit. A changed ref for the same repository also reuses the old pin. Match the full normalized source and requested ref before reusing a lock.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| for match in re.finditer(r"github\.com[/:]([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)", text): | ||
| _add(match.group(1), via=f"{manifest}") |
There was a problem hiding this comment.
Explicit dependency pins disappear
The raw-text scan adds each GitHub dependency with ref="HEAD" before the parsed [tool.uv.sources] entry supplies its rev or tag. Because _add uses setdefault, that explicit pin is discarded. Parse structured entries first, or allow an explicit pin to replace the guessed HEAD value.
Prompt To Fix With AI
This is a comment left during a code review.
Path: onboard.py
Line: 691-692
Comment:
**Explicit dependency pins disappear**
The raw-text scan adds each GitHub dependency with `ref="HEAD"` before the parsed `[tool.uv.sources]` entry supplies its `rev` or `tag`. Because `_add` uses `setdefault`, that explicit pin is discarded. Parse structured entries first, or allow an explicit pin to replace the guessed `HEAD` value.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| for child in sorted(base.iterdir()): | ||
| if child.is_dir() and not child.name.endswith(".git"): | ||
| return child |
There was a problem hiding this comment.
_envit_store_path returns the first checkout directory without comparing it with the generated lock's commit. If the store holds another revision, the new .envit/repos link points there while the lock claims a different pin. Existing links are also skipped on refresh. Select and verify the locked commit, and refresh links when the pin changes.
Prompt To Fix With AI
This is a comment left during a code review.
Path: onboard.py
Line: 1547-1549
Comment:
**Linked code differs from lock**
`_envit_store_path` returns the first checkout directory without comparing it with the generated lock's commit. If the store holds another revision, the new `.envit/repos` link points there while the lock claims a different pin. Existing links are also skipped on refresh. Select and verify the locked commit, and refresh links when the pin changes.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| match = re.match(r"^(npm|pnpm|yarn|bun)@", str(declared)) | ||
| if match: | ||
| ev.package_manager = match.group(1) | ||
| ev.generated.add("package_manager") |
There was a problem hiding this comment.
Evidence requirement has no effect
_collect_evidence always adds package_manager to generated, even when it only chose the default npm and read no repository facts. Since detected is just bool(generated), --require-evidence cannot reject an empty initialized checkout. Only mark facts actually read from the target, and test this with a real empty Git checkout.
Prompt To Fix With AI
This is a comment left during a code review.
Path: onboard.py
Line: 821
Comment:
**Evidence requirement has no effect**
`_collect_evidence` always adds `package_manager` to `generated`, even when it only chose the default `npm` and read no repository facts. Since `detected` is just `bool(generated)`, `--require-evidence` cannot reject an empty initialized checkout. Only mark facts actually read from the target, and test this with a real empty Git checkout.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| {"name": "yaml-validate", | ||
| "cmd": "python3 -c \"import yaml, sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]\" " | ||
| "$(find . -name '*.yaml' -o -name '*.yml' | grep -v node_modules | head -5) 2>&1 || true", | ||
| "cwd": ".", "languages": "yaml"}, | ||
| ], | ||
| "json": [ | ||
| {"name": "json-validate", | ||
| "cmd": "python3 -c \"import json, sys; [json.load(open(f)) for f in sys.argv[1:]]\" " | ||
| "$(find . -name '*.json' | grep -v node_modules | head -5) 2>&1 || true", |
There was a problem hiding this comment.
Broken config passes verification
The generated YAML and JSON checks end with || true, so malformed files still produce a successful exit. run_verify_gate treats that exit as a passed check. A newly onboarded config-only repository can therefore report successful verification without any check capable of failing. Preserve parser failures instead of forcing success.
| {"name": "yaml-validate", | |
| "cmd": "python3 -c \"import yaml, sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]\" " | |
| "$(find . -name '*.yaml' -o -name '*.yml' | grep -v node_modules | head -5) 2>&1 || true", | |
| "cwd": ".", "languages": "yaml"}, | |
| ], | |
| "json": [ | |
| {"name": "json-validate", | |
| "cmd": "python3 -c \"import json, sys; [json.load(open(f)) for f in sys.argv[1:]]\" " | |
| "$(find . -name '*.json' | grep -v node_modules | head -5) 2>&1 || true", | |
| {"name": "yaml-validate", | |
| "cmd": "python3 -c \"import yaml, sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]\" " | |
| "$(find . -name '*.yaml' -o -name '*.yml' | grep -v node_modules | head -5) 2>&1", | |
| "cwd": ".", "languages": "yaml"}, | |
| ], | |
| "json": [ | |
| {"name": "json-validate", | |
| "cmd": "python3 -c \"import json, sys; [json.load(open(f)) for f in sys.argv[1:]]\" " | |
| "$(find . -name '*.json' | grep -v node_modules | head -5) 2>&1", |
Prompt To Fix With AI
This is a comment left during a code review.
Path: onboard.py
Line: 183-191
Comment:
**Broken config passes verification**
The generated YAML and JSON checks end with `|| true`, so malformed files still produce a successful exit. `run_verify_gate` treats that exit as a passed check. A newly onboarded config-only repository can therefore report successful verification without any check capable of failing. Preserve parser failures instead of forcing success.
```suggestion
{"name": "yaml-validate",
"cmd": "python3 -c \"import yaml, sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]\" "
"$(find . -name '*.yaml' -o -name '*.yml' | grep -v node_modules | head -5) 2>&1",
"cwd": ".", "languages": "yaml"},
],
"json": [
{"name": "json-validate",
"cmd": "python3 -c \"import json, sys; [json.load(open(f)) for f in sys.argv[1:]]\" "
"$(find . -name '*.json' | grep -v node_modules | head -5) 2>&1",
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Comments Outside DiffThese findings could not be posted inline.
|
…move root path The reorg moved verify_gate.py to src/review/verify_gate.py, but the index-gate selftest still named 'verify_gate.py' (root) in its receipt and claim. line_count() returned None for the missing file, so the fresh-receipt case returned UNSUPPORTED instead of OK and the stale receipt returned UNSUPPORTED instead of STALE -- 3 of 8 checks failed. Now uses src/review/verify_gate.py throughout. Selftest: all checks behaved as specified (verified locally).
branben
left a comment
There was a problem hiding this comment.
Review: 1nzr reorg — mostly solid, but CI is red and several holes need attention
Reviewed against main@9e3ea87, all 294 files, the five commits, and live CI check-runs.
tl;dr
Do not merge yet. The reorg itself is mechanically well-executed (rename detection clean, import sweep verified, no leftover flat imports), but:
- CI is red on this PR and green on main — pytest (py3.9/3.11/3.12) all abort with a suite-killing timeout. This is a regression introduced here, not pre-existing.
- 9 Greptile findings confirmed — I verified all of them against the code. The ENTIRE-veto publication gap (#2) and lock-timeout veto-drop (#3) are the most serious.
- Behavioral changes (new ENTIRE veto, conductor bookbag persistence, new modules) ride inside a PR titled "reorganise ... (reorg-only — no behaviour change)" — the framing is inaccurate and makes review harder.
project_verify.yamlcoverage regressed —compileall -q src/no longer covers the six root-level.pyfiles.- Runtime data files committed under
src/with machine-specific local paths.
1. CI regression (blocker)
- main HEAD:
pytest (py3.9/3.11/3.12)→ success (2400 passed, 29 skipped;test_adversarial_review_failure_falls_backtakes 30.15s and passes; no timeout marker). - This PR: all three pytest jobs fail — the run aborts at ~40% with
+++ Timeout +++intests/test_issue_bridge.py::TestAdversarialReviewStep::test_adversarial_review_failure_falls_back→_apply_lens→_call_with_transport_retry→time.sleep(backoff_s * (attempt + 1)).
Root cause: the PR added @pytest.mark.timeout(10) to a test that inherently takes ≥30s (default LENS_TRANSPORT_RETRIES=2 / LENS_TRANSPORT_BACKOFF_S=5 → 5s+10s sleeps before the fallback raise, plus the model call attempt). On main, without the marker, the global timeout=60 lets it complete at 30.15s. The new 10s budget guarantees the retry sleep exceeds it → pytest-timeout with method=thread aborts the entire run, so none of the ~60% of tests after it ever run.
The PR body's "4 failed, 2474 passed, 34 skipped" doesn't describe CI reality. Either raise the marker to ≥40s, or (better) make the test hermetic — the comment on the patch even admits "This test is not hermetic — if the patch fails... it makes a live HTTP call... and hangs". A hermetic test would patch the transport layer (or inject a fake call_model_fn) so it fails fast deterministically.
(Also: the scan check is red on both main and PR — pre-existing Codex Security Scan, not introduced here.)
2. Greptile findings — all 9 confirmed valid
I verified each against the branch code:
_resolve_repo_pathfallback returnssrc/core, not the checkout root —src/core/director.py:1861-1863Path(__file__).resolve().parentnow resolves one level too deep (main's root-level director resolved to checkout root). The reorg fixed sibling path bugs (config_path,REPO_ROOT,CREW_RUNS_FILE,hermes-fm-wrapper) but missed this one. Targeted tests (e.g.test_review_build_gate) monkeypatch_resolve_repo_path, so the fallback isn't exercised. Fix:.parent.parent.parent.- CRITICAL ENTIRE veto does not actually block acceptance/publication in the sync bridge path — the veto sets
adversarial_review["verdict"]="FAIL",score=0.0, and injects CRITICAL findings, but:- acceptance is decided by
_review["accepted"](from the canonical packet/two-judge gate) — untouched by the veto; - the combined score is
exec*0.5 + review*0.3 + heuristic*0.2, so review=0 still yields a positive score; - the PR creation path checks
review_evidence(director's review) / the candidate gate — notadversarial_review.
The bridge's own test assertsstatus == "success"while also assertingadversarial_review["verdict"] == "FAIL"— i.e. it codifies that a CRITICAL sensor finding still publishes. This contradicts the comment ("matching conductor._persist_acceptance"). Conductor's_persist_acceptancedoes veto (via the bookbagentire_findings) — the bridge path should mirror that by rejecting the canonical packet / stopping before grading+publication.
- acceptance is decided by
- Lock timeout silently drops the ENTIRE veto —
conductor.py:657-664ignoreslocked_update_bookbag'sNone(documented lock-timeout result). If the write times out,_persist_acceptancereads a bag withoutentire_findingsand accepts. The docs say a non-persisted gate is not a gate. Check the return and prevent acceptance when the result wasn't saved. _resolve_lock_entryavoids the online lookup — sources are stored asowner/reposlugs, and the URL expansion only fires when"github.com/" in source. So thels-remote/gh api repos/{source}/commitsbranch is dead for collected deps →commit: nulleven online. Normalize the slug to a clone URL (or drop the guard and use the slug for the API call).- Lock entries reused by basename alone —
if str(locked.get("name")) == name or locked.get("source","").endswith(source)matchesredhat-et/ripwirefor a requestedacme/ripwire(same basename) and copies the wrong source+commit. Match full normalized source + requested ref. - Raw-text scan pre-empts explicit pins —
_addusessetdefault, so the regex scan recordsref="HEAD"first and the parsed[tool.uv.sources]rev/tag is discarded. Parse structured entries first, or let an explicit pin replace the guessedHEAD. .envit/repossymlinks not verified against the lock commit —_envit_store_pathreturns the first checkout dir without comparing its commit to the lock, and existing links are skipped on refresh. A stale/different-revision checkout can be linked while the lock claims another pin.yaml-validate/json-validateend with|| true— malformed YAML/JSON yields exit 0, sorun_verify_gatetreats it as a passed check. A config-only repo can report successful verification with no check capable of failing. Drop|| true.--require-evidencecannot reject an empty checkout —_collect_evidencealways doesev.generated.add("package_manager")even when it only defaulted tonpmwith no facts read;detected == bool(generated)is therefore always true. Only mark facts actually read.
3. "No runtime behaviour was changed" is inaccurate
The first commit's message and the PR body claim a pure reorg, but the diff adds:
- two new root modules (
onboard.py1718 lines,a2a_bridge.py272 lines + CLI wiring inissue_bridge.py), - a new ENTIRE CRITICAL/HIGH veto in
issue_bridge.py(not on main), - a new bookbag persistence block in
conductor.py(not on main), src/entire_review.py:qodo_replacement→entire_replacementrename and the CLI invocation change (--profile general,--format textdropped),review_packet.py::_sensorlist/int normalization fix.
Each may be fine on its own, but mixing them into a "reorg-only, no behaviour change" PR makes the reviewer hunt for behavior changes across 294 files. Recommend splitting (or at least renaming) — the reorg alone is already large.
4. project_verify.yaml coverage regression
core-python-compile changed from compileall -q *.py to compileall -q src/. The six root-level modules (a2a_bridge.py, bookbag.py, conductor.py, issue_bridge.py, onboard.py, review_packet.py) — including the two new ones — are no longer compile-checked by the verify gate. A syntax error in issue_bridge.py/onboard.py would now sail through. Either extend to both (src/ *.py at root) or keep a stable root-file list.
5. Committed runtime/test data under src/
src/sensors/data/activity_log.json(53 lines) — contains local macOS pytest temp paths (/private/var/folders/.../pytest-of-brandonbennett/...) — machine-specific test debris.src/tools/data/grading_queue.jsonl— runtime queue data.
The runtime data/ dir is gitignored (data/*), but these live under src/ which isn't. They're runtime/test artifacts that shouldn't be tracked. The .gitignore-visible root data/ is empty of activity_log.json/grading_queue.jsonl in this PR — these are sweep-ups from test runs during the reorg.
Positives
- Rename detection is clean; most renames carry ≤2 non-import changed lines.
- The path arithmetic fixes (
config_path,REPO_ROOT,CREW_RUNS_FILE, wrapper path etc.) are correct. - Import sweep verified: no flat imports of moved modules remain outside
.scratch/; all 102src/modules + 6 root modules parse cleanly. testpaths = tests/norecursedirsin pytest.ini and the stable (non-diff-scoped) ruff scope are good hygiene.- The
noqa-with-reason treatment for the two deliberately-deferred findings (beads tkqn/88jf) is exactly right.
Minimal fix list to unblock
- Make
test_adversarial_review_failure_falls_backhermetic or drop/raise its@pytest.mark.timeout(10)(≥40s) so CI stops aborting. _resolve_repo_pathfallback →Path(__file__).resolve().parent.parent.parent.- ENTIRE veto: actually reject (
accepted=False/ stop before grading+publication), and make conductor's lock-timeout path fail closed. project_verify.yaml: compilesrc/and the root modules.- Remove
src/sensors/data/activity_log.json+src/tools/data/grading_queue.jsonl(and gitignoresrc/**/data/if runtime files land there). - onboard.py #4/#5/#6/#8/#9 — the five mechanical correctness fixes (cheap, well-scoped).
…c adversarial-review test - tests/test_issue_bridge.py: make test_adversarial_review_failure_falls_back hermetic and fast (LENS_TRANSPORT_RETRIES=0, LENS_TRANSPORT_BACKOFF_S=0, no-op adversarial_reviewer time.sleep, also force issue_bridge.call_model to raise); timeout raised 10 -> 60. Assertions unchanged. - src/core/director.py: _resolve_repo_path default_repo() fallback returns the checkout root, not src/core. - src/core/director.py: run_verify_gate flake_path anchored to checkout root (where flake.nix lives). - src/core/director.py: ENV_FILE anchored to checkout-root .env. - src/crew/crew_dispatch.py: ENV_FILE and _omniroute_api_key .env fallback anchored to checkout-root .env. - src/crew/crew_dispatch.py: pre-merge run_verify_gate flake_path anchored to checkout root. - src/tools/prompt_composer.py: _CURRICULA_DIR -> <root>/curricula. - src/core/context_orchestrator.py: _REPO_ROOT (DEFAULT_VAULT) -> checkout root, where .cocoindex_code lives. - project_verify.yaml: core-python-compile compiles src/ plus root *.py (compileall -q -l .). - Remove runtime debris src/sensors/data/activity_log.json and src/tools/data/grading_queue.jsonl; .gitignore src/**/data/. Workflow entrypoint/import updates (.github/workflows/school-loop.yml, ci.yml) are prepared but not included: the integration token was refused when the tree touched .github/workflows (likely missing `workflow` scope). They need to be pushed separately.
reorg-only) The behaviour changes below were bundled into the reorg. They now live on school/1nzr-behaviour (branched from a2afda4) and get their own stacked PR. This branch keeps main's logic plus the src/{bridge,core,crew,review, sensors,tools} move, import/path rewrites, lint cleanup, pytest/ruff scope, project_verify compile change, .gitignore, and test import/patch-target updates. Removed: - onboard.py (new root module) and tests/test_onboard.py - a2a_bridge.py (new root module) and tests/test_a2a_bridge.py - issue_bridge.py: --onboard CLI passthrough (--onboard/--write/--out/ --no-clone/--offline/--require-evidence and the onboard.main dispatch) - issue_bridge.py: ENTIRE CRITICAL/HIGH (ENTIRE_HIGH_STRICT) veto on the sync adversarial_review path; tests/test_issue_bridge.py sync-sensor test restored to main's non-blocking assertion (HIGH finding, verdict != FAIL) - conductor.py: bookbag persistence of entire_findings/entire_status via locked_update_bookbag before the acceptance gate - src/entire_review.py: qodo_replacement -> entire_replacement rename and the CLI change (--profile general added, --format text dropped); main's ["entire", "review", "--base", base, "--format", "text"] restored. The unused current_file lint cleanup is kept. - review_packet.py: _sensor list/int findings normalization - docs/setup.md: "Onboarding a new target repo" section - docs/pipeline-explainer.md: Stage 7 rewrite describing the CRITICAL veto, ENTIRE_HIGH_STRICT and --profile general (restored to main) - campus.md: Entire veto / --profile general / Qodo-removal wording (restored to main) - project_verify.yaml: compile-step comment no longer names onboard or a2a_bridge (the compile command itself is unchanged) Not touched: .github/workflows/ci.yml still lists onboard.py and a2a_bridge.py in its ruff CORE list; it needs a separate push with workflow scope.
luna-whymage
left a comment
There was a problem hiding this comment.
APPROVE — reviewed as the secondary reviewer.
Scope: reorg-only. 93 modules relocated under src/{bridge,core,crew,review,sensors,tools}; the 6 root entry points are retained by design (they are the lint gate's CORE list).
Verified against the tree at bb1b968:
- All 5 path-relative anchors now resolve to the repo root:
flake.nix(2155 B),.env(497 B),curricula/,_resolve_repo_path, andcontext_orchestrator._REPO_ROOT. - The 6 lint-gate
COREroot modules exist at the repo root. review-gatepasses on this head: 285 changed files, 1402 added lines, no findings.
Note, not a blocker for this PR: .github/workflows/ci.yml:62 names onboard.py and a2a_bridge.py in its ruff CORE list, and neither file exists on this branch or on main. ruff check warns and exits 0 on a missing path, so the gate is green — but the stale list is a silent scope shrink worth a separate cleanup.
Second note, also not a blocker: ci.yml invokes the gate with --head "origin/<sha>", which is not a ref name. The gate read stdout only and discarded the stderr, so it reported 0 changed files and PASSed vacuously — on every PR since #117. Fixing that is a separate change; it does not affect the reorg itself.
Restores, on top of the #152 revert (bb1b968), the behaviour changes that were bundled into the reorg. The resulting tree is identical to school/1nzr-reorg at a2afda4. - onboard.py + tests/test_onboard.py - a2a_bridge.py + tests/test_a2a_bridge.py - issue_bridge.py: --onboard CLI passthrough - issue_bridge.py: ENTIRE CRITICAL/HIGH veto on the sync path (+ test) - conductor.py: bookbag persistence of entire_findings/entire_status - src/entire_review.py: entire_replacement rename, --profile general, --format text dropped - review_packet.py: _sensor list/int findings normalization - docs/setup.md, docs/pipeline-explainer.md, campus.md, project_verify.yaml comment describing the above
…, close the review-gate fail-open bb1b968 kept #152 reorg-only but carried four unlanded breakages. This lands them. 1. school-loop.yml invoked five moved modules by their PRE-MOVE root paths, so the workflow was broken silently (it does not run on PRs): :149 from school_mail import ... -> from src.tools.school_mail import ... :374 python orca_precondition.py -> python src/crew/orca_precondition.py :405 python gateway_preflight.py -> python src/tools/gateway_preflight.py :424 python worktree_bloat_guard.py -> python src/tools/worktree_bloat_guard.py :495 python crew_ledger_reconcile.py -> python src/crew/crew_ledger_reconcile.py Verified: all five targets exist on disk; no bare root-path invocation remains. 2. ci.yml:293 imported school_mail by its pre-move path -> src.tools.school_mail. 3. tests/test_review_build_gate.py:170 asserted the PRE-REORG value (Path(director.__file__).resolve().parent). director.py now lives at src/core/director.py, so its package dir is src/core, which has no flake.nix -- the verify gate would silently skip its flake check. Rewritten to pin the repo root: asserts (flake_dir / "flake.nix").exists() and that flake_dir is NOT the module's own package dir. Verified: 7 passed in 0.28s (was RED on bb1b968). 4. review-gate fail-open (the PR's only REQUIRED check). ci.yml passed --head "origin/${{ github.event.pull_request.head.sha }}" -- not a ref name. git diff failed on stderr, the error was discarded, and the gate read stdout only, so it reported 0 changed files and PASSed on every PR since #117. - scripts/review_gate.py: git() now raises SystemExit on non-zero exit; new resolve_ref() verifies each ref with rev-parse before diffing; an empty diff is now a FAILURE, not a pass. - ci.yml: both invocations now pass --head "HEAD". Verified RED->GREEN: unresolvable ref exit 1 (was exit 0 + silent PASS); empty diff exit 1 [empty-diff]; real head exit 0; --head HEAD exit 0. tests/test_review_gate.py: +4 regression tests, 15 passed in 7.00s. NOT included here (deliberately): the behaviour batch -- conductor.py, issue_bridge.py, tests/test_conductor_issue_path.py, tests/test_issue_bridge.py -- which belongs on school/1nzr-behaviour per bb1b968's split. Also NOT fixed: ci.yml's ruff CORE= list still names onboard.py and a2a_bridge.py, which no longer exist. Harmless -- ruff warns and exits 0 -- but a silent scope shrink; tracked separately.
What this is
Bead 1nzr — reorganise the flat root into
src/{bridge,core,crew,review,sensors,tools}. Commits:b372f7bc3bfd788e826e2f6705de96fe03cverify_gate.pytosrc/review/a2afda4bb1b968Behaviour changes moved out
Earlier revisions of this PR also carried behaviour changes. Commit
bb1b968removed them, and they now live in the stacked PR #153 (#153):onboard.pyand the--onboardCLI,a2a_bridge.py, the ENTIRE CRITICAL/HIGH veto on the sync path, the conductor bookbag persistence of entire findings, theentire_reviewrename and CLI change (--profile general), thereview_packet._sensorlist/int fix, and the docs describing them. This PR is now main's behaviour plus the move.Note:
.github/workflows/ci.ymlstill listsonboard.py a2a_bridge.pyin the ruffCORElist. Those files no longer exist on this branch, so that line needs a separate push withworkflowscope.Measured
This matches the bead's recorded baseline exactly on passed/skipped (2474P / 34S), with one fewer failure. The bead recorded
5F/2474P/34S; this branch measures4F. I am treating my own measurement as the baseline rather than quoting the bead's — the discrepancy is that the bead's 5th failure was atest_issue_bridgecase this branch fixes.All 4 remaining failures are pre-existing and environmental, not introduced here:
test_omni_relay_live×3src/tools/omni_relay.py:364→TransportError: upstream HTTP 500test_timeout_markerWhat the reorg broke, and how it was found
b372f7bmoved modules without following the tests' references to them. 16 failures:tests/test_leaf.pytests/test_anchor_loader.pytests/test_issue_bridge.pyTwo distinct traps, both now written into the
codebase-restructuringskill:import src.core.leafbindssrc, notleaf. The first rewrite oftest_anchor_loader.pyproducedNameError: name 'prompt_composer' is not definedinstead of fixing anything — the alias must survive (import src.tools.prompt_composer as prompt_composer).patch("module.fn")is a STRING target, so an import sweep cannot see it. It fails only at test-run time while collection stays green.Sweep coverage: a whole-repo import sweep found zero flat imports of moved modules outside
.scratch/. A whole-repo sweep for string references found 72 sites, of which 67 were false positives — evidence strings ("crew_dispatch.py:123"), argv (["teacher.py", …]), filenames ("leaderboard.html"), docstrings, and a vendored repo's owntrajectory.json. Each was checked individually rather than bulk-rewritten.Lint gate
The lint step was scoped to "core modules + any
.pychanged in this PR". A diff-scoped gate assumes a changed file is a file someone just wrote; the reorg rewrote intra-repo imports across the repo, so ~280 files were "changed" while only their import lines moved. The gate is now scoped to the canonical tree.review-gateis the only required check — the lint step lives in the non-requiredtestjob.Real defects the wider scope exposed: a phantom
__all__export insrc/core/persistence_contract.py:466(AUDIT_IDENTITY_FIELDS— the real name is_AUDIT_IDENTITY_FIELDS, soimport *would raiseAttributeError), andscripts/candidate_pr_contract.pycallingCandidateStore/SimpleNamespacewith no import.Deliberately NOT deleted
Two lint findings are missing features, not stale code. Deleting them would satisfy the linter while cementing the gap — both are filed as beads:
school-core-tkqn—generate_activity_dashboard.pybuildsdata = json.dumps(...)and never uses it, while the docstring promises "all data baked in".school-core-88jf—bookbag.pyconstructsBookbagSignal(bead, repo)and never reads it; the polling loop still reads the bookbag JSON, so the flag-file fast path is dead.Both are kept,
# noqa: F841, with the reason in the comment.Verification status
ruffgreen on the exact CI commands.What this does NOT fix
Do not merge yet: scheduled execution breaks, verification paths are wrong, and the new review veto can still allow publication.
Findings
Fix with agent prompt
Summary
Moves root modules into six
srcpackages and updates imports, tests, and lint scope. The diff also adds onboarding and A2A tools and changes Entire review gating.No runtime tests were executed during this review.
Acknowledgments from
branben: the dashboard's unused payload and the bookbag flag-file fast path are intentionally deferred asschool-core-tkqnandschool-core-88jf. The PR description also identifies three live-relay failures, the order-dependent timeout failure, and the full-suite malformed-JSON test failure as known or environmental; none is reported here.Reviews (1) · Last reviewed commit: "fix(reorg): follow the tests' flat impor..." · Reviewed by Greptile