Skip to content

[16.0][IMP] cetmix_tower_server Fixes from 17.0#481

Merged
CetmixGitDrone merged 1 commit into16.0from
16.0-t5313-cetmix_tower_server-fix-backport_from_17
Mar 27, 2026
Merged

[16.0][IMP] cetmix_tower_server Fixes from 17.0#481
CetmixGitDrone merged 1 commit into16.0from
16.0-t5313-cetmix_tower_server-fix-backport_from_17

Conversation

@ivs-cetmix
Copy link
Copy Markdown
Contributor

@ivs-cetmix ivs-cetmix commented Mar 25, 2026

Backport fixes from #480

Task 5313

Summary by CodeRabbit

  • Bug Fixes

    • Improved validations, error handling, and control-flow across requests, waypoints, action triggering, plan logging, variables, and copies to reduce unexpected failures.
  • New Features

    • Enhanced YAML export/import with deferred relation resolution and new scheduled-task YAML model support.
  • Security/Permissions

    • Expanded manager read criteria for jet templates.
  • Tests

    • Added and updated tests covering jets, templates, waypoints, server action commands, and YAML import/export.
  • Changes

    • Demo scheduled tasks are disabled by default.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Disabled demo scheduled tasks and applied targeted fixes across jet/request/template/waypoint/server/variable/log/YAML handling, security rule adjustments, and extensive test additions and updates.

Changes

Cohort / File(s) Summary
Demo Configuration
cetmix_tower_server/demo/demo_data.xml
Disabled five demo scheduled-task records by changing <field name="active"> from True to False.
Jet core & requests
cetmix_tower_server/models/cx_tower_jet.py, cetmix_tower_server/models/cx_tower_jet_request.py
Fixed indentation/assignment in jet creation, corrected clone to copy scheduled tasks/server logs properly, standardized _trigger_action to return dicts and aggregate errors, enforced ensure_one() usage and tightened unlink via sudo().write() in request flows.
Jet templates & dependencies
cetmix_tower_server/models/cx_tower_jet_template.py, cetmix_tower_server/models/cx_tower_jet_template_dependency.py
Replaced level-based dependency ordering with deterministic topological sort (with level fallback on cycles); added TODOs about graph fetch/caching inefficiency.
Waypoints
cetmix_tower_server/models/cx_tower_jet_waypoint.py
Added is_destination constraint and single-destination enforcement per jet; changed prepare signature to accept is_destination, raise ValidationError on invalid states, updated fly_to, state sequencing, and failure handling to clear destinations and finalize logs accordingly.
Server, command runner & logs
cetmix_tower_server/models/cx_tower_server.py, cetmix_tower_server/models/cx_tower_server_log.py, cetmix_tower_server/models/cx_tower_plan_log.py
Added server ownership check in run_flight_plan; _command_runner_jet_action now resolves action early, aggregates per-jet trigger results and finishes logs with aggregated messages; removed current_command_log arg; added copy=False to server/file log fields; wrapped post-finish hooks in DB savepoints with exception handling and warnings.
Variables, values & files
cetmix_tower_server/models/cx_tower_variable.py, cetmix_tower_server/models/cx_tower_variable_value.py, cetmix_tower_server/models/cx_tower_file.py, cetmix_tower_server/models/cx_tower_metadata_mixin.py
Treat None (not falsy) as missing in variable selection to preserve empty strings, expanded compute/constraint triggers to include jet fields, made server_dir trimming robust for falsy inputs, and update_metadata now returns True.
Access control & security
cetmix_tower_server/models/res_users.py, cetmix_tower_server/security/cx_tower_jet_template_security.xml
Refactored access-level flow to return False for no-access; expanded jet-template manager read rule to include jet_ids.manager_ids membership in domain_force.
YAML export, import & new YAML models
cetmix_tower_yaml/models/..., cetmix_tower_yaml/wizards/cx_tower_yaml_import_wiz.py
Extended YAML field lists for commands and scheduled tasks; added deferred m2o/x2m import support and queuing, added _get_deferred_* helpers for templates/commands/waypoints/scheduled-task children, introduced cx.tower.scheduled.task.cv YAML model, refactored YAML mixin to support shared collector and deferred-resolution post-pass, and implemented import wizard post-pass resolution with aggregated errors.
Jet waypoint template field
cetmix_tower_server/models/cx_tower_jet_waypoint_template.py
Made jet_template_id non-required on waypoint templates (keeps ondelete cascade/help).
Tests
cetmix_tower_server/tests/..., cetmix_tower_yaml/tests/...
Added new test module test_server_jet_action_command; extensive test additions/adjustments for jet availability, dependency ordering, waypoint destination constraints and failure cases, YAML export/import deferral and expectations, command rendering changes, and updated fixtures/assertions to match behavior changes.
Package imports
cetmix_tower_yaml/models/__init__.py, cetmix_tower_server/tests/__init__.py
Registered new YAML model import and new test module import.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 87.31% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The PR title '[16.0][IMP] cetmix_tower_server Fixes from 17.0' is clear and specific, accurately indicating this is a backport of fixes from version 17.0 to 16.0, which matches the changeset's nature of stabilization improvements across multiple models and test files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cetmix_tower_server/models/cx_tower_variable.py (1)

515-529: ⚠️ Potential issue | 🟠 Major

Preserve empty-string server-template values here too.

The rest of _get_value() now treats only None as “missing”, but this branch still uses or. If a server-template override is "", it incorrectly falls back to the global value.

🔧 Suggested fix
         if server_template:
-            return server_template_value_char or global_value_char
+            return (
+                server_template_value_char
+                if server_template_value_char is not None
+                else global_value_char
+            )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cetmix_tower_server/models/cx_tower_variable.py` around lines 515 - 529, In
_get_value(), the server-template branch currently uses a truthy "or" which
treats an empty string as missing; change the logic to treat only None as
missing by explicitly checking server_template_value_char for None (e.g., if
server_template_value_char is not None return it, else return global_value_char)
so that empty-string overrides are preserved; keep the same pattern used in the
later "Jet" branch and update the return for the server_template branch
accordingly.
cetmix_tower_server/models/cx_tower_server.py (1)

2369-2385: ⚠️ Potential issue | 🟠 Major

no_fallback is a no-op in this override.

The signature now advertises a server-local lookup mode, but this implementation always goes through _get_variable_values_by_references(), so callers still receive fallback values even when they explicitly pass no_fallback=True.

🔧 Suggested fix
 def get_variable_value(self, variable_reference, no_fallback=False):
@@
         """
         self.ensure_one()
+        if no_fallback:
+            return super().get_variable_value(
+                variable_reference, no_fallback=no_fallback
+            )
         variable = self.env["cx.tower.variable"].get_by_reference(variable_reference)
         if not variable:
             return None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cetmix_tower_server/models/cx_tower_server.py` around lines 2369 - 2385,
get_variable_value currently ignores the no_fallback flag by always calling
variable._get_variable_values_by_references which returns fallbacks; change
get_variable_value (and its call to variable._get_variable_values_by_references)
to respect no_fallback by propagating the flag (e.g. call
variable._get_variable_values_by_references(variable_references=[variable_reference],
server=self, no_fallback=no_fallback)), and if that helper does not accept a
no_fallback parameter, add a branch in get_variable_value that when no_fallback
is True performs a server-only lookup (read the server-specific value directly
from the variable record or via a server-only helper) and returns None if no
server-specific value exists, otherwise return the found value; reference
functions: get_variable_value and _get_variable_values_by_references.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cetmix_tower_server/models/cx_tower_jet_request.py`:
- Around line 258-260: The finalizer currently does a TOCTOU by checking
self.jet_id and then calling self.jet_id.sudo().write({"served_jet_request_id":
False}) which can clear a newer attach; change it to perform an atomic
conditional update on the jet row (update WHERE id = <jet_id.id> AND
served_jet_request_id = <self.id>) or acquire a row lock on the Jet record
before clearing so you only clear when served_jet_request_id still equals this
request; update the code paths that reference jet_id and served_jet_request_id
to use the conditional UPDATE or locking variant instead of the two-step
check-then-write.

In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py`:
- Around line 396-412: The target waypoint is being marked only as
is_destination=True before the previous waypoint begins an async leave, so when
the leave plan completes _plan_finished() looks for a waypoint in "arriving" and
never resumes the target; change the initial write so it sets both
is_destination=True and state="arriving" (i.e. self.write({"is_destination":
True, "state": "arriving"})) so _plan_finished() can find it and call _arrive();
apply the same adjustment to the analogous code around the other occurrence
referenced (lines ~567-568) and keep the existing rollback that sets
is_destination=False on immediate leave failure.

In `@cetmix_tower_server/models/cx_tower_jet.py`:
- Around line 903-908: The loop in cx_tower_jet.py currently skips "command"
logs but should explicitly guard for file logs for consistency and defensive
coding: replace the condition using jet_log.log_type == "command" with an
explicit check that jet_log.log_type == "file" and only call
jet_log.file_template_id.create_file(server=jet.server_id, jet=jet,
if_file_exists="skip") when that check passes; this aligns behavior with the
same explicit check used where file creation is handled elsewhere and references
the same symbols jet.server_log_ids, jet_log.log_type, and
file_template_id.create_file.

In `@cetmix_tower_server/models/cx_tower_plan_log.py`:
- Around line 352-388: The post-hook savepoint blocks swallow failures leaving
the plan marked finished while hook state changes are rolled back; update each
except handler (for waypoint_id._plan_finished,
jet_template_install_id._flight_plan_finished, jet_id._flight_plan_finished) to
persist the hook failure on the plan record (e.g. write an error field like
hook_failure_message/hook_failure_at and set plan_status='failed' or a failure
flag) and then re-raise the exception (or otherwise surface the error) so the
failure is recorded and not silently ignored.

In `@cetmix_tower_server/models/cx_tower_server.py`:
- Around line 1746-1797: The code currently converts any dependent-jet failure
into GENERAL_ERROR before calling log_record.finish, losing actionable child
statuses returned by jet._trigger_action (see dependent_jets loop and
_trigger_action) such as JET_DEPENDENCIES_NOT_SATISFIED; change the logic so
that when exactly one dependent jet produced a non-zero status (or an explicit
status is present in its result) you preserve that child's status (use that
status value for status) and keep its error message in error/response, while
only using GENERAL_ERROR when multiple dependent_jets failed or no specific
child status is available; update the branch that builds error_parts, status,
error and response (and the call to log_record.finish) to implement this
conditional preservation.

In `@cetmix_tower_server/tests/test_command.py`:
- Line 676: The test currently asserts a literal implementation artefact by
setting rendered_code_expected = "cd False && mkdir test-odoo-1"; update the
assertion to check for the intended fallback behavior instead of the exact "cd
False" string — for example, assert that the generated command uses a
safe/default directory token (like the current directory or a fallback path) or
that it omits a failing cd and still contains "mkdir test-odoo-1". Locate the
variable rendered_code_expected (in tests/test_command.py) and replace the
literal check with a behavior-level expectation (e.g., contains "mkdir
test-odoo-1" and either contains a valid path token or does not contain "cd
False") so the test verifies semantics rather than the implementation artifact.

In `@cetmix_tower_server/tests/test_jet_template.py`:
- Around line 1893-1914: The test currently derives expected from
_topological_sort_dependency_graph which can mask regressions; update
test_get_all_dependencies_consistency_with_build_graph to compute expected
without calling _topological_sort_dependency_graph by either hard-coding the
known reverse-topological list of template ids/templates for the Odoo fixture
(based on jet_template_odoo.id and the known graph) or by asserting explicit
relative-order constraints: call _build_dependency_graph() and for each edge (u
-> v) assert that dependencies.index(graph[v]["template"]) <
dependencies.index(graph[u]["template"]); keep using _get_all_dependencies() and
validate against the concrete/hard assertions instead of using
_topological_sort_dependency_graph().

In `@cetmix_tower_server/tests/test_jet.py`:
- Around line 24-28: The test name/docstring claims the explicit request is
marked "processing" before dispatch but the assertions only verify that
_serve_jet_request() was called; update the test to either (A) rename the
test/method and docstring to reflect that it only asserts _serve_jet_request()
invocation, or (B) add an explicit assertion that the explicit request object's
status (e.g., request.status or explicit_request.status) equals "processing" at
the point just before dispatch/transition handling and/or that the transition
dispatch code observes that state; locate the test method
test_on_is_available_explicit_request_marked_processing_before_dispatch and the
helper _serve_jet_request to implement the chosen fix consistently (also apply
the same alignment to the similar test around lines 54-65).

---

Outside diff comments:
In `@cetmix_tower_server/models/cx_tower_server.py`:
- Around line 2369-2385: get_variable_value currently ignores the no_fallback
flag by always calling variable._get_variable_values_by_references which returns
fallbacks; change get_variable_value (and its call to
variable._get_variable_values_by_references) to respect no_fallback by
propagating the flag (e.g. call
variable._get_variable_values_by_references(variable_references=[variable_reference],
server=self, no_fallback=no_fallback)), and if that helper does not accept a
no_fallback parameter, add a branch in get_variable_value that when no_fallback
is True performs a server-only lookup (read the server-specific value directly
from the variable record or via a server-only helper) and returns None if no
server-specific value exists, otherwise return the found value; reference
functions: get_variable_value and _get_variable_values_by_references.

In `@cetmix_tower_server/models/cx_tower_variable.py`:
- Around line 515-529: In _get_value(), the server-template branch currently
uses a truthy "or" which treats an empty string as missing; change the logic to
treat only None as missing by explicitly checking server_template_value_char for
None (e.g., if server_template_value_char is not None return it, else return
global_value_char) so that empty-string overrides are preserved; keep the same
pattern used in the later "Jet" branch and update the return for the
server_template branch accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bdfdd676-ffd5-4569-b52c-e87800c15a5f

📥 Commits

Reviewing files that changed from the base of the PR and between 0f016d0 and 3c9fbdd.

📒 Files selected for processing (27)
  • cetmix_tower_server/demo/demo_data.xml
  • cetmix_tower_server/models/cx_tower_file.py
  • cetmix_tower_server/models/cx_tower_jet.py
  • cetmix_tower_server/models/cx_tower_jet_request.py
  • cetmix_tower_server/models/cx_tower_jet_template.py
  • cetmix_tower_server/models/cx_tower_jet_template_dependency.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint.py
  • cetmix_tower_server/models/cx_tower_metadata_mixin.py
  • cetmix_tower_server/models/cx_tower_plan_log.py
  • cetmix_tower_server/models/cx_tower_server.py
  • cetmix_tower_server/models/cx_tower_server_log.py
  • cetmix_tower_server/models/cx_tower_variable.py
  • cetmix_tower_server/models/cx_tower_variable_value.py
  • cetmix_tower_server/models/res_users.py
  • cetmix_tower_server/security/cx_tower_jet_template_security.xml
  • cetmix_tower_server/tests/__init__.py
  • cetmix_tower_server/tests/test_command.py
  • cetmix_tower_server/tests/test_jet.py
  • cetmix_tower_server/tests/test_jet_template.py
  • cetmix_tower_server/tests/test_jet_template_install.py
  • cetmix_tower_server/tests/test_jet_waypoint.py
  • cetmix_tower_server/tests/test_server_jet_action_command.py
  • cetmix_tower_yaml/models/cx_tower_command.py
  • cetmix_tower_yaml/models/cx_tower_scheduled_task.py
  • cetmix_tower_yaml/models/cx_tower_scheduled_task_cv.py
  • cetmix_tower_yaml/tests/test_command.py
  • cetmix_tower_yaml/tests/test_yaml_export_wizard.py

Comment thread cetmix_tower_server/models/cx_tower_jet_request.py
Comment thread cetmix_tower_server/models/cx_tower_jet_waypoint.py
Comment thread cetmix_tower_server/models/cx_tower_jet.py Outdated
Comment thread cetmix_tower_server/models/cx_tower_plan_log.py
Comment thread cetmix_tower_server/models/cx_tower_server.py
Comment thread cetmix_tower_server/tests/test_command.py
Comment thread cetmix_tower_server/tests/test_jet_template.py Outdated
Comment thread cetmix_tower_server/tests/test_jet.py
@ivs-cetmix ivs-cetmix changed the title [16.0] cetmix_tower_server/yaml Fixes from 17.0 [16.0][IMP] cetmix_tower_server/yaml Fixes from 17.0 Mar 25, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py`:
- Around line 650-661: The failure branch filters destination candidates by
"(w.is_destination or w.state == 'arriving')" which is asymmetric with the
success path that uses filtered("is_destination"); change the filter in the
failure branch to only select waypoints with is_destination and not the current
id (use the same predicate as the success path on
self.jet_id.waypoint_ids.filtered), then keep the subsequent
write({"is_destination": False}) and call to
_finalize_create_waypoint_command_log(success=False, error=...) unchanged so
destination handling is consistent.

In `@cetmix_tower_server/models/cx_tower_jet.py`:
- Around line 977-981: The _trigger_action() function currently returns None
when the early-exit condition self.state_id == target_state and from_transition
is true (and similar branches), violating the new {status, error} contract;
change that bare return to return a dict like {"status": "skipped", "error":
None} (or {"status":"ok","error":None} per your semantics) and update the other
early-return branches referenced (the same-state/restart branches within
_trigger_action around the from_transition checks) to also return the structured
result so all paths consistently return the {status, error} dict.
- Around line 894-910: The cloned server logs are being copied with
server_id=False so the new jet's logs lose their server binding; update the copy
step for self.server_log_ids -> jet.server_log_ids (the log.copy(...) calls) to
set server_id to the new jet's server (use jet.server_id) instead of False, and
ensure that after copying you still assign file-type logs a new file via
file_template_id.create_file(server=jet.server_id, jet=jet,
if_file_exists="skip") and that command-type logs also retain
server_id=jet.server_id so server-scoped lookups/access rules remain correct.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 08740b9d-63a6-44c3-875b-2d1c9c1b5271

📥 Commits

Reviewing files that changed from the base of the PR and between 3c9fbdd and bd58ff6.

📒 Files selected for processing (5)
  • cetmix_tower_server/models/cx_tower_jet.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint.py
  • cetmix_tower_server/tests/test_jet.py
  • cetmix_tower_server/tests/test_jet_template.py
  • cetmix_tower_server/tests/test_jet_waypoint.py

Comment thread cetmix_tower_server/models/cx_tower_jet_waypoint.py
Comment thread cetmix_tower_server/models/cx_tower_jet.py
Comment thread cetmix_tower_server/models/cx_tower_jet.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py`:
- Around line 177-206: The current check using
existing_destinations/existing_by_jet misses duplicates inside the same
destination_waypoints batch; add an intra-batch duplicate check before the
external lookup by iterating destination_waypoints and building a map (e.g.,
batch_by_jet) from waypoint.jet_id.id to the first waypoint, skipping any in
"leaving"/"deleting" state, and if a second waypoint for the same jet is found
raise the same ValidationError (use waypoint.name and jet=waypoint.jet_id.name)
so that duplicates within the create/write batch are rejected before consulting
existing_destinations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 24295c0c-bdc7-43b7-82c1-8e727957e76b

📥 Commits

Reviewing files that changed from the base of the PR and between bd58ff6 and 8236a3b.

📒 Files selected for processing (2)
  • cetmix_tower_server/models/cx_tower_jet.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint.py

Comment thread cetmix_tower_server/models/cx_tower_jet_waypoint.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cetmix_tower_server/tests/test_jet_waypoint.py (1)

923-930: ⚠️ Potential issue | 🟡 Minor

Make this test start from the real pre-arrival state.

Line 929 still seeds the destination as arriving, so this test would pass even if _plan_finished() regressed to the old state-based lookup. Use ready + is_destination=True here to exercise the new handoff.

🧪 Suggested setup
-        # Create destination waypoint in arriving state
+        # Create destination waypoint in ready state; _plan_finished() should
+        # move it to arriving/current via `is_destination`.
         destination_waypoint = self.JetWaypoint.create(
             {
                 "name": "Destination Waypoint",
                 "jet_id": self.jet_test.id,
                 "waypoint_template_id": self.waypoint_template.id,
                 "is_destination": True,
-                "state": "arriving",
+                "state": "ready",
             }
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cetmix_tower_server/tests/test_jet_waypoint.py` around lines 923 - 930, The
test is seeding the destination waypoint with state "arriving" which masks
regressions in _plan_finished(); update the setup that calls JetWaypoint.create
to use state "ready" and keep is_destination=True so the test truly starts from
the pre-arrival state and exercises the new handoff logic in _plan_finished();
locate the JetWaypoint.create call in the test and change the "state" value to
"ready" while leaving "is_destination": True.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py`:
- Around line 452-455: Setting is_destination on the target before the source
finishes leaving allows unlink() to delete a ready waypoint and leave no
destination; instead, either delay marking the target with is_destination until
the source's leave completes (i.e., move the self.write({"is_destination":
True}) into the successful completion callback/transaction of the leave flow),
or harden unlink()/unlink logic to refuse removal/transition of waypoints where
is_destination is True (add an early check in unlink() to raise/abort if
record.is_destination and state == "ready"). Update the code paths that call
write({"is_destination": True}) and the unlink() implementation accordingly
(referencing is_destination, unlink(), and the write(...) call) so the
destination cannot be removed while a leave is in progress.
- Around line 456-470: The code can call self._arrive() twice because
previous_waypoint._leave() may synchronously trigger _plan_finished() which
already advanced the destination; to fix, only set state to "arriving" and call
self._arrive() if the current record wasn't already in "arriving" (i.e., check
self.state before writing) and ensure you call self._arrive() only when you
actually changed the state to "arriving" (e.g., check previous_state !=
"arriving" or verify the write changed state); update the block around
previous_waypoint._leave(), previous_waypoint.state and the subsequent
self.write(...) / self._arrive() calls to include that guard so _arrive() is not
invoked redundantly.

---

Outside diff comments:
In `@cetmix_tower_server/tests/test_jet_waypoint.py`:
- Around line 923-930: The test is seeding the destination waypoint with state
"arriving" which masks regressions in _plan_finished(); update the setup that
calls JetWaypoint.create to use state "ready" and keep is_destination=True so
the test truly starts from the pre-arrival state and exercises the new handoff
logic in _plan_finished(); locate the JetWaypoint.create call in the test and
change the "state" value to "ready" while leaving "is_destination": True.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 987a474b-a09d-48e0-bd51-9c4803a99f87

📥 Commits

Reviewing files that changed from the base of the PR and between 8236a3b and c4d352a.

📒 Files selected for processing (2)
  • cetmix_tower_server/models/cx_tower_jet_waypoint.py
  • cetmix_tower_server/tests/test_jet_waypoint.py

Comment thread cetmix_tower_server/models/cx_tower_jet_waypoint.py
Comment thread cetmix_tower_server/models/cx_tower_jet_waypoint.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cetmix_tower_server/tests/test_jet_waypoint.py (1)

915-922: ⚠️ Potential issue | 🟡 Minor

Use ready here so the test exercises the new promotion logic.

The real flow keeps the destination waypoint ready with is_destination=True until the leave succeeds. Starting this fixture in arriving means the test still passes even if the code stops promoting the destination before _arrive(), so it misses the regression this change is meant to cover.

Based on learnings, in cetmix_tower_server/models/cx_tower_jet_waypoint.py the leaving success path uses filtered("is_destination") because fly_to() keeps the target waypoint ready until leave completion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cetmix_tower_server/tests/test_jet_waypoint.py` around lines 915 - 922, The
test creates a destination waypoint in "arriving" state which bypasses the
promotion logic; change the JetWaypoint.create call for the destination waypoint
in this test to set "state": "ready" (keeping "is_destination": True and other
fields) so the test exercises the promotion behavior tied to _arrive()/fly_to()
and matches the leaving-success path in cx_tower_jet_waypoint.py that filters on
"is_destination".
♻️ Duplicate comments (1)
cetmix_tower_server/models/cx_tower_jet_waypoint.py (1)

468-486: ⚠️ Potential issue | 🟠 Major

Active destinations can still be deleted mid-flight.

After Line 470 the target waypoint can sit in ready with is_destination=True until the source leave plan completes. unlink() still accepts ready waypoints, so a delete/delete-plan in that window can either remove the destination entirely or flip it to deleting while _plan_finished() still expects filtered("is_destination") to resume it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py` around lines 468 - 486,
Currently the code sets is_destination on the new waypoint before the previous
waypoint's leave completes, allowing unlink()/delete to act on a ready waypoint
mid-transition; move the is_destination write so it is only set after the
previous waypoint's leave has succeeded (i.e., after previous_waypoint._leave()
returns without error and/or when you determine leaving completed immediately),
and only then set self.write({"is_destination": True}) immediately before
calling self.write({"state":"arriving"}) / self._arrive(); adjust the rollback
path (where previous_waypoint.state == "error") to ensure is_destination is not
set and keep _plan_finished()'s filtered("is_destination") invariant intact (use
the existing methods _leave, _arrive, unlink, _plan_finished, state,
is_destination to locate and update the logic).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@cetmix_tower_server/tests/test_jet_waypoint.py`:
- Around line 915-922: The test creates a destination waypoint in "arriving"
state which bypasses the promotion logic; change the JetWaypoint.create call for
the destination waypoint in this test to set "state": "ready" (keeping
"is_destination": True and other fields) so the test exercises the promotion
behavior tied to _arrive()/fly_to() and matches the leaving-success path in
cx_tower_jet_waypoint.py that filters on "is_destination".

---

Duplicate comments:
In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py`:
- Around line 468-486: Currently the code sets is_destination on the new
waypoint before the previous waypoint's leave completes, allowing
unlink()/delete to act on a ready waypoint mid-transition; move the
is_destination write so it is only set after the previous waypoint's leave has
succeeded (i.e., after previous_waypoint._leave() returns without error and/or
when you determine leaving completed immediately), and only then set
self.write({"is_destination": True}) immediately before calling
self.write({"state":"arriving"}) / self._arrive(); adjust the rollback path
(where previous_waypoint.state == "error") to ensure is_destination is not set
and keep _plan_finished()'s filtered("is_destination") invariant intact (use the
existing methods _leave, _arrive, unlink, _plan_finished, state, is_destination
to locate and update the logic).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bc688f7c-28c9-4bcd-aa27-fa8c13311281

📥 Commits

Reviewing files that changed from the base of the PR and between c4d352a and 7be8795.

📒 Files selected for processing (3)
  • cetmix_tower_server/models/cx_tower_jet.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint.py
  • cetmix_tower_server/tests/test_jet_waypoint.py

@ivs-cetmix ivs-cetmix force-pushed the 16.0-t5313-cetmix_tower_server-fix-backport_from_17 branch 2 times, most recently from 6b62223 to 3be4382 Compare March 25, 2026 22:01
Comment thread cetmix_tower_server/models/cx_tower_jet_waypoint.py
Copy link
Copy Markdown
Collaborator

@tendil tendil left a comment

Choose a reason for hiding this comment

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

Code review: LGTM

@ivs-cetmix ivs-cetmix force-pushed the 16.0-t5313-cetmix_tower_server-fix-backport_from_17 branch 5 times, most recently from 6ad2e22 to 043850b Compare March 27, 2026 14:19
@ivs-cetmix
Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cetmix_tower_server/tests/test_jet_waypoint.py (1)

552-563: ⚠️ Potential issue | 🟡 Minor

Update the stale setup comment to match the new call path.

The comment says the waypoint is created with is_destination=True, but now that flag is passed in prepare(is_destination=True).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cetmix_tower_server/tests/test_jet_waypoint.py` around lines 552 - 563,
Update the stale test comment: change the comment that currently states the
waypoint is created with is_destination=True to reflect the new call path where
is_destination is passed to prepare(is_destination=True); locate the
JetWaypoint.create(...) call and the subsequent
waypoint.prepare(is_destination=True) invocation and reword the comment to
indicate the waypoint is created in draft state and the destination flag is
supplied when calling prepare.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py`:
- Around line 162-205: The constraint _check_is_destination currently watches
only "is_destination" and "jet_id" so changes to waypoint.state won't re-run it;
update the `@api.constrains` decorator on _check_is_destination to include "state"
(i.e., `@api.constrains`("is_destination", "jet_id", "state")) so the validation
runs when state is written, ensuring invalid-state checks and
duplicate-destination detection still occur on state transitions.

In `@cetmix_tower_server/models/cx_tower_jet.py`:
- Around line 891-911: Before replacing or unlinking server log records, remove
their associated file records to avoid orphaned files: in the clone flow (from
clone() calling jet_template.create_jet() and the block that assigns
jet.server_log_ids) first gather the existing jet.server_log_ids' file_id fields
and unlink those files (checking they exist) before reassigning or calling
unlink(); additionally add an unlink() override on the cx.tower.server.log model
to cascade-delete its file_id (or set ondelete='cascade' on the file_id
relation) so any future deletes also remove attached files. Ensure you reference
cx.tower.server.log.unlink, the file_id field, and the
clone()/jet_template.create_jet() assignment when making the changes.

In `@cetmix_tower_server/tests/test_jet_waypoint.py`:
- Around line 1971-1981: The test
test_unlink_destination_waypoint_no_raise_context_logs currently claims to
verify that an error is logged but only checks that the record still exists;
either add an explicit logging assertion or update the name/docstring to remove
the logging claim. To add logging verification, wrap the unlink call in
self.assertLogs(...) targeting the module logger used by the unlink logic (use
the same logger name used in the code path, e.g., the logger in the destination
waypoint module) and assert the expected error message appears in the captured
output; alternatively, rename the test and adjust the docstring to something
like "no raise + record skipped" to reflect the actual assertion if you prefer
not to assert logs. Ensure you reference the existing test function
test_unlink_destination_waypoint_no_raise_context_logs and the
waypoint.with_context(...).unlink() call when making the change.
- Around line 1311-1366: The test
test_fly_to_leave_failure_does_not_keep_destination_arriving should also assert
that destination_waypoint does not become "current" after a failed leave; after
the existing checks (after result and current_waypoint.state assertions) add an
assertion that destination_waypoint.state != "current" (and optionally re-check
destination_waypoint.is_destination is False) to ensure the destination is
neither left arriving nor promoted to current when the source leave fails.

In `@cetmix_tower_yaml/models/cx_tower_scheduled_task_cv.py`:
- Around line 19-35: In _post_process_yaml_dict_values, replace the plain truthy
check on the record returned by
env["cx.tower.variable.value"].browse(variable_value_id) with a defensive
existence check using .exists() (e.g., assign variable_value =
self.env["cx.tower.variable.value"].browse(variable_value_id).exists() and only
proceed to read name, variable_id, option_id, value_char if variable_value is
truthy); this ensures you don't rely on browse() returning a valid DB record and
prevents reading fields from a non-existent record.

---

Outside diff comments:
In `@cetmix_tower_server/tests/test_jet_waypoint.py`:
- Around line 552-563: Update the stale test comment: change the comment that
currently states the waypoint is created with is_destination=True to reflect the
new call path where is_destination is passed to prepare(is_destination=True);
locate the JetWaypoint.create(...) call and the subsequent
waypoint.prepare(is_destination=True) invocation and reword the comment to
indicate the waypoint is created in draft state and the destination flag is
supplied when calling prepare.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ab1f57b8-6f19-4bc8-8ddb-e149f63f17e9

📥 Commits

Reviewing files that changed from the base of the PR and between c4d352a and 043850b.

📒 Files selected for processing (16)
  • cetmix_tower_server/models/cx_tower_jet.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint_template.py
  • cetmix_tower_server/tests/test_jet.py
  • cetmix_tower_server/tests/test_jet_template.py
  • cetmix_tower_server/tests/test_jet_waypoint.py
  • cetmix_tower_yaml/models/__init__.py
  • cetmix_tower_yaml/models/cx_tower_command.py
  • cetmix_tower_yaml/models/cx_tower_jet_template.py
  • cetmix_tower_yaml/models/cx_tower_jet_waypoint_template.py
  • cetmix_tower_yaml/models/cx_tower_scheduled_task.py
  • cetmix_tower_yaml/models/cx_tower_scheduled_task_cv.py
  • cetmix_tower_yaml/models/cx_tower_yaml_mixin.py
  • cetmix_tower_yaml/tests/test_tower_yaml_mixin.py
  • cetmix_tower_yaml/tests/test_yaml_import_wizard.py
  • cetmix_tower_yaml/wizards/cx_tower_yaml_import_wiz.py
💤 Files with no reviewable changes (1)
  • cetmix_tower_server/models/cx_tower_jet_waypoint_template.py

Comment thread cetmix_tower_server/models/cx_tower_jet_waypoint.py
Comment thread cetmix_tower_server/models/cx_tower_jet.py
Comment thread cetmix_tower_server/tests/test_jet_waypoint.py
Comment thread cetmix_tower_server/tests/test_jet_waypoint.py
Comment thread cetmix_tower_yaml/models/cx_tower_scheduled_task_cv.py Outdated
@ivs-cetmix
Copy link
Copy Markdown
Contributor Author

@CodeRabbit review commit 043850b

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

@ivs-cetmix I'll trigger a review of commit 043850b6f273a641a07204debef235dd5182dbc1 for you.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ivs-cetmix ivs-cetmix force-pushed the 16.0-t5313-cetmix_tower_server-fix-backport_from_17 branch from 043850b to 761e63e Compare March 27, 2026 17:23
Demo and small fixes:

- Demo scheduled tasks are set to `active=False` so they do not run
  unexpectedly on install.
- `cx_tower_file`: `server_dir` normalisation handles `None` safely
  (`(values.get("server_dir") or "").strip()`).
- `cx_tower_jet`: Help text typo (“set by by” → “set by”).

Jets:

- Jet creation from template: Scheduled tasks assignment is
  dedented so it runs for every created jet (not only inside a conditional branch).
- `clone_jet`: After `create_jet`, scheduled tasks are copied
  from the source jet; server logs are reassigned (copy with `jet_id`
  /`server_id` cleared, recreate files for non-command logs, or unlink
  new-jet logs if the source had none). Avoids passing these through
  `kwargs` in the old way.
- `_trigger_action`: Return on “not available” is aligned with
  the surrounding control flow so the error path is actually returned.
- `_on_is_available`: Explicit and implicit transitions go through
  `_serve_jet_request` (with `return` after dispatch) so requests
  are tracked and finalized correctly.
  Implicit template requests are filtered with `("jet_id", "=", False)`
  so they do not steal jets that already have a target.
- `_command_runner_jet_action`: Aggregates errors from dependent jets
  into a single message, and returns a main message if no errors occurred.
- `_trigger_action` returns a dictionary with the following keys:
  - `status`: The status of the action
  - `error`: The error message if the action is not available

Jet requests:

- `ensure_one()` when resolving `jet` / `jet_template` from arguments.
- `_finalize_jet_request`: Clears `served_jet_request_id` only
  when it still points to this request,
  using `sudo().write` to avoid access edge cases.

Jet template dependencies:

- `_get_all_dependencies`: Replaced level-only ordering with
  Kahn topological sort (heap + id tie-break), reverse of topo
  order for install line ordering; level-based fallback if the graph
has a cycle, with a warning.
- `cx_tower_jet_template_dependency`: TODO note that building the
  full graph via `search([])` is inefficient.

Waypoints (`fly_to` / plan completion):

- `fly_to`: Destination is marked with `is_destination` first;
  `arriving` is set only after leave succeeds (sync path) or in
  `_plan_finished` for async leave; rollback and finalize create
  log as failed if leave fails immediately.
- `_plan_finished`: On successful leave, destination can be found
  via `is_destination` or `arriving`; on failed leave plan,
  pending destination is cleared and its create log is finalized as failed.
- Conflict log: Includes the other waypoint’s name in the translation kwargs.

Plan log robustness:

- `cx_tower_plan_log`: Waypoint, template install, and jet post-finish hooks
  run inside `savepoint()` with try/except and warning logs
  so a failing callback does not abort the whole transaction.

Server / variables / users / security:

- `cx_tower_server`: Validates that a given jet belongs to the server
  before proceeding; jet action path resolves `jet_action_id`
  once and fails cleanly if missing; dependent jets use that cached action;
  `get_variable_value` gains optional `no_fallback`
  (signature only in this diff snippet—implementation may extend below).
- `cx_tower_server_log`: `jet_id` and `file_id` get
  `copy=False` so clones do not duplicate bad links.
- `cx_tower_variable`: Value resolution uses `is None` instead
  of truthiness so empty string is preserved through the fallback chain.
- `cx_tower_variable_value`: `_compute_is_global` depends on jet
  fields; uniqueness constraint includes `variable_id`
- `res_users`: Tower access helper uses plain `if` / final
  return False` and documents False as no access.
- `cx_tower_jet_template_security.xml`: Managers of related jets
  (`jet_ids.manager_ids`) can see templates under the same OR rules as before.

Metadata mixin:

- `update_metadata` (or equivalent) returns `True` after write
  (callers expecting a boolean).

Task 5309
@ivs-cetmix ivs-cetmix force-pushed the 16.0-t5313-cetmix_tower_server-fix-backport_from_17 branch from 761e63e to 5b377dd Compare March 27, 2026 17:30
@ivs-cetmix ivs-cetmix changed the title [16.0][IMP] cetmix_tower_server/yaml Fixes from 17.0 [16.0][IMP] cetmix_tower_server Fixes from 17.0 Mar 27, 2026
@ivs-cetmix
Copy link
Copy Markdown
Contributor Author

/ocabot merge patch

@CetmixGitDrone
Copy link
Copy Markdown

What a great day to merge this nice PR. Let's do it!
Prepared branch 16.0-ocabot-merge-pr-481-by-ivs-cetmix-bump-patch, awaiting test results.

@CetmixGitDrone CetmixGitDrone merged commit 7f9f974 into 16.0 Mar 27, 2026
10 checks passed
@CetmixGitDrone
Copy link
Copy Markdown

Congratulations, your PR was merged at 4c12e40. Thanks a lot for contributing to cetmix. ❤️

@CetmixGitDrone CetmixGitDrone deleted the 16.0-t5313-cetmix_tower_server-fix-backport_from_17 branch March 27, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants