Repository navigation
fix(updater): re-detect ansible_dir per execute_playbook call, not once at construction (#17150) - #17163
Conversation
…ce at construction (#17150) PlaybookExecutor is a module-level lazy singleton (get_playbook_executor): constructed once per SLM backend process, on whichever API call first touches it. __init__ resolved ansible_dir once -- preferring code_source's ansible directory, falling back to the deployed copy when that directory did not exist yet -- and never looked again. A process that started under the fallback stayed pinned to it for the rest of its life, even after code_source was brought fully current by the code-sync API. Restarting the process was the only way to clear it. Extracted the resolution into _auto_detect_ansible_dir() (module-level DEFAULT_CODE_SOURCE_ANSIBLE_DIR/FALLBACK_ANSIBLE_DIR constants so tests can monkeypatch either candidate without touching real /opt/autobot) and _refresh_ansible_dir(), called at the top of execute_playbook(), BEFORE _update_code_source() rather than after: _update_code_source derives its OWN sync target from self.ansible_dir (two directories up), so syncing first against a stale fallback would target the wrong directory too (/opt/autobot, which has no .git, instead of /opt/autobot/code_source, which does) -- silently reporting "nothing to sync" without ever touching the real checkout. This is the second, compounding half of the bug that a naive "just re-check after syncing" fix would have left in place; see playbook_executor_ansible_dir_self_heal_17150_test.py's test_a_stale_fallback_makes_update_code_source_target_the_wrong_directory. An explicit ansible_dir passed to the constructor (tests, wizard provisioning) is never auto-refreshed -- that is a deliberate caller choice, not the auto-detected default this instance would otherwise track. Tests (new file, 4 tests): the module is loaded from disk like its playbook_executor_timeout_14524_test.py sibling, since the package conftest stubs services.* and a plain import would exercise nothing. Core positive test calls the REAL execute_playbook() end-to-end (not just the extracted resolver) so a future change that moves the refresh call to the wrong spot is also caught. Negative control verified by hand: removed the self._refresh_ansible_dir() call, re-ran the test, confirmed it fails with "Playbook not found" at the STALE fallback path rather than the fresh code_source path -- restored the fix, re-ran, confirmed green (18 passed total including the untouched sibling suite). Sibling candidates from the filed issue, both still unexcluded as independent causes of the same symptom, handled as follows rather than silently dropped: - The silently non-fatal git fetch/reset failure at _update_code_source's no-.git early-return: this IS what this fix addresses -- once ansible_dir is correctly re-detected before _update_code_source runs, that method's own directory derivation is correct too, so it no longer silently no-ops against the wrong tree. - AUTOBOT_GIT_BRANCH pointing at a branch other than main: left alone, deliberately. This is a legitimate, working operator override (same philosophy as SLM_ANSIBLE_DIR) for tracking a non-default branch; a misconfigured value is an operational concern, not a code defect this issue's scope covers. Not fixing it here. Reach floors measured directly, not via a local guard pass (#17144): prompt-injection-detector-strict-mode is unaffected (scoped to autobot-backend/*.py only; this change is entirely under autobot-slm-backend/) -- confirmed 3245, unchanged from main. hooks-path- override confirmed safe at its current 6488 (population 6886 with the new test file staged, ceiling 6889) -- no re-pin needed. File-size ratchet: playbook_executor.py re-pinned 1362 -> 1406 for this fix's own lines (both baselines updated). Host confirmation is explicitly out of scope for this PR: per the owner's drift check relayed during this work, code_source on the affected host is already at origin/main's current commit while the deployed tree sits six merges behind, at last night's .deployed_commit -- exactly this bug's signature. This PR fixes the caching defect and proves the self-heal at the unit level; whether a real code-sync now advances .deployed_commit past that stale value is host evidence only the owner can produce by running an actual sync through the builtin updater.
|
Warning Review limit reachedNext included review available in 46 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (1)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthrough
ChangesAnsible directory self-healing
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Sequence Diagram(s)sequenceDiagram
participant PlaybookExecutor
participant DirectoryDetection
participant CodeSourceSync
PlaybookExecutor->>DirectoryDetection: Refresh ansible_dir and inventory_path
DirectoryDetection-->>PlaybookExecutor: Return detected directory
PlaybookExecutor->>CodeSourceSync: Update code source using refreshed directory
Merge Risk: ⚪ Minimal · up to The updated directory refresh behavior preserves explicitly configured paths and intentionally rechecks automatic selections before execution. No actionable merge-blocking risk remains. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
✅ SSOT Configuration Compliance: Passing🎉 No new hardcoded values of either class — Known backlog in |
…ouble (#17150) test_self_update_systemd_detach_11492.py's _executor() helper constructs a PlaybookExecutor via __new__ to skip __init__'s env probing, setting only ansible_dir by hand. execute_playbook() now calls _refresh_ansible_dir() at its top, which reads _ansible_dir_explicit -- an attribute that bypass construction never created, so the two tests driving execute_playbook() end-to-end (test_a_detached_run_leaves_both_staged_files_on_disk, test_an_attached_run_still_cleans_both_staged_files) failed with AttributeError on CI's python-suite shard 6/12. Setting it to True matches the semantic exactly: a hand-set ansible_dir is an explicit choice, never auto-refreshed. Confirmed the other three test files using the same __new__ bypass pattern (test_dynamic_inventory_ group_vars_11781.py, test_extra_vars_file_11735.py, test_wizard_inventory_ parity_14286.py) never call execute_playbook() on their bypassed instances, so none of them hit this gap. 24/24 in the fixed file pass, 50/50 across the full affected set. Filed #17164 for an unrelated, pre-existing collection failure found while checking the other three files (test_wizard_inventory_parity_14286.py's own ad-hoc module loader breaks on services.role_manifest_shape -- zero diff between main and this branch for any file involved).
…ookexecutor-caches-ansible-dir
…-caches-ansible-dir' into issue-17150-bug-playbookexecutor-caches-ansible-dir
…691-line ceiling (#17150) The prior fix's +3 lines put this grandfathered file 3 over its ceiling (#14236: an exemption freezes the size it was granted, it does not license more). Folded the two statements the fix needs onto one line via tuple assignment (ex.ansible_dir, ex._ansible_dir_explicit = ansible_dir, True) with the rationale as its own trailing comment, matching the existing __new__ line's own trailing-comment style one line above. Net zero lines added versus the pre-#17150 file; still 691 exactly. 42/42 relevant tests still pass. black/flake8 clean.
Refs #17150 (not Closes: the host-confirmation criterion below needs the owner to run a real sync)
Thinking Path
PlaybookExecutoris a module-level lazy singleton (get_playbook_executor): constructed once per SLM backend process, on whichever API call first touches it.__init__resolvedansible_dironce — preferring code_source's ansible directory, falling back to the deployed copy when that directory did not exist YET — and never looked again. A process that started under the fallback stayed pinned to it for its entire lifetime, even after code_source was brought fully current by the code-sync API (the actual mechanism that populatescode_source;_update_code_sourcehere is only a pre-playbook freshness safety net, not what creates it). Restarting the process was the only way to clear it.This is not a hypothetical. Per the owner's drift check on the live host (read-only):
origin/mainand/opt/autobot/code_sourceare both at358606d25— code_source is byte-current — while.deployed_commitsits at1b9f51432840(last night's #17048 merge), six merges behind. Verified per-file:ai_api_server.pyhas the #16428_validation_error_without_inputhandler incode_sourceand not in the deployed copy;worker_settings.pyhas 7 matches for the #17087 weight pins incode_sourceand 0 deployed. The live ai-stack container is missing #16428's handler, so its 422 responses echo the submitted payload verbatim — a merged security fix sitting on disk, not running, for a day. This is the mechanism, not a coincidence: the git sync step works and is silently syncing the wrong directory while the process stays pinned to the fallback.Why the fix runs the re-check BEFORE
_update_code_source, not after:_update_code_sourcederives its own sync target fromself.ansible_dir(two directories up). While pinned to the fallback, that target is/opt/autobot(no.git), not/opt/autobot/code_source(which has one) — so_update_code_sourcesees no.gitand reports a no-op success, without ever touching the real checkout. Re-checking after syncing would leave this half of the bug in place; a naive fix that only reorders "check, then sync, in that order" without noticing this dependency would look correct and still fail.What Changed
playbook_executor.py: extracted the resolution logic into_auto_detect_ansible_dir()(module-levelDEFAULT_CODE_SOURCE_ANSIBLE_DIR/FALLBACK_ANSIBLE_DIRconstants, so a test can monkeypatch either candidate without touching real/opt/autobot) and_refresh_ansible_dir(), called at the top ofexecute_playbook()before_update_code_source(). An explicitansible_dirpassed to the constructor (tests, wizard provisioning) is never auto-refreshed — a deliberate caller choice, not the auto-detected default this instance would otherwise track.playbook_executor_ansible_dir_self_heal_17150_test.py(4 tests): module loaded from disk like itsplaybook_executor_timeout_14524_test.pysibling (the package conftest stubsservices.*). The core test calls the REALexecute_playbook()end-to-end, not just the extracted resolver, so a future change that moves the refresh call to the wrong spot is also caught.Sibling candidates (from the filed issue) — handled, not dropped
_update_code_source's no-.gitearly-return: this fix directly addresses it — onceansible_diris re-detected before_update_code_sourceruns, that method's own directory derivation is correct too, so it stops silently no-opping against the wrong tree.AUTOBOT_GIT_BRANCHpointing at a branch other thanmain: left alone, deliberately. A legitimate, working operator override (same philosophy asSLM_ANSIBLE_DIR) for tracking a non-default branch; a misconfigured value is an operational concern, not a code defect in this issue's scope. Not fixing it here.Acceptance Criteria
PlaybookExecutorinstance constructed whilecode_source's ansible directory does not yet exist, then given a followingexecute_playbook()call after that directory has since appeared, reads the playbook from the code_source path on that later call — not the cached fallback. Proven viatest_execute_playbook_self_heals_before_syncing_and_before_reading_paths.SLM_ANSIBLE_DIRenv-var override still takes priority unconditionally, unchanged from current behavior — the resolution logic itself is untouched, only extracted and re-invoked;_auto_detect_ansible_dir()is byte-identical logic to the old__init__block.self._refresh_ansible_dir()call, re-ran, confirmedtest_execute_playbook_self_heals_before_syncing_and_before_reading_pathsfails withFileNotFoundErrornaming the STALE fallback path, restored the fix, confirmed green again..deployed_commitpast1b9f5143and the deployed files gain the contentcode_sourcealready has. That requires running an actual sync through the builtin updater on the live host, which only the owner can do and confirm; this PR's own testing proves the code-level self-heal, not the host-level outcome. Left unchecked deliberately — a guessed "yes" here would be exactly the fabrication this repo's rules forbid.Verification
playbook_executor_timeout_14524_test.py, confirming no regression). Environment is below CI's declared dependency floors, so this is not CI evidence — CI is authoritative.prompt-injection-detector-strict-modeunaffected (scoped toautobot-backend/*.pyonly; this change is entirely underautobot-slm-backend/) — confirmed 3245, unchanged frommain.hooks-path-overrideconfirmed safe at its current 6488 (population 6886 with the new test file staged, ceiling 6889) — no re-pin needed.playbook_executor.pyre-pinned 1362 → 1406 for this fix's own lines (both baselines updated).Model Used
Claude Sonnet 5 (
claude-sonnet-5)🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests