Skip to content

fix(guards): an AST finder for the secrets wiring guard, a reason on every dropped watch-folder event, and the #17502 revert fixture (#17822, #17547, #17558) - #17955

Merged
mrveiss merged 14 commits into
mainfrom
issue-guard-ast-batch-17822
Oct 4, 2026
Merged

mrveiss merged 14 commits into
mainfrom
issue-guard-ast-batch-17822

Conversation

@mrveiss

@mrveiss mrveiss commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Thinking Path

Three issues that triage found were each one criterion from done, batched because
all three are guard-shaped and none of them shares a file with an open PR.

#17822 finding 1 — the mutation decided the design. The guard's finder was
if _SYMBOL not in source. The owner stated the killing mutation on #16982 on
2026-09-29, and the first thing I did was run it rather than assume it: remove the
real call in the coordinator, leave the docstring that describes it. The old
finder stayed GREEN, so the guard that asserts a secret-access rule is wired
passes with the rule deleted. That decided the fix: a docstring is an
ast.Constant in statement position and a comment is not in the tree at all, so
walking for Name / Attribute / alias separates prose from code by
construction rather than by a heuristic chasing how people write comments.

A first attempt at the mutation — return True inserted above the call — did
not kill the new guard, and that is recorded here rather than quietly fixed:
the call text is still in the AST as dead code. The issue's own wording ("remove
that call and leave a docstring that mentions it") is the mutation that bites,
and it is the one the result below reports.

The reference mechanisms are enumerated rather than guessed, because
MEASUREMENT_DISCIPLINE.md says a control witnesses only the shape it is written
in. getattr(obj, "symbol") is included as a string argument to a forwarding
builtin specifically — counting string constants generally would reinstate the
defect, since a docstring is one.

#17547 AC3 — the control was already in the tree. kb_folder_events.py binds
logger at module scope and makes zero logger. calls;
kb_folder_watcher.py, the module it was split out of, makes 30. Every early
return in _handle_change dropped a watch-folder event leaving nothing to read,
so "the KB ignored my file" and "watchdog never fired" are the same observation.
There are four such returns, not the three named in triage — the fourth is the
#15636 cross-thread hand-off failure, which is the one that is not routine, so it
is a warning where the other three are debug.

#17558 Gap 2 AC2 — the measurement changed the fix. The existing assertion is
the inverse ("OpenTelemetry is not reported"), which a check that reports nothing
ever satisfies. Building the revert fixture and running it reported three of
#17557's four packages. opentelemetry-proto was silent because both its
requirers say opentelemetry-proto==1.44.0, which a per-requirement test reads as
a structural cap.

It is not one. ==1.44.0 is what grpc 1.44.0's metadata says, and grpc itself is
unpinned once the fix is reverted — pip takes grpc 1.45.0, whose metadata says
==1.45.0, and proto moves with it. That is family F: a correct answer to "does
this requirement have an upper bound", read as an answer to "can this package's
version climb". floating_packages resolves it to a fixed point, and the blast
radius was measured before the rule was trusted: ['protobuf'] on the tree before
and after.

What Changed

File Issue Change
repo_tests/secret_access_check_is_wired_16982_test.py #17822 _references_symbol is an AST walk (Name / Attribute / alias / getattr-family string arg), parameterised by symbol so the positive control uses the same instrument. Nine-case contrast pair added.
autobot-backend/services/kb_folder_events.py #17547 All four early returns in _handle_change log folder, path, change type and reason. Routine drops debug; the failed hand-off warning.
autobot-backend/tests/services/test_kb_watch_events_17546.py #17547 Five behavioural tests (four reasons + a success-path contrast) and one structural test that fails if a fifth silent return is added.
tools/lint/check_ci_plane_unenforced_pins.py #17558 New floating_packages: a cap is structural only when the requirer stating it is itself held. unenforced_pairs consumes it.
repo_tests/ci_plane_unenforced_pins_17558_test.py #17558 TestRevertingTheFixReproducesIt (the #17502 revert against the real requirement files) and TestFloatingPackages (the rule as data). One pre-existing fixture made faithful, with the half it assumed now stated as its own test.

Deliberately not touched: .github/workflows/code-quality.yml — see the last
section.

Verification

Commands run with PATH=/home/martins/.venv-python-suite/bin:$PATH (python 3.14.6;
the system python3 is 3.10 and fails collection on StrEnum).

Suites — 101 passed across the five changed suites plus
code_quality_guard_reach_test.py, watchdog_thread_handoff_15636_test.py and
dependency_floor_report_test.py. Pre-push selected and passed 5 suites.

Mutation results — every new assertion was mutation-checked.

# Mutation Expected Observed
17822 Delete the secret.is_accessible_by(...) call in services/secrets_coordinator.py, leave the docstring (owner's mutation, #16982 2026-09-29) guard RED old finder GREEN ['secrets_coordinator.py'], new finder RED [] — test_the_access_check_is_wired_into_production_or_absent failed
17822 return True above the call, call text left in place — new guard GREEN. Recorded, not hidden: the call is still in the AST. The issue's own wording is the removal, which is the row above.
17547 Drop the debounce logger.debug 2 RED test_a_debounced_edit_says_it_was_coalesced_and_not_refused, test_every_return_in_handle_change_logs_first
17547 Hand-off failure warning → debug 1 RED test_a_failed_handoff_is_a_warning_naming_the_file
17547 Log unconditionally at the top of _handle_change ≥1 RED 5 RED, including the success-path contrast
17558 Revert floating_packages to the per-requirement cap test 3 RED test_a_cap_stated_by_a_floating_requirer_is_not_structural, test_all_four_are_reported_once_the_pin_is_reverted, test_floating_is_transitive
17558 floating_packages returns every closure package ≥1 RED 5 RED, incl. test_the_tree_currently_has_no_unbaselined_pair
17558 Delete the four pins from requirements-ci/storage.txt for real ≥1 RED 4 RED, incl. test_the_fix_being_reverted_is_actually_in_the_tree

Per-criterion evidence

#17822 — finding 1 (finding 2 was already delivered in #17772 and is untouched):

  • "_references_symbol walks AST nodes rather than matching text." — repo_tests/secret_access_check_is_wired_16982_test.py, _references_symbol walks ast.Name.id, ast.Attribute.attr, ast.alias.name/.asname and _forwards_by_name. No in source remains in the finder.
  • "Contrast pair: a module whose only mention is a docstring or comment must not match; one that calls or forwards it must." — test_prose_alone_is_not_a_reference (4 cases: the owner's mutation shape, a comment, a function docstring, a bare string literal) and test_real_code_is_a_reference (5 cases: attribute call, bare name, import-only, aliased import, getattr). test_the_pair_differs_only_in_prose_versus_code asserts both fixtures contain the literal text, so a raw-text finder cannot satisfy it.

#17547 — AC3 (AC1/AC2/AC4 were already delivered and are re-confirmed below):

  • AC1 "A modification to a file the KB has already ingested is processed after a restart." — on_modified is unconditional, kb_folder_events.py :51-68; pinned by test_a_modification_to_a_file_never_seen_before_is_dispatched.
  • AC2 "Membership is decided against durable state … or, if the dict stays as a debounce cache, it is no longer the authority" — the dict is read only by the 2.0s debounce, :139-141, written only at :169.
  • AC3 "A dropped event is observable: if a modification is deliberately ignored, the reason is logged once, not silently returned." — four logger calls, one per early return; test_every_return_in_handle_change_logs_first asserts 4 bare returns and 4 logging guards, so the count cannot drift.
  • AC4 "A file predating the watch-folder configuration is ingested on its first modification." — same code as AC1; no membership test remains on the modification path.

#17558 — Gap 1 (verified, not re-done) and Gap 2:

  • G1.1 "audit() returns how many declared packages were not installed, and render() states it on both the pass and the fail path." — FloorAudit.not_installed / .not_compared_declarations in pipeline-scripts/dependency_floor_report.py; uncompared is interpolated into both the all-satisfied block and the below-floor block.
  • G1.2 "A pass line can never claim a number larger than the comparisons actually made." — the pass line reads {result.compared} of {result.declared}, and compared counts only names present with readable metadata.
  • G1.3 "A test asserts that an environment missing a declared package reports it as not-compared … Contrast pair: an installed package at or above its floor is reported as compared and satisfied." — test_a_clean_pass_counts_comparisons_and_admits_what_it_skipped (128 declared / 86 compared / 42 absent) and test_clean_environment_says_how_many_were_checked (206/206).
  • G1.4 "--all lists the not-compared names, as it does shortfalls." — _print_report in pipeline-scripts/check_dependency_floors.py prints "Declared but not installed, therefore not compared" and each name under show_all.
  • G2.1 "The detector flags a package pinned in the production plane whose CI-plane presence comes only from an unbounded transitive requirement, as its own finding distinct from a constraint mismatch." — tools/lint/check_ci_plane_unenforced_pins.py is its own module with its own UnenforcedPair finding.
  • G2.2 "python-suite shard 1/12 red on every PR: CI installs opentelemetry 1.45.0 against its own ==1.44.0 pins #17557's four packages are the worked example and must be reported by the new check when the fix(deps): check that both declaration planes resolve the same, and let the floor check reach a venv it is not running in (#17448, #17449) #17502 pin is reverted in a fixture." — test_all_four_are_reported_once_the_pin_is_reverted asserts exactly the four; test_the_fix_being_reverted_is_actually_in_the_tree is the non-vacuity check that the thing being reverted is still present.
  • G2.3 "Shrink-only baseline … a new finding fails, and a stale entry fails." — BASELINE plus TestTheBaselineOnlyShrinks, both directions.
  • G2.4 "The check says which plane declares and which merely installs." — UnenforcedPair.describe(); test_the_report_names_which_plane_declares_and_which_merely_installs.

Model Used

Claude Opus 5 (1M context).

Not delivered here, and why

tools/lint/check_ci_plane_unenforced_pins.py has no --audit run step in
.github/workflows/code-quality.yml — 17 other check_*.py --audit steps are
there, and this checker's only mention in that file is a comment explaining why
packaging is installed for it. It is reached today only through
repo_tests/ci_plane_unenforced_pins_17558_test.py under python-suite.

That file is owned by an open PR in this queue, so it is left alone rather than
worked around. It is not an acceptance criterion of #17558 — both gaps' criteria
are satisfied above — and it is filed separately.

Closes #17822
Closes #17547
Closes #17558

Summary by CodeRabbit

  • Bug Fixes
    • Watch-folder events that are skipped or fail to be handed off are now recorded with clearer diagnostic details. Failed hand-offs can be retried without waiting for the debounce interval.
    • Dependency checks now more accurately identify packages that may float through transitive dependencies and distinguish production requirements from CI requirements.
    • Python code-reference checks now distinguish actual symbol references from mentions in comments, documentation and unrelated strings.

…every dropped watch-folder event, and the #17502 revert fixture (#17822, #17547, #17558)

#17822 finding 1 -- `repo_tests/secret_access_check_is_wired_16982_test.py`'s
`_references_symbol` was `if _SYMBOL not in source`, raw text over the whole
file. The owner's mutation on #16982 (2026-09-29): remove the real call and
leave the docstring that describes it, and the guard asserting a security rule
is wired stays green with the rule gone. Measured on that exact mutation --
old finder GREEN, new finder RED. The finder now walks `Name`, `Attribute`,
`alias` and `getattr`-family string arguments, with a nine-case contrast pair
(four prose shapes that must not match, five code shapes that must).

#17547 AC3 -- `services/kb_folder_events.py` bound `logger` and made zero
`logger.` calls, against 30 in the module it was split out of. All four early
returns in `_handle_change` dropped a watch-folder event with no trace, so "the
KB ignored my file" and "watchdog never fired" read the same. Each now names
the folder, path, change type and reason; the failed cross-thread hand-off
(#15636) is a warning where the three routine drops are debug. A structural
test fails if a fifth silent return is ever added.

#17558 Gap 2 AC2 -- only the inverse assertion existed, which a check that
reports nothing ever satisfies. Reverting #17502 against the real requirement
files reported three of #17557's four packages: `opentelemetry-proto` was
silent because both its requirers say `==1.44.0`, which reads as a cap. It is
not one -- those requirers are themselves unpinned once the fix is reverted, so
the cap travels with them. `floating_packages` resolves that to a fixed point.
Blast radius measured: the tree's reported set is `['protobuf']` before and
after, and the fixture now reports all four.

Closes #17822
Closes #17547
Closes #17558
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Too many files!

This PR contains 107 files, which is 7 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

Check out review usage here.

⚙️ Run configuration
  • Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b5a759dd-cbdc-446b-96b8-5a171c54c72a
📥 Commits

Reviewing files that changed from the base of the PR and between 7d11497 and 17c6587.

📒 Files selected for processing (107)
  • .github/workflows/ci.yml
  • autobot-backend/services/kb_folder_events.py
  • autobot-backend/tests/services/test_kb_watch_events_17546.py
  • autobot-frontend/index.html
  • autobot-frontend/src/App.vue
  • autobot-frontend/src/__tests__/safeAreaInsets.test.ts
  • autobot-frontend/src/assets/base.css
  • autobot-frontend/src/assets/css/design-tokens.css
  • autobot-frontend/src/components/CommandPalette.vue
  • autobot-frontend/src/components/__tests__/CommandPalette.nav.test.ts
  • autobot-frontend/src/components/knowledge/panels/SourcePreviewPanel.vue
  • autobot-frontend/src/components/ui/ToastContainer.vue
  • autobot-frontend/src/components/workflow/WorkflowCanvas.vue
  • autobot-frontend/src/config/navItems.ts
  • autobot-frontend/src/i18n/locales/ar.json
  • autobot-frontend/src/i18n/locales/de.json
  • autobot-frontend/src/i18n/locales/en.json
  • autobot-frontend/src/i18n/locales/es.json
  • autobot-frontend/src/i18n/locales/fa.json
  • autobot-frontend/src/i18n/locales/fr.json
  • autobot-frontend/src/i18n/locales/he.json
  • autobot-frontend/src/i18n/locales/lv.json
  • autobot-frontend/src/i18n/locales/pl.json
  • autobot-frontend/src/i18n/locales/pt.json
  • autobot-frontend/src/i18n/locales/ur.json
  • autobot-frontend/src/utils/safeAreaInsets.ts
  • autobot-frontend/src/views/AuditLogsView.vue
  • autobot-frontend/tsconfig.app.json
  • autobot-slm-frontend/.storybook/preview.ts
  • autobot-slm-frontend/src/components/AddNodeModal.vue
  • autobot-slm-frontend/src/components/DeploymentWizard.vue
  • autobot-slm-frontend/src/components/common/SkipLink.stories.ts
  • autobot-slm-frontend/src/components/common/SkipLink.vue
  • autobot-slm-frontend/src/components/skills/GovernanceModeSelector.stories.ts
  • autobot-slm-frontend/src/locales/ar.json
  • autobot-slm-frontend/src/locales/de.json
  • autobot-slm-frontend/src/locales/en.json
  • autobot-slm-frontend/src/locales/es.json
  • autobot-slm-frontend/src/locales/fa.json
  • autobot-slm-frontend/src/locales/fr.json
  • autobot-slm-frontend/src/locales/he.json
  • autobot-slm-frontend/src/locales/lv.json
  • autobot-slm-frontend/src/locales/pl.json
  • autobot-slm-frontend/src/locales/pt.json
  • autobot-slm-frontend/src/locales/ur.json
  • autobot-slm-frontend/src/utils/debugUtils.ts
  • autobot-slm-frontend/src/views/settings/MonitoringSettings.vue
  • autobot-slm-frontend/src/views/settings/admin/LogForwardingSettings.vue
  • autobot-slm-frontend/src/views/settings/admin/NPUWorkersSettings.vue
  • autobot-slm-frontend/src/views/settings/admin/__tests__/fetchFailureStates.test.ts
  • autobot-slm-frontend/tsconfig.json
  • libs/autobot-ui/index.ts
  • libs/autobot-ui/src/components/SkipLink.vue
  • libs/autobot-ui/src/components/__tests__/SkipLink.test.ts
  • pipeline-scripts/release_sync_main.py
  • pytest.ini
  • repo_tests/_glob_declared_uncovered.py
  • repo_tests/_guard_reach_census.py
  • repo_tests/_reach.py
  • repo_tests/_reach_policy.py
  • repo_tests/_reach_scope.py
  • repo_tests/ansible_backend_path_defects_15560_test.py
  • repo_tests/code_lines_helper_17941_test.py
  • repo_tests/direct_secrets_service_callers_17773_test.py
  • repo_tests/doc_index_worktree_contamination_16934_test.py
  • repo_tests/docs_liquid_tags_are_defined_17930_test.py
  • repo_tests/dpkg_tests_installed_state_not_knowledge_17387_test.py
  • repo_tests/draft_gate_reaches_aggregators_17957_test.py
  • repo_tests/enum_hand_copy_guard_test.py
  • repo_tests/env_var_bare_cast_test.py
  • repo_tests/frontend_fragmentation_ratchet_test.py
  • repo_tests/git_repo_root_calls_are_guarded_17418_test.py
  • repo_tests/guard_reach_floor_window_17818_test.py
  • repo_tests/guard_reach_meta_test.py
  • repo_tests/hardcoded_values_scope_agreement_test.py
  • repo_tests/i18n_untranslated_ratchet_test.py
  • repo_tests/install_prompts_survive_piped_stdin_17929_test.py
  • repo_tests/kb_admin_read_bypass_guard_test.py
  • repo_tests/kb_read_visibility_guard_test.py
  • repo_tests/man_page_indexer_path_resolves_15853_test.py
  • repo_tests/model_revision_pinning_enforced_17804_test.py
  • repo_tests/no_secret_in_response_model_17865_test.py
  • repo_tests/one_claim_registry_16653_test.py
  • repo_tests/one_comment_stripper_17941_test.py
  • repo_tests/parked_branch_merge_workflow_test.py
  • repo_tests/provision_playbook_path_agrees_15707_test.py
  • repo_tests/reach_allowance_verdicts_17914_test.py
  • repo_tests/reach_declarations_test.py
  • repo_tests/reach_floor_migration_test.py
  • repo_tests/reach_floor_window_17356_test.py
  • repo_tests/reach_scope_claim_17844_test.py
  • repo_tests/redis_config_path_is_canonical_17434_test.py
  • repo_tests/relative_reach_floor_17142_test.py
  • repo_tests/release_sync_main_test.py
  • repo_tests/router_auth_coverage_test.py
  • repo_tests/scan_secrets_hook_test.py
  • repo_tests/script_importing_services_reaches_repo_root_17452_test.py
  • repo_tests/secret_access_check_is_wired_16982_test.py
  • repo_tests/slm_frontend_i18n_parity_test.py
  • repo_tests/slm_frontend_publish_contract_test.py
  • repo_tests/slm_self_update_socket_activation_order_test.py
  • repo_tests/store_authority_test.py
  • repo_tests/store_fact_chokepoint_guard_test.py
  • repo_tests/tts_address_env_names_17782_test.py
  • tools/git-hooks/pre-push
  • tools/lint/_comment_syntax.py
  • tools/lint/_scan_helpers.py

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This pull request adds diagnostic logs for dropped watch-folder events, updates CI dependency-pin detection, and changes the secret-access wiring guard to identify code references through Python AST inspection.

Changes

Watch-folder event diagnostics

Layer / File(s) Summary
Log and test dropped watch-folder events
autobot-backend/services/kb_folder_events.py, autobot-backend/tests/services/test_kb_watch_events_17546.py
The handler logs unsupported extensions, disabled file types, debounced events, and failed dispatch. Tests check these log records and confirm successful dispatch does not produce a drop log.

CI dependency-pin detection

Layer / File(s) Summary
Propagate floating status and test pin findings
tools/lint/check_ci_plane_unenforced_pins.py, repo_tests/ci_plane_unenforced_pins_17558_test.py
floating_packages propagates floating status through the installed dependency closure. unenforced_pairs uses that result to report production-bounded packages that float in CI. Tests cover pin behavior and the OpenTelemetry regression case.

Secret-access wiring guard

Layer / File(s) Summary
Find code references with AST inspection
repo_tests/secret_access_check_is_wired_16982_test.py
The reference finder recognises names, attributes, imports, and selected dynamic forwarding calls. The visibility sweep uses the AST finder. Tests distinguish executable references from comments, docstrings, and other strings.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🔵 Low · up to 7d114

Watch-folder drops are now logged, and dependency-pin checks are improved. The secret-access wiring guard can still pass when the access check is unreachable, so it does not fully protect against that regression. This is acceptable to merge with a follow-up.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 7d114

The changes improve repository checks and make dropped folder events diagnosable without showing new access authority. No new security vulnerability was established, but operational log exposure and end-to-end enforcement remain incompletely assessed.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The new diagnostic sink receives folder IDs, basenames, change types, extension configuration and timing or handoff status. It does not receive file contents or expanded file paths. Operational exposure of that metadata depends on log access and retention policies not supplied for this review.

Trust Boundaries and Controls

  • inferred — The supplied logging-only changed ranges do not establish a new cross-folder route or greater ingestion authority. Folder-specific registration, identity propagation and configured collection resolution remain the relevant controls; this does not establish complete tenant-isolation assurance.

Resilience and Maintainability Implications

  • inferred — The new warning makes failed cross-thread scheduling observable without falsely marking the event handled. Later asynchronous failure remains outside the handler's acknowledgment contract; no PR-introduced change to that recovery boundary was established.

Hardening Proposals

  • proposed — If the wiring guard is used as authorization assurance, complement it with secret-read allow and deny tests that demonstrate execution of the intended control. This addresses the guard's structural limitation, not a verified vulnerability introduced by this PR.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning #17822: The AST finder and prose-versus-code tests meet finding 1. Finding 2 remains unmet: SecretsCoordinator.read uses session.get(Secret, secret_id), which can load legacy rows before checking … Restrict the secret pre-check query to rows with sealed_value IS NOT NULL. Keep missing-secret responses at 404 and scope refusals at 403, and add tests that assert both outcomes.
Docstring Coverage ⚠️ Warning Docstring coverage is 73.81% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 42 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately identifies the AST-based secrets guard, watch-folder drop logging and dependency-pin revert fixture. It is long, but it clearly describes the main changes.
Out of Scope Changes check ✅ Passed The changes map to the linked objectives: #17822's wiring guard, #17547's watch-folder event handling, and #17558's dependency checks. The supporting tests and fixed-point handling directly support th…
Full details: Linked Issues check

Explanation

#17822: The AST finder and prose-versus-code tests meet finding 1. Finding 2 remains unmet: SecretsCoordinator.read uses session.get(Secret, secret_id), which can load legacy rows before checking sealed_value; the issue requires the pre-check query to be restricted to envelope rows. The code returns not-found for absent or legacy rows and access-denied for a scope refusal, but the unfiltered lookup fails the separate query requirement. #17547: on_modified now dispatches without the per-process membership gate. _handle_change logs all four dropped-event reasons, and the PR summary reports behavioural and structural tests. #17558: The reported floor audit and tests cover Gap 1. The detector and revert fixture cover Gap 2, including floating requirers, the four-package example, and the baseline contract. The missing workflow --audit step is not an issue acceptance criterion.

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mrveiss

mrveiss commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

Follow-up filed for the gap named under Not delivered here: #17956 (the missing --audit step for check_ci_plane_unenforced_pins.py). Not an acceptance criterion of #17558; its file is owned by another open PR.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No new hardcoded values of either class — ssot and other both block.

Known backlog in pipeline-scripts/hardcoded_values_baseline.txt is suppressed and tracked in #14371.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 1e3f09ea-56c6-45e9-9de6-d8c07c6ee0f8
📥 Commits

Reviewing files that changed from the base of the PR and between 0a4cffc and 7d11497.

📒 Files selected for processing (5)
  • autobot-backend/services/kb_folder_events.py
  • autobot-backend/tests/services/test_kb_watch_events_17546.py
  • repo_tests/ci_plane_unenforced_pins_17558_test.py
  • repo_tests/secret_access_check_is_wired_16982_test.py
  • tools/lint/check_ci_plane_unenforced_pins.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread autobot-backend/services/kb_folder_events.py Outdated
Comment thread autobot-backend/tests/services/test_kb_watch_events_17546.py Outdated
Comment thread repo_tests/secret_access_check_is_wired_16982_test.py Outdated
Comment thread repo_tests/secret_access_check_is_wired_16982_test.py
mrveiss added 12 commits October 4, 2026 19:23
…17547)

Review found the same defect at two sites: a detector that counts a call
without asking whether execution reaches it.

secret_access_check_is_wired_16982_test
  `_references_symbol` walked the whole AST, so `return True` with the access
  check left underneath still read as "wired" -- the owner's #17822 mutation
  in its most faithful form, and the one the comment-stripping fix did not
  cover. Walking now prunes each statement list after a terminator.
  Syntactic reachability only: an `if False:` body still counts, because
  guessing in the permissive direction is how a guard goes vacuous.

  `_forwards_by_name` scanned every argument of `getattr`/`setattr`/`hasattr`/
  `delattr`, so `getattr("is_accessible_by", "other")` matched on the *object*
  position. Only the attribute-name position counts now.

  Contrast fixtures for both, plus two permissive controls (a call after a
  conditional return, and one after a sibling function returns) so the pruning
  cannot quietly swallow live references. Verified against the old detector:
  all three mutations read True before and False after; all four controls
  stay True.

test_kb_watch_events_17546
  `test_every_return_in_handle_change_logs_first` asked whether a `logger`
  call appeared anywhere in the same block as the bare return. A call placed
  below the return satisfied that and can never execute, so the guard accepted
  exactly the silent drop it exists to forbid. `_drop_sites` now counts only
  statements preceding the first bare return, with a three-way contrast
  fixture pinning the ordering.

kb_folder_events
  The four drop logs named `path.name`, so two files called `notes.txt` in
  different subfolders of a recursive watch produced identical lines and the
  log could not say which file was lost. They now log the path relative to the
  watched root -- specific enough to identify the file, without putting the
  host's filesystem layout in a log. A path outside the root degrades to the
  basename rather than leaking the root it sits under.

Refs #17822, #17547
… not just the floor (#17356, #17818, #17870, #16987)

Closes #16987
Closes #17356
Closes #17818
Closes #17870

Also carries the draft-gate aggregator fix (#17957): `python-suite` read twelve
draft-skipped shards and re-asserted `result != 'success'`, so every draft PR
reported red for work deliberately not run. Guard added, parsed from YAML.

Refs #17844, #17957, #17975
… not just the floor (#17356, #17818, #17870, #16987)

Closes #16987
Closes #17356
Closes #17818
Closes #17870

Also carries the draft-gate aggregator fix (#17957): `python-suite` read twelve
draft-skipped shards and re-asserted `result != 'success'`, so every draft PR
reported red for work deliberately not run. Guard added, parsed from YAML.

Refs #17844, #17957, #17975
The defect class is "a text-keyed guard is satisfied by its own comment". The
ENABLER is that stripping comments was reimplemented per guard, so each new one
rediscovered the hazard, re-solved it, or forgot -- and forgetting is silent,
because the guard then passes. Fixing witnesses one at a time was never going
to converge; this fixes the enabler.

#17941 catalogued seven copies. An AST sweep of the live tree found 43. The
catalogue was not the population, which is the same duplication problem one
level up.

CANONICAL HOME, and I nearly got this wrong. The first draft added
`repo_tests/_code_lines.py` -- fork #44, written while fixing the forks.
`tools/lint/_scan_helpers.py` is already the shared scan module, with 51
repo_tests files importing it, and it already owns `logical_lines` for
backslash continuations. So the new module sits beside it as
`tools/lint/_comment_syntax.py`, and its continuation folding DELEGATES to
`logical_lines` rather than reimplementing it. `_scan_helpers` is 489 lines
against a 600 ceiling, which is why this is a neighbour and not an append.

WHAT THE SHARED ONE DOES THAT NONE OF THE PRIVATE ONES DID. Every copy dropped
whole-line comments only, and `git_repo_root_calls_are_guarded_17418_test`
stated the reason honestly: a `#` inside a string is not a comment, and a regex
ignoring that reintroduces the class one level down. That reasoning was right,
and it is why this scans character by character tracking quote state. It also
handles block comments with the same tracking, which replaces the
line-anchoring heuristic `slm_frontend_i18n_parity_test` needed because
`import.meta.glob('../locales/*.json')` contains a block-comment opener -- the
anchor approximated "not inside a string"; tracking quotes answers it.

Heredocs are NOT parsed, so `strip_trailing=` defaults to False and a caller
opts into the cleverness. The conservative default is what the migrated sites
already relied on.

All seven listed call sites migrated; none keeps a private copy. One is a real
fix rather than a move: `dpkg_tests_installed_state_not_knowledge_17387_test`
compared the position of a comment marker against the match position, which is
not quote-aware, so a marker inside a string before the match skipped the real
invocation -- a miss in the permissive direction.

THE META-GUARD is AST-based, because a text scan for the stripping idiom
matches its own source and every allowlist entry -- the #17941 defect
reproducing inside its own fix. It ratchets: `_PENDING_PRIVATE_STRIPPERS` holds
the 43 measured entries, a new copy fails `test_no_new_file_strips_comments_by_hand`,
and a migrated file left listed fails `test_the_pending_list_only_shrinks`.
That second test compares the record against the LIVE tree rather than against
a mirrored record, which is #17970's point: a mirrored pair detects
transcription errors, not movement.

The detector excludes `assert` subtrees after a false positive on
`git_repo_root_calls_are_guarded_17418_test:173`, which ASSERTS a fixture line
is a comment rather than stripping one. A guard with false positives gets
muted, which costs more than the copy it would have caught.

Controls: one per syntax claimed, both directions per case (what is removed and
what survives), the quote-awareness matrix, the glob that broke the heuristic,
line-number preservation, and a contrast set for the meta-guard including the
two prose shapes its own file contains.

Refs #17941, #13916, #17312
…shared SkipLink serves both consoles (#17561, #17565, #17958)

Closes #14771
Closes #17561
Closes #17565
Closes #17958

Also carries the fix for a review finding in the #17982/#17983 class: the NPU and
Log Forwarding panels now track a successful load, so a failed fetch is no longer
indistinguishable from defaults. Log Forwarding shows "Unknown" and disables
Start/Stop rather than claiming "stopped".

Refs #17984
…every dropped watch-folder event, and the #17502 revert fixture (#17822, #17547, #17558)

#17822 finding 1 -- `repo_tests/secret_access_check_is_wired_16982_test.py`'s
`_references_symbol` was `if _SYMBOL not in source`, raw text over the whole
file. The owner's mutation on #16982 (2026-09-29): remove the real call and
leave the docstring that describes it, and the guard asserting a security rule
is wired stays green with the rule gone. Measured on that exact mutation --
old finder GREEN, new finder RED. The finder now walks `Name`, `Attribute`,
`alias` and `getattr`-family string arguments, with a nine-case contrast pair
(four prose shapes that must not match, five code shapes that must).

#17547 AC3 -- `services/kb_folder_events.py` bound `logger` and made zero
`logger.` calls, against 30 in the module it was split out of. All four early
returns in `_handle_change` dropped a watch-folder event with no trace, so "the
KB ignored my file" and "watchdog never fired" read the same. Each now names
the folder, path, change type and reason; the failed cross-thread hand-off
(#15636) is a warning where the three routine drops are debug. A structural
test fails if a fifth silent return is ever added.

#17558 Gap 2 AC2 -- only the inverse assertion existed, which a check that
reports nothing ever satisfies. Reverting #17502 against the real requirement
files reported three of #17557's four packages: `opentelemetry-proto` was
silent because both its requirers say `==1.44.0`, which reads as a cap. It is
not one -- those requirers are themselves unpinned once the fix is reverted, so
the cap travels with them. `floating_packages` resolves that to a fixed point.
Blast radius measured: the tree's reported set is `['protobuf']` before and
after, and the fixture now reports all four.

Closes #17822
Closes #17547
Closes #17558
…17547)

Review found the same defect at two sites: a detector that counts a call
without asking whether execution reaches it.

secret_access_check_is_wired_16982_test
  `_references_symbol` walked the whole AST, so `return True` with the access
  check left underneath still read as "wired" -- the owner's #17822 mutation
  in its most faithful form, and the one the comment-stripping fix did not
  cover. Walking now prunes each statement list after a terminator.
  Syntactic reachability only: an `if False:` body still counts, because
  guessing in the permissive direction is how a guard goes vacuous.

  `_forwards_by_name` scanned every argument of `getattr`/`setattr`/`hasattr`/
  `delattr`, so `getattr("is_accessible_by", "other")` matched on the *object*
  position. Only the attribute-name position counts now.

  Contrast fixtures for both, plus two permissive controls (a call after a
  conditional return, and one after a sibling function returns) so the pruning
  cannot quietly swallow live references. Verified against the old detector:
  all three mutations read True before and False after; all four controls
  stay True.

test_kb_watch_events_17546
  `test_every_return_in_handle_change_logs_first` asked whether a `logger`
  call appeared anywhere in the same block as the bare return. A call placed
  below the return satisfied that and can never execute, so the guard accepted
  exactly the silent drop it exists to forbid. `_drop_sites` now counts only
  statements preceding the first bare return, with a three-way contrast
  fixture pinning the ordering.

kb_folder_events
  The four drop logs named `path.name`, so two files called `notes.txt` in
  different subfolders of a recursive watch produced identical lines and the
  log could not say which file was lost. They now log the path relative to the
  watched root -- specific enough to identify the file, without putting the
  host's filesystem layout in a log. A path outside the root degrades to the
  basename rather than leaking the root it sits under.

Refs #17822, #17547
The defect class is "a text-keyed guard is satisfied by its own comment". The
ENABLER is that stripping comments was reimplemented per guard, so each new one
rediscovered the hazard, re-solved it, or forgot -- and forgetting is silent,
because the guard then passes. Fixing witnesses one at a time was never going
to converge; this fixes the enabler.

#17941 catalogued seven copies. An AST sweep of the live tree found 43. The
catalogue was not the population, which is the same duplication problem one
level up.

CANONICAL HOME, and I nearly got this wrong. The first draft added
`repo_tests/_code_lines.py` -- fork #44, written while fixing the forks.
`tools/lint/_scan_helpers.py` is already the shared scan module, with 51
repo_tests files importing it, and it already owns `logical_lines` for
backslash continuations. So the new module sits beside it as
`tools/lint/_comment_syntax.py`, and its continuation folding DELEGATES to
`logical_lines` rather than reimplementing it. `_scan_helpers` is 489 lines
against a 600 ceiling, which is why this is a neighbour and not an append.

WHAT THE SHARED ONE DOES THAT NONE OF THE PRIVATE ONES DID. Every copy dropped
whole-line comments only, and `git_repo_root_calls_are_guarded_17418_test`
stated the reason honestly: a `#` inside a string is not a comment, and a regex
ignoring that reintroduces the class one level down. That reasoning was right,
and it is why this scans character by character tracking quote state. It also
handles block comments with the same tracking, which replaces the
line-anchoring heuristic `slm_frontend_i18n_parity_test` needed because
`import.meta.glob('../locales/*.json')` contains a block-comment opener -- the
anchor approximated "not inside a string"; tracking quotes answers it.

Heredocs are NOT parsed, so `strip_trailing=` defaults to False and a caller
opts into the cleverness. The conservative default is what the migrated sites
already relied on.

All seven listed call sites migrated; none keeps a private copy. One is a real
fix rather than a move: `dpkg_tests_installed_state_not_knowledge_17387_test`
compared the position of a comment marker against the match position, which is
not quote-aware, so a marker inside a string before the match skipped the real
invocation -- a miss in the permissive direction.

THE META-GUARD is AST-based, because a text scan for the stripping idiom
matches its own source and every allowlist entry -- the #17941 defect
reproducing inside its own fix. It ratchets: `_PENDING_PRIVATE_STRIPPERS` holds
the 43 measured entries, a new copy fails `test_no_new_file_strips_comments_by_hand`,
and a migrated file left listed fails `test_the_pending_list_only_shrinks`.
That second test compares the record against the LIVE tree rather than against
a mirrored record, which is #17970's point: a mirrored pair detects
transcription errors, not movement.

The detector excludes `assert` subtrees after a false positive on
`git_repo_root_calls_are_guarded_17418_test:173`, which ASSERTS a fixture line
is a comment rather than stripping one. A guard with false positives gets
muted, which costs more than the copy it would have caught.

Controls: one per syntax claimed, both directions per case (what is removed and
what survives), the quote-awareness matrix, the glob that broke the heuristic,
line-number preservation, and a contrast set for the meta-guard including the
two prose shapes its own file contains.

Refs #17941, #13916, #17312
…17941)

Two of the three are behaviour fixes, not moves.

`release_sync_main._uncommented` stripped trailing comments with
`line.split(" #", 1)[0]`, which is not quote-aware: `echo "a # b"` was
truncated at its own data. The shared scanner tracks quote state, so only a
marker outside quotes ends a line.

`doc_index_worktree_contamination` skipped whole-line comments but not
trailing ones, so `exec thing  # the old doc-sync path` read as a live
invocation. Its own docstring says a comment noting the #16934 move is fine --
that has to hold for a trailing comment too.

`ansible_backend_path_defects` is the straight move, plus trailing-comment
coverage it did not have.

THE LIST IS NOT A BATCH. Reading the 43 showed three different shapes wearing
one idiom, and migrating them alike would be an error in the other direction:

  (a) source scanners -- carry the #17941 hazard; migrate.
  (b) format parsers -- `requirements.txt`, the CodeQL ceiling list, a
      distributions list. A leading `#` is part of the FORMAT. The concept is
      "parse a config line", not "ignore a comment"; forcing them onto a
      comment-syntax helper conflates two concepts, which is this module's own
      error pointed the other way. They need their own shared parser.
  (c) comments as data -- `detect-hardcoded-values_test` extracts a comment
      HEADER, and `comment_line_number_citations_test`'s whole subject is
      comment line numbers. Migrating these deletes their input.

That classification is now recorded in the allowlist, so a later pass does not
read a shrinking count as licence to batch the remainder.

Also worth recording: my first attempt at these three inserted the import by
matching the last import-looking LINE, which landed it inside a string literal
-- a fake script a test writes to disk -- and the module then failed with
NameError. Text matching satisfied by something that is not code, in the
commit removing text matching satisfied by something that is not code. The
insertion now walks `tree.body` and anchors to a real top-level import.

Allowlist re-derived from the live tree rather than hand-edited: 40 entries.

Refs #17941, #13916
…17941)

Four files, five copies -- `man_page_indexer_path_resolves` had two, and the
ratchet is what found the second.

One is a behaviour fix. `store_authority_test` split baseline entries on
`line.split("#", 1)[0]`, which is not quote-aware, so an entry containing a
quoted `#` was truncated at its own data. The shared scanner tracks quote
state.

`scan_secrets_hook_test` gains trailing-comment coverage it did not have: a
hook line ending `# the old hex-escape form` named the needle without using
it, and the guard read that as an offence.

`man_page_indexer_path_resolves` and `slm_self_update_socket_activation_order`
are straight moves. The latter's own docstring already records why it strips
at all -- "a text scan cannot tell code from prose about code" -- which is
#17941 stated by a guard that then wrote its own copy anyway. That is the
enabler in one file: the hazard was understood, and the fix was still local.

THE RATCHET CAUGHT MY OWN INCOMPLETE MIGRATION. After migrating four files the
count went 40 -> 37, not 36. `man_page_indexer_path_resolves` stayed listed
because it had a SECOND private copy sixty lines below the first, which I had
not read. `test_the_pending_list_only_shrinks` compares the record against the
live tree, so a file that still strips by hand cannot be delisted -- the
discrepancy was the signal, and a mirrored-record check would have shown two
agreeing numbers and nothing wrong (#17970).

Allowlist re-derived from the tree after each step, never hand-edited.

Refs #17941, #13916
…rded (#17941)

`hardcoded_values_scope_agreement`, `parked_branch_merge_workflow` and
`release_sync_main_test` onto the shared stripper. The last two performed the
IDENTICAL operation -- strip comments from a workflow `run:` body -- in two
files, which is the duplication in miniature: not a subtle fork, the same four
lines twice.

A FOURTH CLASS, found by trying and failing. `check_ci_system_package_
provisioning` cannot use the shared helper and that is correct, not laziness.
It runs in a CI job that installs linters and NOT the application's
dependencies, and `ci_system_package_provisioning_test` enforces this with a
stdlib-only import check, plus a SECOND test keeping its one exemption
(`_scan_helpers`) dependency-free so the exemption cannot launder new imports.

Importing `_comment_syntax` there means relaxing both guards to buy a straight
move that fixes no bug at that site. I reverted it and recorded the reason in
the allowlist as class (d), dependency-constrained.

Worth stating plainly, because the goal is one shared implementation: that
goal has a real boundary. A module that must run in a minimal environment
cannot take a dependency on a shared helper merely to be tidy, and a guard
that forces it to would be trading a deliberate constraint for uniformity. The
allowlist now distinguishes four classes -- migrate, format parser, comments
as data, dependency-constrained -- so a later pass argues each entry rather
than batching toward zero.

Refs #17941, #13916
The rebase onto the new main rewrote commits that were already pushed, so the
push was refused as non-fast-forward. Merging the published tip back in rather
than force-pushing: the two sides are byte-identical on every file they share
(verified file by file against origin/main), so this reconciles the histories
without rewriting anything that was published.

# Conflicts:
#	repo_tests/one_comment_stripper_17941_test.py
@mrveiss
mrveiss merged commit 90ceed7 into main Oct 4, 2026
81 checks passed
@mrveiss
mrveiss deleted the issue-guard-ast-batch-17822 branch October 4, 2026 20:07
@mrveiss

mrveiss commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

Correction: the squash message for this merge overstates what landed

I wrote the merge commit body for 90ceed7ca2 and it claims two consolidations. Only one of them is in main.

Actually merged — the comment-stripper work:

tools/lint/_comment_syntax.py on origin/main : 242 lines, present

NOT merged, despite the message saying so — the configure_logging consolidation:

def configure_logging in tools/lint/_scan_helpers.py on origin/main : 0
private copies still on main                                        : 13

The cause: I committed that work locally as a78756ee07 and merged this PR at 17c6587d36, which predates it. The commit was never pushed, so the merge could not contain it, and I wrote the message from what I had done rather than from what the branch tip held.

That is the same error class this PR is full of fixes for — asserting a state instead of measuring it — committed in the sentence describing the fixes. I caught it only because I checked main for the canonical definition afterwards instead of trusting the merge.

What is true as of now

claim in the merge message status
_comment_syntax.py is the one comment stripper true — in main
11 private copies migrated, ratchet pins the remaining 33 true — in main
four were behaviour fixes, not moves true — in main
configure_logging replaces 13 byte-identical copies NOT in main — still 13 copies
a guard fails when a checker re-declares a _scan_helpers helper NOT in main

What happens to the unmerged half

a78756ee07 stays on issue-guard-ast-batch-17822 and goes out with the next push on that branch, under #13916 rather than claimed retroactively here. I am not editing the merge commit on main to match; a wrong message that is corrected in the open is better than a rewritten history that hides the mistake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment