Skip to content
27 changes: 27 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,30 @@ Entry format:
- Details: Updated Hard Rules and BUILDER-CRITIC Step 4 to enforce critical/horizontal-only logging; removed non-critical historical entries (`ML-20260211-001`, `ML-20260211-004`, `ML-20260211-005`, `ML-20260211-006`, `ML-20260211-007`, `ML-20260211-008`) per owner request.
- Verification: `rg -n "critical-only|ballast|Criticality|ML-20260211-00[1245678]|ML-20260212-009" AGENTS.md`; `sed -n '1,260p' AGENTS.md`
- Links: `AGENTS.md`

- ID: `ML-20260224-001`
- Timestamp: `2026-02-24T00:50:16Z`
- Type: `change`
- Summary: Refactored Deeploy manager endpoints to use PostponedRequest polling instead of blocking waits.
- Criticality: Operational/runtime behavior change; long-running deploy endpoints no longer block the main plugin loop.
- Details: Added non-blocking response checks in deeploy mixin, deferred blockchain confirmations to postponed solver, and converted create/update/scale-up endpoints to return PostponedRequest while tracking pending state and timeouts.
- Verification: Not run (not requested).
- Links: `extensions/business/deeploy/deeploy_manager_api.py`, `extensions/business/deeploy/deeploy_mixin.py`

- ID: `ML-20260224-002`
- Timestamp: `2026-02-24T13:22:52Z`
- Type: `change`
- Summary: Restored blockchain update submissions for non-confirmable deeploy operations in async path.
- Criticality: Operational correctness for blockchain state updates when chainstore confirmations are disabled.
- Details: When response keys are absent, async create/update now submits node updates for non-confirmable jobs; scale-up submits confirmations using combined new/update nodes. Tests adjusted to override the mangled balance-check method without touching production behavior.
- Verification: Not run (not requested).
- Links: `extensions/business/deeploy/deeploy_manager_api.py`, `extensions/business/deeploy/test_deeploy.py`

- ID: `ML-20260224-003`
- Timestamp: `2026-02-24T14:01:46Z`
- Type: `change`
- Summary: Fixed pending deeploy timeout cleanup and scale-up confirmation node extraction.
- Criticality: Correctness in deferred deploy processing and confirmation logic.
- Details: Timeout handler now uses pending_id from state and handles missing `now`; scale-up finalization extracts nodes from status entries safely.
- Verification: Not run (not requested).
- Links: `extensions/business/deeploy/deeploy_manager_api.py`
1 change: 1 addition & 0 deletions extensions/business/container_apps/container_app_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3298,6 +3298,7 @@ def _handle_initial_launch(self):
self._start_container_log_stream()
self._maybe_execute_build_and_run()

self._last_image_check = self.time()
self.P("Container launched successfully")
self.P(self.container)
if self.current_image_hash:
Expand Down
22 changes: 15 additions & 7 deletions extensions/business/container_apps/worker_app_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"REPO_URL": None,
"BRANCH": "main",
"POLL_INTERVAL": 60,
"RATE_LIMIT_BACKOFF": 5 * 60,
},

# Disable image auto-update; Git monitoring drives restarts
Expand Down Expand Up @@ -97,6 +98,7 @@ def _after_reset(self):
self.branch = None
self.repo_url = None
self._last_git_check = 0
self._git_backoff_until = 0
self._repo_configured = False
self._repo_owner = None
self._repo_name = None
Expand Down Expand Up @@ -279,21 +281,19 @@ def _check_git_updates(self, current_time=None):
return None

self._last_git_check = current_time
previous_commit = self.current_commit
latest_commit = self._get_latest_commit()

if not latest_commit:
return None

if self.current_commit and latest_commit != self.current_commit:
if previous_commit and latest_commit != previous_commit:
self.P(
f"New commit detected ({latest_commit[:7]} != {self.current_commit[:7]}). Restart required.",
f"New commit detected ({latest_commit[:7]} != {previous_commit[:7]}). Restart required.",
color='y',
)
self.current_commit = latest_commit
return StopReason.EXTERNAL_UPDATE # Git update triggers external update restart
else:
if not self.current_commit:
self.current_commit = latest_commit
self.P(f"Commit check ({self.branch}): {latest_commit}", color='d')
return None

Expand Down Expand Up @@ -338,8 +338,9 @@ def _ensure_repo_state(self, initial=False):
self._configure_repo_url()

latest_commit = self._get_latest_commit()
self._last_git_check = self.time()

if latest_commit:
self.current_commit = latest_commit
self.P(f"Latest commit on {self.branch}: {latest_commit}", color='d')
else:
self.P("Unable to determine latest commit during initialization", color='y')
Expand Down Expand Up @@ -444,20 +445,27 @@ def _get_latest_commit(self, return_data=False):

headers = {"Authorization": f"token {token}"} if token else {}

if self.time() < self._git_backoff_until:
return (None, None) if return_data else None

try:
self.Pd(f"Commit check URL: {api_url}", score=5, color='b')
resp = requests.get(api_url, headers=headers, timeout=10)

if resp.status_code == 200:
data = resp.json()
latest_sha = data.get("commit", {}).get("sha", None)
if latest_sha:
self.current_commit = latest_sha
if return_data:
return latest_sha, data
return latest_sha
if resp.status_code == 404:
self.P(f"Repository or branch not found: {api_url}", color='r')
elif resp.status_code == 403:
self.P("GitHub API rate limit exceeded or access denied", color='r')
vcs_backoff = (getattr(self, 'cfg_vcs_data', {}) or {}).get('RATE_LIMIT_BACKOFF', 300)
self._git_backoff_until = self.time() + vcs_backoff
self.P(f"GitHub API rate limit exceeded or access denied. Backing off for {vcs_backoff}s.", color='r')
else:
self.P(f"Failed to fetch latest commit (HTTP {resp.status_code}): {resp.text}", color='r')
except requests.RequestException as exc:
Expand Down
24 changes: 22 additions & 2 deletions extensions/business/dauth/dauth_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"""
from extensions.business.mixins.node_tags_mixin import _NodeTagsMixin
from naeural_core.business.default.web_app.supervisor_fast_api_web_app import SupervisorFastApiWebApp as BasePlugin
from extensions.business.mixins.request_tracking_mixin import _RequestTrackingMixin
from extensions.business.dauth.dauth_mixin import _DauthMixin

__VER__ = '0.2.2'
Expand All @@ -35,7 +36,12 @@

'DAUTH_VERBOSE' : False,
'DAUTH_LOG_RESPONSE' : True,
'LOG_REQUESTS' : True,

'REQUESTS_CSTORE_HKEY': 'DAUTH_REQUESTS',
'REQUESTS_MAX_RECORDS': 2,
'REQUESTS_LOG_INTERVAL': 5 * 60,

'SUPRESS_LOGS_AFTER_INTERVAL' : 300,

# required ENV keys are defined in plugin template and should be added here
Expand Down Expand Up @@ -83,7 +89,8 @@
class DauthManagerPlugin(
BasePlugin,
_DauthMixin,
_NodeTagsMixin
_NodeTagsMixin,
_RequestTrackingMixin,
):
"""
This plugin is the dAuth FastAPI web app that provides an endpoints for decentralized authentication.
Expand All @@ -103,10 +110,23 @@ def on_init(self):
self.P("Started {} plugin on {} / {}\n - Auth keys: {}\n - Predefined keys: {}".format(
self.__class__.__name__, my_address, my_eth_address,
self.cfg_auth_env_keys, self.cfg_auth_predefined_keys)
)
)
self._init_request_tracking()
return


def on_request(self, request):
self._track_request(request)
return

def on_response(self, method, response):
self._track_response(method, response)
return

def process(self):
self._maybe_log_and_save_tracked_requests()
return

def __get_current_epoch(self):
"""
Get the current epoch of the node.
Expand Down
Loading
Loading