Skip to content

fix(ansible): two update-path regressions — python_interpreter_binary scope, code_source_dir default (#16750) - #16752

Merged
mrveiss merged 1 commit into
mainfrom
issue-16750-ansible-update-regressions
Sep 14, 2026
Merged

mrveiss merged 1 commit into
mainfrom
issue-16750-ansible-update-regressions

Conversation

@mrveiss

@mrveiss mrveiss commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

Thinking Path

Both regressions surfaced on a real update-all run on 2026-09-14; CI never
executes Ansible, so neither was caught there. Traced each to the exact
scoping rule it violates, then applied the fix mechanically everywhere the
same shape recurred, rather than patching only the one site that happened to
fail first.

(a) python_interpreter_binary is a role default
(roles/python_interpreter/defaults/main.yml). Ansible's rule: include_role
(dynamic) keeps an included role's defaults private to that role's own task
execution unless public: true is set — even for later tasks in the same
file. import_role (static) and a meta/main.yml dependency don't have this
problem; their defaults merge into play/role scope unconditionally. Swept
every file referencing the variable (git grep -n python_interpreter_binary)
and classified each by which of the three shapes puts python_interpreter in
scope.

(b) _shared/tasks/sync_deletions.yml templated bare code_source_dir,
which not every caller sets (the frontend role never does). Its siblings
(sync_deletions_source_dir, and _shared/tasks/record_slm_deployed_commit.yml)
already use an override-first, SSOT-derived default for the same concept —
applied the identical shape here instead of inventing a new one.

What Changed

(a) — roles/python_interpreter include sites (11 files reference
python_interpreter_binary; roles/python_interpreter/defaults/main.yml
defines it and roles/python_interpreter/tasks/main.yml is the role's own
task file, so neither needs an include):

File Mechanism reaching the role Status
roles/agent_config/tasks/main.yml include_role (dynamic) fixed: added public: true — also covers python_env.yml, python_deps.yml, openvino.yml, playwright.yml, all include_tasks'd after it in the same play
roles/agent_config/tasks/python_env.yml inherits play scope from main.yml's now-public include in scope (no change needed)
roles/agent_config/tasks/python_deps.yml inherits play scope in scope (no change needed)
roles/agent_config/tasks/openvino.yml inherits play scope in scope (no change needed)
roles/agent_config/tasks/playwright.yml inherits play scope in scope (no change needed)
roles/backend_services/tasks/main.yml include_role (dynamic) fixed: added public: true
roles/browser/tasks/main.yml include_role (dynamic) fixed: added public: true (the reported regression)
roles/npu-worker/tasks/main.yml include_role (dynamic) fixed: added public: true
roles/tts-worker/tasks/main.yml include_role (dynamic) fixed: added public: true
roles/python_interpreter/tasks/main.yml same role as the definition in scope by construction (no include needed)

No default was duplicated or hardcoded anywhere — public: true keeps
roles/python_interpreter/defaults/main.yml the single source.

(b) — roles/_shared/tasks/sync_deletions.yml: added one set_fact as
the first task inside the existing block: (not a sibling before it, so
the file keeps the single top-level block/rescue task the #16310 wiring
tests assert on):

_sd_code_source_dir: "{{ code_source_dir | default(autobot.base_dir ~ '/code_source') }}"

and switched all three sites (git -c safe.directory=/-C, the diff-plan
--repo-root, the bootstrap-plan --repo-root) from bare code_source_dir
to _sd_code_source_dir. The delegated-to-localhost tasks still template
against the iterated host's vars — set_fact here runs undelegated, so the
fact lands in that host's hostvars before any delegate_to: localhost task
reads it.

Guard tests (repo_tests/):

  • ansible_python_interpreter_binary_scope_16750_test.py — sweeps every
    Ansible file for {{ python_interpreter_binary }} and fails if the owning
    role/play has no meta dependency, import_role, or
    include_role(public: true) for python_interpreter. Includes fixtures
    reproducing the pre-fix browser shape (asserted broken) and the fixed shape
    (asserted in scope), plus meta-dependency and import_role fixtures.
  • ansible_shared_tasks_code_source_dir_default_16750_test.py — sweeps
    roles/_shared/tasks/*.yml for any {{ ... code_source_dir ... }}
    expression with no default( in the same expression. Includes fixtures
    reproducing the pre-fix sync_deletions.yml shape (asserted broken) and the
    fixed shape (asserted defaulted).
  • repo_tests/glob_declared_reads_15900_test.py updated: both new guards
    declare "*.yml" via rglob, added to the existing recorded set.

Verification

  • ansible-playbook --syntax-check on every playbook that reaches the five
    fixed roles or sync_deletions.yml: playbooks/update-all-nodes.yml,
    playbooks/deploy-full.yml, playbooks/deploy-native-services.yml,
    playbooks/provision_host.yml, playbooks/recover_host.yml (with
    -e target_host=dummy), deploy.yml — all pass.
  • python3 -m pytest repo_tests/ansible_python_interpreter_binary_scope_16750_test.py repo_tests/ansible_shared_tasks_code_source_dir_default_16750_test.py repo_tests/glob_declared_reads_15900_test.py — 41 passed.
  • Full related sweep: pytest repo_tests/ -k "ansible or sync_deletions or python_interpreter" — 223 passed, 1 pre-existing unrelated failure
    (scan_helpers_vacuity_test.py::test_the_ansible_reference_checker_refuses_when_nothing_resolved,
    reproduced identically on origin/main before this change — a local
    ModuleNotFoundError: tools.lint subprocess/PYTHONPATH issue, not caused by
    this diff).
  • repo_tests/sync_deletions_ansible_wiring_16310_test.py and
    sync_deletions_target_pinning_and_shell_safety_16310_test.py (both assert
    the file's single top-level block/rescue task, marker-write ordering,
    rescue behavior, injection safety) — all still pass unmodified.
  • Pre-push hook ran the changed-file pytest budget and passed.
  • Host evidence (the next update-all run succeeding against these roles) is
    pending — never run Ansible from this session.

Closes #16750

Single-issue rationale

#16750 fixes two regressions that today's merges put into the live update path: the browser venv task (#16721, python_interpreter_binary out of scope) and the sync-deletions step (#16351, bare code_source_dir). Both failed on a real update-all run. No other open issue shares this scope, and holding it for a batch would leave update-all broken on the owner's install.

Model Used

Sonnet 5

The browser role's pip task referenced python_interpreter_binary right
after a plain include_role of python_interpreter -- a dynamic include
keeps a role's defaults private unless public: true is set, so the
variable was undefined on a real update-all run on 2026-09-14. The same
shape existed in agent_config, backend_services, npu-worker and
tts-worker; all five now mark the include public: true rather than
duplicating the default.

_shared/tasks/sync_deletions.yml templated bare code_source_dir, which
the frontend role never sets, silently skipping its deletion pass.
Computed once as a local fallback (_sd_code_source_dir, the same
override-first default its callers already use) and used at all three
sites.

Adds guard tests for both regression shapes, with fixtures reproducing
the pre-fix browser and sync_deletions shapes to prove the detectors
would have caught them.
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 18 minutes.

Check out review usage here.

View limit details

Limit 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.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 549dc9fc-f86c-472c-a224-18d264f668e1

📥 Commits

Reviewing files that changed from the base of the PR and between 7391e59 and 5b2af52.

📒 Files selected for processing (9)
  • autobot-slm-backend/ansible/roles/_shared/tasks/sync_deletions.yml
  • autobot-slm-backend/ansible/roles/agent_config/tasks/main.yml
  • autobot-slm-backend/ansible/roles/backend_services/tasks/main.yml
  • autobot-slm-backend/ansible/roles/browser/tasks/main.yml
  • autobot-slm-backend/ansible/roles/npu-worker/tasks/main.yml
  • autobot-slm-backend/ansible/roles/tts-worker/tasks/main.yml
  • repo_tests/ansible_python_interpreter_binary_scope_16750_test.py
  • repo_tests/ansible_shared_tasks_code_source_dir_default_16750_test.py
  • repo_tests/glob_declared_reads_15900_test.py

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.

@github-actions

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.

@mrveiss
mrveiss merged commit f721fd9 into main Sep 14, 2026
72 of 73 checks passed
@mrveiss
mrveiss deleted the issue-16750-ansible-update-regressions branch September 14, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant