Merge main into the api_v3 split, carrying the three fixes it would have dropped

main modified web_interface/blueprints/api_v3.py while this branch deletes it,
so git reports a modify/delete conflict. Resolving that by accepting the
deletion - the obvious move - would silently discard every api_v3.py change
merged since this branch forked. Three were:

  #560  the sudo diagnosis on /system/action  -> api_v3/system.py
  #557  the stale-reconciliation-findings filter -> api_v3/plugins.py
  #550  the deletion of dead _validate_time_range -> api_v3/__init__.py

All three were verified absent from the split beforehand: _sudo_hint_for,
_SUDO_NEEDS_PASSWORD and _drop_stale_reconciliation_findings appeared in zero
files, while _validate_time_range was still sitting at __init__.py:343 after
#550 removed it from main. Each is now ported to the module that owns the
affected route, and the dead function is gone.

Two further changes this surfaced:

- api_v3/__init__.py imported fcntl at module scope. fcntl is POSIX-only, the
  monolithic blueprint never imported it, and on this branch it made the whole
  api_v3 package unimportable off POSIX: the pristine branch cannot run its
  test suite on Windows at all - 14 collection errors interrupt the run. The
  import now happens inside _starlark_manifest_lock(), the only thing that uses
  it. Deliberately NOT degraded to a no-op lock off POSIX; that function's
  docstring describes a real lost-update race, and silently not locking would
  be worse than failing loudly on a platform that cannot drive the display.

- test/test_reconciliation_status_endpoint.py is new. #557's endpoint wiring had
  no test - only the pure still_unresolved() helper in src/ was covered, and
  that lives outside web_interface so it survived the split untouched. Losing
  the filter during this merge would therefore have been completely silent.
  Removing the filter call now fails three of its seven cases.

Suite on this merge: 115 failed / 4317 passed. main today: 105 / 4252. All 11
extra failures are in test_starlark_pixlet_routes.py and need POSIX file locks,
so they are Windows-only; on that same file the pristine branch shows 19 failed
plus 39 errors, so this is a large net improvement rather than a regression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-11 09:07:35 -04:00
co-authored by Claude Opus 5
33 changed files with 1856 additions and 234 deletions
+9 -14
View File
@@ -17,7 +17,6 @@ everything they need has to exist first.
"""
from flask import Blueprint, request, jsonify, Response
import contextlib
import fcntl
import json
import os
import re
@@ -340,19 +339,6 @@ def _validate_time_format(time_str):
return True, None
except (ValueError, TypeError):
return False, f"Invalid time format: {time_str}. Expected HH:MM format."
def _validate_time_range(start_time_str, end_time_str, allow_overnight=True):
"""Validate time range. Returns (is_valid, error_message)"""
try:
start_time = datetime.strptime(start_time_str, '%H:%M').time()
end_time = datetime.strptime(end_time_str, '%H:%M').time()
# Allow overnight schedules (start > end) or same-day schedules
if not allow_overnight and start_time >= end_time:
return False, f"Start time ({start_time_str}) must be before end time ({end_time_str}) for same-day schedules"
return True, None
except (ValueError, TypeError) as e:
return False, f"Invalid time format: {str(e)}"
def _git_current_branch(project_dir):
"""Current branch name, or '' when detached or git fails."""
try:
@@ -1380,6 +1366,15 @@ def _starlark_manifest_lock():
Callers should do their read, mutation and _write_starlark_manifest() call
entirely inside the `with` block, mirroring the plugin's lock scope.
"""
# Imported here, not at module scope: fcntl is POSIX-only, and a top-level
# import made the whole api_v3 package unimportable on Windows -- which the
# monolithic blueprint never was, so the split would have broken local dev
# and the test suite there. Deliberately NOT degraded to a no-op lock off
# POSIX: the docstring above describes a real lost-update race, and silently
# not locking would be worse than failing loudly on a platform that cannot
# run the display anyway.
import fcntl
_STARLARK_APPS_DIR.mkdir(parents=True, exist_ok=True)
lock_fd = os.open(str(_STARLARK_MANIFEST_LOCK_FILE), os.O_CREAT | os.O_RDWR, 0o644)
try: