From 96b1e4c1142adabe6ecc52e595fbcc413bb8c46f Mon Sep 17 00:00:00 2001 From: Midhat Nashar <35053211+midhatn@users.noreply.github.com> Date: Mon, 9 Nov 2026 06:41:15 +0300 Subject: [PATCH 1/3] Allow bounded Windows refusal observation after terminal shutdown --- REFERENCES.bib | 7 + .../validation/video_coexistence/README.md | 22 +++- .../video_coexistence/observe_adaptive.py | 28 ++-- tests/test_video_adaptive_observation.py | 123 ++++++++++++++++++ 4 files changed, 169 insertions(+), 11 deletions(-) diff --git a/REFERENCES.bib b/REFERENCES.bib index 08ee883..b890e0d 100644 --- a/REFERENCES.bib +++ b/REFERENCES.bib @@ -1131,3 +1131,10 @@ @misc{erlangOtpSupervision url = {https://www.erlang.org/doc/system/sup_princ.html}, note = {Inspected 2026-11-08 for automatic desktop tool recovery. Design reference only for a supervisor-owned, bounded replacement of a failed worker (restart intensity). Implemented through this repository's existing retained-child lifecycle, task journal and reconcile command; no Erlang/OTP code copied} } + +@misc{cpythonWindowsLoopbackRefusal, + author = {Steve Dower and Python contributors}, + title = {socket.connect() slow to time out on Windows, CPython issue 85480}, + url = {https://github.com/python/cpython/issues/85480}, + note = {Inspected 2026-11-09 with Python urllib.error and http.client documentation and Microsoft's TCP_INITIAL_RTO_PARAMETERS reference. A bounded local Python 3.11.9 loopback diagnostic reproduced two-second connect timeouts and actual WinError 10061 refusal with a five-second bound; accepted stalled responses remained timeouts. The GET-only observer reuses its existing urllib parser and loop with a longer bound only after known gateway identity and terminal-path existence, clamped to remaining loop time. No external implementation or TCP setting change is copied; historical physical observer failures remain unchanged} +} diff --git a/examples/validation/video_coexistence/README.md b/examples/validation/video_coexistence/README.md index ec595cd..9f3a4bb 100644 --- a/examples/validation/video_coexistence/README.md +++ b/examples/validation/video_coexistence/README.md @@ -136,8 +136,15 @@ the controller's policy-input stream. Blocking HTTP can delay samples; actual gaps are saved and missing CPU deltas stay unknown. It reads only the existence of the terminal result path while the case runs, never active SQLite or result contents. Redirects, oversized responses and a changed gateway instance are -refused. Its 2700-second loop bound is not a hard operating-system deadline for -every blocking operation. +refused. Normal requests retain a two-second timeout per blocking HTTP operation. +After both a known gateway instance and terminal-path existence have been +observed, the same GET may use up to five seconds to allow Windows to report a +closed localhost listener as connection refused. The selected timeout is clamped +to the remaining 2700-second loop allowance and saved as `status_timeout_s`, +alongside the selected two/five-second limit. No extra retry is added. This +per-operation timeout is not a hard operating-system deadline across connection, +response and body operations. CPU gaps remain unknown. Error rows retain the +underlying exception type, errno and Windows error number. Both helpers write fresh private sidecars next to the run. Terminal-path existence plus a refused status connection does not establish task correctness @@ -158,6 +165,17 @@ Component checks use `python -m unittest discover -s tests -p test_video_adaptive_observation.py -v`: actual inert Python children, fake loopback status and injected activity only. They do not qualify desktop coexistence. +The terminal timeout adjustment follows the Windows localhost behavior reported +in [CPython issue 85480](https://github.com/python/cpython/issues/85480), reviewed +on 9 November 2026, and reproduced in a bounded local no-engine diagnostic. +An accepted-but-stalled response remained a timeout at both settings; a longer +timeout did not convert it into closure. The observer still requires a known +instance, terminal-path existence and an actual `ConnectionRefusedError` or +WinError 10061. A timeout, reset or marker alone remains insufficient. This reuses +the existing urllib parser and loop; no external implementation, dependency, +per-socket TCP override or system setting is added. See the bibliography entry +`cpythonWindowsLoopbackRefusal` in [REFERENCES.bib](../../../REFERENCES.bib). + ## Auditing one closed inventory task After the physical owner has joined the parent and observer handles, use diff --git a/examples/validation/video_coexistence/observe_adaptive.py b/examples/validation/video_coexistence/observe_adaptive.py index 4850e9d..4b3378c 100644 --- a/examples/validation/video_coexistence/observe_adaptive.py +++ b/examples/validation/video_coexistence/observe_adaptive.py @@ -117,14 +117,21 @@ def observe(profile_path, run, *, activity_reader, qpc_now, clock_id, if stamp >= next_status: row['status_before_deadline_clock'] = qpc_now() next_status = time.monotonic() + status_interval_s + # Windows localhost refusal can arrive after two seconds + # (CPython #85480). Only a known terminal gateway gets the + # longer bound; timeout alone still cannot establish closure. + limit = 5. if terminal_exists and instance is not None else 2. + row['status_timeout_limit_s'] = limit + row['status_timeout_s'] = min(limit, max(0., deadline - time.monotonic())) try: - value = fetch_status(opener, url, min(2., max(.001, deadline - time.monotonic()))) - if instance is not None and value['instance'] != instance: - raise ValueError('Gateway instance changed') - instance = value['instance'] - valid += 1 - failures = 0 - row['gateway_status'] = value + if row['status_timeout_s'] > 0: + value = fetch_status(opener, url, row['status_timeout_s']) + if instance is not None and value['instance'] != instance: + raise ValueError('Gateway instance changed') + instance = value['instance'] + valid += 1 + failures = 0 + row['gateway_status'] = value except urllib.error.HTTPError as error: reason = 'http_error' fault = row['observation_error'] = {'type': 'HTTPError', 'status': error.code} @@ -134,7 +141,10 @@ def observe(profile_path, run, *, activity_reader, qpc_now, clock_id, refused = (isinstance(underlying, ConnectionRefusedError) or getattr(underlying, 'winerror', None) == 10061) row['observation_error'] = {'type': type(error).__name__, - 'reason_type': type(underlying).__name__, 'connection_refused': refused} + 'reason_type': type(underlying).__name__, + 'errno': getattr(underlying, 'errno', None), + 'winerror': getattr(underlying, 'winerror', None), + 'connection_refused': refused} row['consecutive_connection_errors'] = failures if terminal_exists and refused and instance is not None: reason = 'terminal_path_exists_and_connection_refused' @@ -175,7 +185,7 @@ def observe(profile_path, run, *, activity_reader, qpc_now, clock_id, 'active_case_file_contents_read': False, 'task_completion_claimed': False, 'qualification_claimed': False, 'scope': 'Independent foreground samples and GET-only gateway status. Not the controller sample stream, GPU attribution, task acceptance or cleanup proof.', - 'timeout_scope': 'Two seconds per blocking HTTP operation and a bounded sampling loop; not a hard OS wall-clock deadline. CPU gaps remain unknown.'} + 'timeout_scope': 'Two seconds per blocking HTTP operation, or five after terminal-path existence and a known gateway instance; the selected timeout is clamped to the remaining loop allowance. Not a hard OS wall-clock deadline across all operations. CPU gaps remain unknown.'} with receipt_path.open('x', encoding='utf-8') as stream: stream.write(json.dumps(receipt, indent=2, allow_nan=False) + '\n') return receipt diff --git a/tests/test_video_adaptive_observation.py b/tests/test_video_adaptive_observation.py index 8b249da..ee26022 100644 --- a/tests/test_video_adaptive_observation.py +++ b/tests/test_video_adaptive_observation.py @@ -9,6 +9,7 @@ import threading import time import unittest +from unittest import mock import urllib.error import urllib.request @@ -44,6 +45,20 @@ def sample(self): 'ram_available_gib': None, 'commit_available_gib': None}, 'errors': []} +class FakeClock: + def __init__(self): + self.now = 0. + + def monotonic(self): + return self.now + + def time(self): + return 1000. + self.now + + def sleep(self, seconds): + self.now += seconds + + class AdaptiveBindingTests(unittest.TestCase): def setUp(self): temporary = tempfile.TemporaryDirectory() @@ -203,6 +218,114 @@ def test_changed_gateway_instance_is_not_combined_into_one_run(self): self.assertFalse(result['task_completion_claimed']) self.assertEqual(calls, [('GET', '/status'), ('GET', '/status')]) + def test_known_terminal_gateway_requires_real_refusal_after_owned_listener_closes(self): + port, calls, close = self.server([self.status()]) + run = self.folder / 'closed-gateway' + original_fetch = observer.fetch_status + def fetch(opener, url, timeout): + value = original_fetch(opener, url, timeout) + close() + run.mkdir() + (run / 'result.private.json').write_text('existence only, not JSON') + return value + with mock.patch.object(observer, 'fetch_status', side_effect=fetch): + result = self.observe(self.profile(port), run, FakeActivity(), duration_s=10) + rows = OBSERVATIONS[-1]['rows'] + requests = [row for row in rows if 'status_timeout_s' in row] + self.assertEqual(result['end_reason'], 'terminal_path_exists_and_connection_refused') + self.assertEqual(result['valid_responses'], 1) + self.assertEqual(calls, [('GET', '/status')]) + self.assertEqual([row['status_timeout_limit_s'] for row in requests], [2., 5.]) + self.assertEqual([row['status_timeout_s'] for row in requests], [2., 5.]) + error = requests[-1]['observation_error'] + self.assertEqual(error['reason_type'], 'ConnectionRefusedError') + self.assertTrue(error['connection_refused']) + self.assertIsInstance(error['errno'], int) + if sys.platform == 'win32': + self.assertEqual(error['winerror'], 10061) + self.assertFalse(result['task_completion_claimed']) + + def test_terminal_marker_does_not_turn_timeout_or_reset_into_closure(self): + for name, error in (('wrapped-timeout', urllib.error.URLError(TimeoutError())), + ('response-timeout', TimeoutError()), + ('reset', ConnectionResetError(10054, 'fixture reset'))): + with self.subTest(error=name): + run = self.folder / name + calls = [] + def fetch(opener, url, timeout): + calls.append(timeout) + if len(calls) == 1: + run.mkdir() + (run / 'result.private.json').write_text('existence only') + return json.loads(self.status()[2]) + raise error + with mock.patch.object(observer, 'fetch_status', side_effect=fetch): + result = self.observe(self.profile(), run, FakeActivity(), duration_s=10) + self.assertEqual(result['end_reason'], 'three_connection_errors_after_response') + self.assertEqual(calls, [2., 5., 5., 5.]) + errors = [row['observation_error'] for row in OBSERVATIONS[-1]['rows'] + if 'observation_error' in row] + self.assertEqual(len(errors), 3) + self.assertTrue(all(not item['connection_refused'] for item in errors)) + self.assertFalse(result['qualification_claimed']) + + def test_terminal_marker_without_known_instance_keeps_normal_bound(self): + run = self.folder / 'unknown-gateway' + clock = FakeClock() + calls = [] + def fetch(opener, url, timeout): + calls.append(timeout) + run.mkdir(exist_ok=True) + (run / 'result.private.json').write_text('existence only') + raise urllib.error.URLError(ConnectionRefusedError(10061, 'fixture refusal')) + with mock.patch.object(observer, 'time', clock), \ + mock.patch.object(observer, 'fetch_status', side_effect=fetch): + result = self.observe(self.profile(), run, FakeActivity(), duration_s=.15) + requests = [row for row in OBSERVATIONS[-1]['rows'] if 'status_timeout_s' in row] + self.assertGreaterEqual(len(calls), 2) + self.assertTrue(any(row['terminal_path_exists'] for row in requests)) + self.assertTrue(all(row['status_timeout_limit_s'] == 2. for row in requests)) + self.assertTrue(all(0 < bound <= 2. for bound in calls)) + self.assertEqual(result['end_reason'], 'observation_deadline') + self.assertIsNone(result['gateway_instance']) + + def test_known_instance_without_terminal_marker_keeps_normal_bound(self): + calls = [] + def fetch(opener, url, timeout): + calls.append(timeout) + if len(calls) == 1: + return json.loads(self.status()[2]) + raise urllib.error.URLError(ConnectionRefusedError(10061, 'fixture refusal')) + with mock.patch.object(observer, 'fetch_status', side_effect=fetch): + result = self.observe(self.profile(), self.folder / 'no-marker', FakeActivity(), + duration_s=10) + self.assertEqual(calls, [2., 2., 2., 2.]) + self.assertEqual(result['end_reason'], 'three_connection_errors_after_response') + self.assertFalse(result['terminal_path_exists']) + + def test_terminal_timeout_is_clamped_to_original_outer_allowance(self): + clock = FakeClock() + run = self.folder / 'near-deadline' + calls = [] + def fetch(opener, url, timeout): + calls.append(timeout) + if len(calls) == 1: + run.mkdir() + (run / 'result.private.json').write_text('existence only') + clock.now = 9.5 + return json.loads(self.status()[2]) + clock.now += timeout + raise TimeoutError() + with mock.patch.object(observer, 'time', clock), \ + mock.patch.object(observer, 'fetch_status', side_effect=fetch): + result = self.observe(self.profile(), run, FakeActivity(), duration_s=10) + self.assertEqual(calls, [2., .5]) + self.assertEqual(result['elapsed_s'], 10.) + self.assertEqual(result['end_reason'], 'observation_deadline') + requests = [row for row in OBSERVATIONS[-1]['rows'] if 'status_timeout_s' in row] + self.assertEqual(requests[-1]['status_timeout_limit_s'], 5.) + self.assertEqual(requests[-1]['status_timeout_s'], .5) + def test_redirect_and_oversize_are_refused_without_followup(self): for response, expected in ( ((302, {'Location': '/other', 'Content-Length': '0'}, b''), urllib.error.HTTPError), From d55d3b321db732be2f44dea97d385bb1c950d752 Mon Sep 17 00:00:00 2001 From: Midhat Nashar <35053211+midhatn@users.noreply.github.com> Date: Mon, 9 Nov 2026 07:07:47 +0300 Subject: [PATCH 2/3] Record installed compiler interruption and observer correction --- .gitattributes | 2 + CHANGELOG.md | 4 +- HANDOFF.md | 25 +- PLAN.md | 24 +- docs/compiler-tool-recipe.md | 21 +- docs/evidence.md | 31 +++ ...d-active-tool-interruption-2026-11-09.json | 201 ++++++++++++++++ .../video_coexistence/observe_adaptive.py | 202 ++++++++++++++++ .../tests/test_video_adaptive_observation.py | 221 ++++++++++++++++++ .../observer-shutdown/record.json | 132 +++++++++++ 10 files changed, 847 insertions(+), 16 deletions(-) create mode 100644 provenance/compiler/installed-active-tool-interruption-2026-11-09.json create mode 100644 provenance/video-coexistence/observer-shutdown/predecessor/examples/validation/video_coexistence/observe_adaptive.py create mode 100644 provenance/video-coexistence/observer-shutdown/predecessor/tests/test_video_adaptive_observation.py create mode 100644 provenance/video-coexistence/observer-shutdown/record.json diff --git a/.gitattributes b/.gitattributes index c97f69c..0b22881 100644 --- a/.gitattributes +++ b/.gitattributes @@ -78,3 +78,5 @@ provenance/video-tool/** -text whitespace=cr-at-eol provenance/video-tool/predecessor/src/adaptive_inference/desktop/hermes/report_contract.py whitespace=cr-at-eol,-blank-at-eof # Exact historical CI fixture bytes include their original CRLF line endings. provenance/ci-fixtures/** -text whitespace=cr-at-eol +# Preserve the observer version used by the retained physical failure. +provenance/video-coexistence/observer-shutdown/** -text whitespace=cr-at-eol diff --git a/CHANGELOG.md b/CHANGELOG.md index a861ffd..92e681c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,13 @@ These changes belong to the development source. The published `0.4.0a1` tag and downloadable packages retain their original capabilities and validation scope. +- Validate **active compiler interruption** through one installed Hermes SDK worker: durably retain interrupted feedback, hold the next provider request while foreground activity remains busy, then complete a distinct compiler call within the original task budget. Independent closed review passed 61 checks. The compiler/SDK were real; provider replies and foreground/resource signals were scripted. Actual IQ3_S/physical active-tool qualification remains open. See the [scoped evidence](provenance/compiler/installed-active-tool-interruption-2026-11-09.json). +- Correct the GET-only Windows shutdown observer's connection bound after a known instance and terminal marker: allow up to five seconds, clamped to its remaining outer allowance, while preserving refusal-only closure acceptance. Ten component checks and two affected-test follow-ups passed; stalled responses still fail. Earlier physical observer failures remain failed. See the [observer record and retained predecessor](provenance/video-coexistence/observer-shutdown/record.json). - Add **Open saved task** after a safe Stop, including tasks created later through the running controller. Accepted installed IQ3_S trials preserve the original task, confirmed reads, conversation, deadline and consumed work across application close/reopen in the same Windows boot. A completed task can be viewed without starting another worker or model, or writing its result again. See [saved-task reopening](docs/saved-task-reopening.md). - Add a bounded **C++17 compile-and-test recipe** that an opted-in Hermes task can request through the existing resource handoff. Reuse CMake/CTest, retained process ownership and durable tool feedback. An accepted IQ3_S foreground trial handed resources to the compiler and returned its checked result to the same task. See [the compiler recipe and evidence](docs/compiler-tool-recipe.md). - Add a bounded **GPU video recipe** using pinned FFmpeg/FFprobe and NVENC. An accepted IQ3_S foreground trial parked inference, encoded and checked one silent 90-frame test clip, restored the model and completed the task from saved tool feedback. This is a fixed test-clip capability; H3, audio synchronization and the full autonomous media workflow remain unqualified. See [the video recipe and evidence](docs/video-tool-recipe.md). - Recover missing tool feedback through the existing explicit reconciliation path when the completed result is known. Extend this to a pending journal entry when the live tool host retains the exact terminal result. Installed Hermes SDK trials use one actual video operation with scripted provider replies and resource observations; they preserve the task and deadline without replaying the operation. Unknown outcomes remain unresolved. See [completed-result recovery](docs/video-tool-recipe.md#completed-result-feedback-repair) and the [pending-effect receipt](provenance/pending-tool-recovery/installed-worker-loss-2026-11-02.json). -- Add **automatic desktop worker replacement** for that narrowly defined pending-result boundary. In the accepted real IQ3_S trial, foreground activity parked inference, one video operation finished, and the first worker exited before recording completion. The live host reconciled the saved result and launched one replacement automatically; the same task completed within its original deadline after two total model requests. Independent closed review passed 49 checks. The separate shutdown observer failed and remains an open diagnostic; this is not active-tool interruption, whole-host loss or reboot recovery. See [automatic recovery](docs/video-tool-recipe.md#automatic-desktop-recovery) and its [physical receipt](provenance/automatic-tool-recovery/physical-foreground-recovery-2026-11-08.json). +- Add **automatic desktop worker replacement** for that narrowly defined pending-result boundary. In the accepted real IQ3_S trial, foreground activity parked inference, one video operation finished, and the first worker exited before recording completion. The live host reconciled the saved result and launched one replacement automatically; the same task completed within its original deadline after two total model requests. Independent closed review passed 49 checks. The separate shutdown observer remains a failed result; the later component correction above does not requalify it. This trial does not establish active-tool interruption, whole-host loss or reboot recovery. See [automatic recovery](docs/video-tool-recipe.md#automatic-desktop-recovery) and its [physical receipt](provenance/automatic-tool-recovery/physical-foreground-recovery-2026-11-08.json). - Add the **durable reboot-budget foundation**: a fixed expiry and saved remaining-time cap prevent a restart from granting a new execution allowance. Source, package, installation and scripted SDK checks passed. The first actual Windows restart preserved saved work but the original allowance expired before continuation; execution was correctly refused. Successful computer-reboot continuation remains open. See [budget behavior](docs/reboot-budget.md) and the [retained expired attempt](provenance/installed-reopen/reboot-budget/physical-restart-expired-2026-11-02.json). - Add browser-video observation and comparison tooling, retain unsuccessful measurement attempts and correct continuation-test cleanup. The attempted comparison did not establish desktop/video performance because its observation conditions differed; no broad coexistence or speed claim follows from it. - Make evidence-based agent engineering an explicit goal requirement: bounded changes, meaningful acceptance, independent review, reproducible handoffs and a protected main branch. See the [collaboration practices](docs/multi-agent-collaboration.md#engineering-practices). diff --git a/HANDOFF.md b/HANDOFF.md index 7d10171..5b87dc1 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -58,7 +58,18 @@ negative expiry checks. Production deadlines and runtime source are unchanged. Use the exact current PR checks before integrating; historical or local passes do not override a failed required check. Main branch protection is enabled. -**Latest accepted M4 increment:** automatic desktop recovery of a pending +**Latest bounded M4 gate — 9 November 2026:** the +[installed active-compiler interruption case](provenance/compiler/installed-active-tool-interruption-2026-11-09.json) +passed 61 independent closed-evidence checks. One actual Hermes SDK worker +interrupted a running CTest operation, durably retained its feedback and completed +a distinct successful compiler call: three provider requests, two effects and the +same original task/deadline. Provider replies, foreground/resource observations +and inference-owner actions were scripted. The +[observer shutdown correction](provenance/video-coexistence/observer-shutdown/record.json) +has source/component and owned-loopback evidence; it does not relabel earlier +physical observer failures. + +**Preceding accepted M4 increment:** automatic desktop recovery of a pending compiler/video operation after worker loss. `DesktopComposition` now detects a non-zero worker exit with one pending effect whose exact terminal result is held by the live tool host, exports `CompilerHost.terminal_receipt`, runs the @@ -74,10 +85,10 @@ separate observer failed during shutdown; the accepted task/cleanup audit does not erase that failure. Recovery requires the live host's exact completed result and does not qualify interruption of an active tool or loss of that host. -**Next bounded work:** diagnose the retained observer shutdown failure before -relying on it for a fresh physical acceptance, then follow M4's active-tool -interruption gate. Reuse existing cancellation and ownership mechanisms and -research any missing behavior before implementation. Whole-host loss, successful +**Next bounded work:** qualify active-tool interruption with actual IQ3_S and +physical foreground activity using the reviewed observer correction. Reuse +existing cancellation and ownership mechanisms and research any missing behavior +before implementation. Worker loss during an active tool, whole-host loss, successful OS-reboot continuation and broad desktop/media qualification remain open under the roadmap. Preserve unchanged IQ3_S quality and the closed accepted/failed cases; a new experiment needs a fresh case, not a renewed historical budget. @@ -354,12 +365,12 @@ daily-use qualification remain open. | Integrated main starting point | Commit `f362ed0ed8c0f9f6552bd71c9c3ba9e8a847aa3a`, including PRs #34–#39, has the exact reviewed `08053a0` tree. Verify current `main`; this is the 9 November anchor, not a claim that it will remain HEAD. | | Released reference and active work | [v0.4.0a1](https://github.com/midhatn/adaptive-inference/releases/tag/v0.4.0a1) is published at tested source `57f673f8e934e275bc2bd51e85d434dc7025bc9a`; PR #28's merged tree matches it exactly. PR #29's measurement preparation merged after its exact-head checks. The first adaptive diagnostic and following control are closed; their scoped results and unmet comparison gates are recorded in the [evidence ledger](docs/evidence.md#first-adaptive-video-diagnostic). | | Installed source and evidence | The latest automatic-recovery [physical receipt](provenance/automatic-tool-recovery/physical-foreground-recovery-2026-11-08.json) binds its actual installation. Earlier later-task M5 used `b35b631aca9ec37da9955903b87463201a6b3630`; initial-task M5 used `6ac601546daf32d96e38b6689a5818e4cc8dafa8`; M3 video used `7306a53085ddc31d53d2ba50e91975c80cfc0014`. All preserved IQ3_S and quality. A source merge does not update those installations. | -| Current work | Compiler/video handoffs, known-result reconciliation and automatic desktop worker recovery are integrated. The latest physical trial passed 49 closed-audit checks at the completed-tool/surviving-host boundary. Observer shutdown remains unresolved; active-tool interruption and whole-host loss remain open. PR #34's budget source is integrated, but its expired physical restart attempt does not qualify successful reboot continuation. All consumed trials stay closed. | +| Current work | Compiler/video handoffs, known-result reconciliation and automatic desktop worker recovery are integrated. The latest physical trial passed 49 closed-audit checks at the completed-tool/surviving-host boundary. Active compiler interruption now passes the scripted-provider installed SDK gate; the observer has a component correction. Their fresh physical gate and loss during an active tool or of the whole host remain open. PR #34's expired physical restart attempt does not qualify successful reboot continuation. All consumed trials stay closed. | | Preserved M5 same-boot result | The installed same-boot application restart preserved one input read, the task/native session and original deadline; three calls total completed the correct 23-unit answer with one physical write. A third launch viewed the result without additional calls or changes to the eight canonical files. All three app parents joined normally; bounded observer shutdown errors remain retained. See the source-bound M5 record above. | | Preserved M2 recovery result | Case17 passed both original audits: 99 primary recovery/handover checks plus the foreground/lineage supplement. Its correct inventory result survived safe expiry and one Continue; a separate welcome task completed before one normal Stop. Six calls produced two physical writes. Welcome has format-only acceptance. Full M2 is broader than that run. | | Preserved limits and failures | Original quality, 900-second execution budgets, 2,700-second outer bound and task accounting remain unchanged. Cases14/15/16 keep their original failed outcomes. Case17 retains the rejected file-picker input and 30-second operating-target misses; neither repeated a task control nor violated the strict foreground gate. | | Foreground evidence | Case17 retained 25 real document actions, 24 wholly preseal, maximum adjacent interval 54.680872 seconds and preseal tail 37.453106 seconds. The original supplement passed onset, continuous hold and saved-read lineage; the postseal action is excluded from coverage. This is not a desktop-performance or GPU-preemption measurement. | -| Next bounded work | Diagnose the retained observer shutdown failure first, then qualify active-tool interruption using the existing compiler/video journal and owner, preserving completed effects and feedback. Whole-host loss remains separate. A future M5 attempt needs fresh identity, prompt postboot return and unchanged allowance. M3's bounded diagnostic remains video-surface attribution without inference. Follow PLAN.md before starting any fresh case. | +| Next bounded work | Qualify active compiler interruption with actual IQ3_S and physical foreground activity, using the reviewed observer and existing journal/owner while preserving task budget and feedback. Loss during an active tool and whole-host loss remain separate. A future M5 attempt needs fresh identity, prompt postboot return and unchanged allowance. M3's bounded diagnostic remains video-surface attribution without inference. Follow PLAN.md before starting any fresh case. | | Latest release validation | Corrected source `57f673f` passed all 14 original hosted jobs, final archive/runtime verification and merge-tree verification. All five uploaded assets and the tag were verified at publication. The 48-check watchdog and 21-check refusal corrections retain their own local scope; earlier failed runs remain failed. | | M3 publication | [PR #29](https://github.com/midhatn/adaptive-inference/pull/29) merged at `f6c0da1` with the tested `3136e35` tree. Its original 14 head jobs and seven post-merge jobs passed, including portable archive checks. Earlier archive/fixture failures remain retained; these preparation checks do not requalify the installed runtime. | | Diagnostic publication and test correction | [PR #30](https://github.com/midhatn/adaptive-inference/pull/30) merged at `c348398` with the tested `72ac717` tree after 14 head jobs and seven post-merge jobs passed. Its original `fd4d483` pair retains 13 passes and one Windows SQLite concurrency-fixture failure. The [bounded correction](docs/evidence.md#sqlite-concurrency-fixture-ownership) keeps test threads in a retained child and joins before cleanup; 18 focused checks passed. Runtime, models and physical records are unchanged. | diff --git a/PLAN.md b/PLAN.md index af32642..5276265 100644 --- a/PLAN.md +++ b/PLAN.md @@ -53,7 +53,7 @@ A roadmap entry is not a qualification receipt. | Native foreground and tool work | One complete source-bound Windows/IQ3_S workflow is accepted: real foreground interaction, verified release, checked GPU encode, restoration and clean shutdown. | | Resource-aware agent waiting | Immediate admission and the pinned Hermes recovery adapter completed the same accepted workflow, with durable waiting following foreground interruption and one same-boot worker restart. | | Desktop protection observation | A later qualification withheld model startup while a foreground game was active. That observation is not a gaming-performance benchmark or a completed qualification. | -| Integrated M4 development | Bounded IQ3_S compiler/video foreground handoffs and automatic worker recovery with an exact completed result retained by the surviving tool host are accepted at their recorded scopes. The independent shutdown observer remains unresolved. | +| Integrated M4 development | Bounded IQ3_S compiler/video foreground handoffs and automatic worker recovery with an exact completed result retained by the surviving tool host are accepted at their recorded scopes. Active compiler interruption now passes an installed SDK gate with scripted provider/signals; the observer correction has component evidence. Their physical qualification remains open. | | Restart development | Same-boot application close/reopen is accepted for the recorded tasks. The reboot-budget foundation is integrated, but the first actual reboot attempt expired before continuation; successful OS-reboot recovery remains open. | | Full always-on experience | Persistent everyday operation, broad foreground coverage, physical reboot recovery and native qualification of other engines remain development work. | @@ -75,11 +75,21 @@ passed 49 closed-audit checks at the exact-known-result boundary described in the [evidence ledger](docs/evidence.md#physical-iq3_s-foreground-recovery). That result does not complete M4 or qualify successful computer-reboot recovery. -**Next bounded work:** diagnose the retained observer shutdown failure using its -closed evidence and current primary documentation, then continue M4's active-tool -interruption gate through the existing owned-process and handoff mechanisms. -Preserve completed effects, original task/budget and unchanged IQ3_S quality. -Whole-host loss and the M5 restart/reboot sequence remain separate gates. The +**Latest bounded gate — 9 November 2026:** an installed Hermes SDK worker +interrupted an actual compiler test, retained its feedback and completed a +distinct successful compiler call within the same task and original budget. +[Independent closed review](provenance/compiler/installed-active-tool-interruption-2026-11-09.json) +passed 61 checks. Provider responses, foreground/resource observations and +inference-owner actions were scripted; actual IQ3_S inference was not run. +The [observer correction](provenance/video-coexistence/observer-shutdown/record.json) +has owned-loopback/component evidence; earlier physical observer failures remain +failed. + +**Next bounded work:** qualify active-tool interruption with actual IQ3_S and +physical foreground activity, using the reviewed observer correction and existing +owned-process/handoff mechanisms. Preserve completed effects, the original task +and budget, and unchanged IQ3_S quality. Worker loss while a tool is still active, +whole-host loss and the M5 restart/reboot sequence remain separate gates. The expired reboot trial stays closed; successful continuation needs a fresh, separately coordinated case. Keep this main workflow ahead of optional ports or optimization. Do not replay accepted trials merely because their source merged. @@ -248,7 +258,7 @@ and public reproduction retain their separate evidence boundaries. | M1 | One complete corrected handoff and agent-restart workflow | Existing foundation | Accepted for the bounded Windows/Strata/Hermes IQ3_S composition | | M2 | A practical persistent controller with durable waiting and clear user intent | M1 for live integration acceptance | Active: bounded foreground/continuation/two-task run passed both audits; public reproduction and task preparation/setup are available; reference alpha delivered; persistent everyday use remains open | | M3 | Measured coexistence with real desktop applications | M2 for broad qualification; a bounded control-window policy now feeds M2 | Early policy work active; broad desktop qualification planned | -| M4 | Agent-driven compiler and GPU-tool handoffs | M1 and M2; foreground acceptance from M3 | Active: bounded IQ3_S compiler/video foreground handoffs and exact-known-result automatic worker recovery accepted; observer shutdown, active-tool interruption, whole-host loss and broader workflows remain open | +| M4 | Agent-driven compiler and GPU-tool handoffs | M1 and M2; foreground acceptance from M3 | Active: bounded IQ3_S handoffs and exact-known-result automatic worker recovery accepted; active compiler interruption has installed SDK evidence and observer shutdown has a component correction. Their fresh physical gate, loss during an active tool, whole-host loss and broader workflows remain open | | M5 | Recovery across application restart, sleep/wake and computer reboot | M2 and supported M4 effect contracts | Active: bounded same-boot application close/reopen accepted; durable reboot-budget checks passed; first actual reboot attempt expired; successful OS reboot and sleep/wake remain open | | M6 | A reproducible daily-use release with fault and soak qualification | M3, M4 and M5 for its advertised scope | Planned | | M7 | Broader engine and agent adapters using the same contracts | M2 contract stability; native acceptance follows M3–M6 | Staged expansion | diff --git a/docs/compiler-tool-recipe.md b/docs/compiler-tool-recipe.md index 25d5a22..cfa6034 100644 --- a/docs/compiler-tool-recipe.md +++ b/docs/compiler-tool-recipe.md @@ -2,7 +2,9 @@ Status: source, local package/installation, actual SDK/compiler feedback and the bounded IQ3_S compiler/foreground workflow are verified at the scopes below. -Observer shutdown and broader interruption/daily-use qualification remain open. +An installed SDK active-interruption gate and observer component correction are +also verified below; actual IQ3_S active-tool interruption and broader daily-use +qualification remain open. This is the first small useful-tool increment in [M4](../PLAN.md#m4--let-the-agent-hand-resources-to-useful-tools); the separate reboot installation remains fixed. The first physical restart preserved saved @@ -14,6 +16,23 @@ result returns to the same conversation. The next IQ3_S request can proceed only after confirmed resource release or a truthful failure. The original model, context, quality, task allowance and consumed work remain unchanged. +## Active compiler interruption — installed SDK gate + +On 9 November 2026, one installed Hermes SDK worker interrupted an actual running +CTest operation, retained its feedback and completed a distinct successful +compiler call within the original task/session/deadline. The controller held +provider admission while foreground activity stayed busy. The first operation's +children were joined and its Job emptied before the second operation passed its +stdout test. Independent closed review passed 61 checks, with three provider +requests, two effects and all 114 original case files unchanged. See the +[source-bound record](../provenance/compiler/installed-active-tool-interruption-2026-11-09.json). + +Provider replies, foreground/resource observations and inference-owner actions +were scripted. The next gate uses actual IQ3_S and physical foreground activity +with the [reviewed observer correction](../provenance/video-coexistence/observer-shutdown/record.json). +Worker loss while the tool is active, host loss and successful Windows-reboot +continuation remain separate unqualified cases. + ## Reuse and scope The existing `HandoffController.acquire_tool`, `ToolGrant.interruption` and diff --git a/docs/evidence.md b/docs/evidence.md index 88805be..472bc87 100644 --- a/docs/evidence.md +++ b/docs/evidence.md @@ -51,6 +51,37 @@ allowances are unchanged. These source tests do not qualify installed agents, hardware, physical responsiveness or reboot recovery. Successor hosted CI is a separate gate; a local pass does not relabel an original failed run. +## Installed active-tool interruption and observer correction — 9 November 2026 + +The [installed SDK/compiler record](../provenance/compiler/installed-active-tool-interruption-2026-11-09.json) +captures one worker, three provider requests and two distinct compiler effects. +The first completed configure/build and entered an actual CTest test before +foreground activity was injected. A retained live CTest handle, exact Job +membership and flushed stderr marker establish that the test was running. +The controller interrupted its grant, joined the children and retained an empty +Job plus durable interrupted feedback. No provider request proceeded during the +foreground hold. After it cleared, a distinct compiler operation passed CTest +and produced exactly `23 units`; the original task/session/deadline completed. + +Independent inspection of fresh closed database copies, native SDK messages, +admission/wire records and raw compiler artifacts passed **61 checks**. All +114 original case files remained byte-identical. All 25 budget-bearing +checkpoints retained the original allowance; the worker and tool host closed +normally. Provider replies, foreground/resource observations and inference-owner +actions were scripted. This does not qualify actual IQ3_S active-tool recovery, +physical foreground resource handoff, active-tool worker loss, host loss, reboot, +full media quality or a performance improvement. + +The separate [observer correction record](../provenance/video-coexistence/observer-shutdown/record.json) +retains its predecessor sources and owned-loopback diagnosis. After observing a +known instance and terminal marker, the GET timeout can reach five seconds, +clamped to the remaining outer allowance; ordinary polling stays at two seconds. +Actual connection refusal remains required, and timeout/reset responses remain +failures. Ten component tests passed, followed by two affected-test checks after +making a test clock deterministic; these are not twelve distinct tests or a new +full-suite run. This is source/component evidence pending fresh physical use. +Earlier physical observer failures remain unchanged. + ## Agent-requested GPU video and foreground handoff The [second physical video task](../provenance/video-tool/physical-foreground-video-2026-11-02.json) diff --git a/provenance/compiler/installed-active-tool-interruption-2026-11-09.json b/provenance/compiler/installed-active-tool-interruption-2026-11-09.json new file mode 100644 index 0000000..6ae120e --- /dev/null +++ b/provenance/compiler/installed-active-tool-interruption-2026-11-09.json @@ -0,0 +1,201 @@ +{ + "schema": "adaptive.active-tool-interruption-validation.v1", + "date": "2026-11-09", + "status": "passed_at_declared_scope", + "documentation": "docs/compiler-tool-recipe.md", + "scope": "One installed Hermes SDK worker with real C++ configure/build/CTest processes; scripted provider responses, foreground observations, resource samples and inference-owner adapter.", + "environment": { + "platform": "Windows x64", + "hermes_source": "https://github.com/NousResearch/hermes-agent", + "hermes_revision": "14e1e43a23761b369641208e204365cd6cb9399d", + "model_configuration": { + "model": "qwen3.8-flash-next-iq3_s-adaptive-media-beta", + "quality": { + "max_tokens": 10923, + "reasoning_effort": "high", + "reasoning_budget_tokens": 8192, + "experimental_speed_projection": false, + "strata_tune": { + "pcie_frac": 0.37 + } + }, + "actual_inference": false + }, + "tools": { + "cmake": { + "sha256": "ab8247ca4554871e5d75c0ee118ee8663727bd6fdf37ad76b17e9cc8d5f286a8" + }, + "compiler": { + "sha256": "ec094d5f8f37c106eefcfc47c9997c261a06e3bd8eaaa7255835983a58524156" + }, + "ctest": { + "sha256": "083f482c8656c86932c28906445cb3c10c0c5ee649a90ff3e0080ee4447d3dfc" + }, + "ninja": { + "sha256": "9546466f0feca6d62d7d4ea9b7fbc381f8e302cb7f02f4ce327a39d8577bb293" + }, + "linker": { + "sha256": "631a02cecb87f9f7f2270713d6c7f84e54a0fabce6c6638d21d3a0caea78204d" + } + } + }, + "method": [ + "Require successful configure/build, actual test-start log and flushed child stderr marker before injecting foreground activity.", + "Retain and validate a live CTest process handle against its exact compiler Job, executable hash and creation identity.", + "Inject foreground activity through the controller observation path; interrupt the active grant while keeping the compiler host service available.", + "Keep foreground busy until interrupted feedback is durably committed and the next provider request is waiting for admission.", + "Clear foreground activity, permit a distinct compiler operation and verify its stdout and CTest result.", + "Close the worker and compiler host normally; inspect copied closed databases and raw artifacts without replaying the case." + ], + "core_result": { + "worker_processes": 1, + "provider_requests": 3, + "compiler_effects": 2, + "compiler_statuses": [ + "interrupted", + "passed" + ], + "first_test_actually_running_before_foreground": true, + "first_job_terminated_and_all_children_joined": true, + "first_job_active_processes_after_interruption": 0, + "interrupted_feedback_committed_before_next_request": true, + "provider_requests_during_foreground_hold": 0, + "second_operation_distinct": true, + "second_test_name": "program_stdout", + "second_stdout": "23 units", + "second_recipe_all_stage_exit_codes_zero": true, + "same_original_task_session_and_deadline": true, + "original_execution_seconds": 900, + "budget_renewed": false, + "durable_budget_checkpoints_preserved": 25, + "native_sdk_messages": 6, + "terminal_task_status": "accepted", + "normal_worker_exit_code": 0, + "forced_worker_cleanup": false, + "compiler_host_usable_between_operations": true, + "compiler_host_closed_normally_after_completion": true, + "retained_outer_process_exit_code": 0, + "coordinator_post_close_listener_and_workload_absence_observed": true + }, + "independent_review": { + "method": "61 independent file-only checks using fresh copies of closed task and SDK databases; no workload rerun.", + "checks_passed": 61, + "original_case_files_hashed_before_and_after": 114, + "all_original_file_names_lengths_and_hashes_unchanged": true, + "copied_database_base_hashes_unchanged": true, + "raw_evidence": [ + "Compiler stage logs", + "Test stderr marker", + "Retained CTest and Job witness", + "Admission event log", + "Task checkpoint and operation history", + "Native SDK messages and stable tool-call identities", + "Wire/backend request digests", + "Executable, stdout and JUnit artifact hashes", + "Normal worker and host closure" + ] + }, + "evidence_sha256": { + "closed_case": "7f1cb2220cb5304fbb297e955650129db0a7cc08500d2464ec0059daae9930ef", + "independent_closed_review": "722e1a728c306acf4976b7dd7c8e855a014f45d333225e4f05cd9ff60321d3b6", + "independent_review_script": "cfc029a4cee04a850fe1494193d8f1cb290107b08c16f4000f5ececcee465b21", + "reviewed_harness": "679a766aa0a030e1ab68d4c05c24a79153b1719ade70fd3b6c2dc6f7d06af2fa", + "coordinator_outer_closure": "6d7834f71cd622cf1b64bc7239da72d66c521939a6b738594dcde6241d538ca6", + "identity": "63f599071fa5827ffefbedaf2ad8b5a8dbf4b53aed1af5320056def9ccbca028", + "installation_receipt": "4af07b15431f750063f44b10c2ba279996b9d1bd1710762471f42403669b35cb", + "packaging_receipt": "971f779bad97a26f58d519f84958539090941f2ecf105fbd4ec378b68a793232", + "profile": "e0a3be7f9db8f78b4f59b81f2e598eeda46e2dc8cee8cd8592c9a1bc68eb25cd", + "profile_preparation": "5a904021afd2bd1bbc2cd460c528f2deece4bf5502300c491e1ad1133f214d5c" + }, + "runtime_source": { + "version": "0.4.0a1", + "module_count": 77, + "same_hashes_in_current_source_package_host_and_worker": true, + "module_sha256": { + "__init__.py": "1d906cca6d5581290d73292cce949dcaf74001de67bda63b903dfb2e70edacba", + "__main__.py": "935a1c1166b0c1ea35a82256345000bf2c73ded718d77773bc27a71ecce28f7d", + "adapters.py": "378ee41c1bdbb16873e31752d704e656a70009862d41b36898d30f57a36dfc58", + "cli.py": "a13d6c2c52445884f2ee9897eb7fadb75188339be4472db98bd760c18bce5ecf", + "client_routing.py": "d57178cfbd5666e75aef311f1362cf8486f48085c6f4995861b4fbd0cb7e41d1", + "config.py": "a6800a081a1e14a46ee18235dd21c1d1a714e6ef4e879840cd2768c590fde669", + "context_budget.py": "cd16b57bc105a179e94dbc9e429c02b901bcd8602cfaf04c9cf8776e9d799ccf", + "control_mailbox.py": "12e6ceaef36b41099422040fb23db0a9bccf96a6afb799f0e301a64bccb95888", + "costs.py": "9e8fcd06d39af57392f191b5d5701aa828f134e5141c7e298aa62e0c232224e1", + "demand.py": "76a9fbb26128c1e458348a37f0d15c36f6a9a5ae2d0f89b15d372bd2d1e394ad", + "demo.py": "5a3b1ff743bee6c45f85df8417bedbc18657f2c59f3e894f1f3298a480b22b93", + "desktop/__init__.py": "6169073159f3c0e506752f54aa51b40b058f71ed058d5272e160aa4a61ee7daa", + "desktop/activity.py": "3282dc37b493ba3fd347e279b074fa3dfd5d2c1ea5eaba8131dc568d97ec0750", + "desktop/cold_start.py": "1264f34910a0d619c6502c7565e66abbfe7ec642391a461390271b8e0799bf68", + "desktop/compiler_bridge.py": "a33613e13995c098488144557a87ee4b34b1ed3f4342610b3a43fa8923945299", + "desktop/compiler_host.py": "95a7d000bea96f527996e360eadb7324aaafd583506d4587dae89288d47ab3d2", + "desktop/compiler_profile.py": "872b8babf3fe26828ce50422485489fdde35bd59f010ff063be66ef253e323fc", + "desktop/compiler_recipe.py": "12b31514327a7c168c915b3a321ee1c17bf8446b49181eed6bed2bced55b01d2", + "desktop/compiler_template.py": "bf7a3123eb147caded7700c7f67e0c072cfe915239fa3a5cb004e7e1772f7f85", + "desktop/composition.py": "a8688f7dadceac26747e139a8d2e53e3a7a1dd5bdb71751049ab72698650165f", + "desktop/continuation_host.py": "9bd258aa8b1a2516e80cd94641f2a9fce3584c5b8aa484badf9854dfc121d7c7", + "desktop/control_client.py": "ba950c73328e084329fdb29ab78fdfe86cfed1715a792cdbf9872a74e8c75919", + "desktop/control_transport.py": "280d8a55793c241b3ec30e253b758de42386f3329bb6477ba47537f9ca264c6b", + "desktop/desktop_controls.py": "89eeb900ce8bcdb079d5400f09408a56e1df15383d58ec74d405a027795469c9", + "desktop/execution_budget.py": "77ad74ddbf0eaa89379fbecb305574d5b70cc32f9e63934a14f439ed2c744728", + "desktop/foreground_policy.py": "35781fb869c03220c6f48c3e138083b15bbcf7e40187edbfe546ad4da09b7faa", + "desktop/gui_launch.py": "c93e837ae15092de1a7814f766731ef38dc118f3e8a50280ee7658b91966355b", + "desktop/gui_session.py": "f232609a01f11977d7f011a8b1c7f2b7c2a0c3ca697766b63ec0873ec9f4cc64", + "desktop/hermes/__init__.py": "43830f7aa3deb6001eb8aa8646fc515f5d8d74a26f50590984c8278e4db67532", + "desktop/hermes/admission.py": "6f12fd6ad21fa194cb337590cf0bbd71b1d394e6a2be0cd79dfdd634af5e0f07", + "desktop/hermes/compiler_client.py": "fb0372c6b16738cdb1b0c8d527765e8b7f60a038513950988c84d40b741bf8a1", + "desktop/hermes/compiler_evidence.py": "8b2144e5b1634d8e2bc132711d948308ebc62320e98f9129c44817848a806d56", + "desktop/hermes/compiler_recovery.py": "03d19056b0c2f73679754aa3eab215d3fb2728b22c71a5ab5e1c327ad6a8c114", + "desktop/hermes/compiler_worker.py": "8d18ef60025318804e81f848546e1c3e0e84f61cf4a09fe7bfd1c759b243d91e", + "desktop/hermes/continuation.py": "d3b396bba2ed794b7304e3bebb888e2d8d51568de846fbe6549363b42d6de5c6", + "desktop/hermes/continuation_worker.py": "6871ecbfaec346ccd658d957b5f593f0dc242db17c1b13b93ab603f693967b67", + "desktop/hermes/control_bridge.py": "7a8e0aa672efeb1c6f676446e0f3b6886fd778f5d0f6c185a87d005a0256a3db", + "desktop/hermes/native_reader.py": "1f51ea1180ef0d7ad52359430831084de7cde45e21e758068fee5b9a93079464", + "desktop/hermes/pause_activity.py": "3cffa8eae39b956de3d873e82593a052c2682aad5f06b36a8ff845080baf0e5d", + "desktop/hermes/recovery.py": "c6f07435ae29f4e2542af585ace107f7952583ed3c0735cf6df2f3a0df438f37", + "desktop/hermes/report_contract.py": "d86aa6ee0f18912400aa80d96c343128af08ae80e7d8c367ee9c8733ea5d69de", + "desktop/hermes/report_validator.py": "378dd445a088a075d300fc85b86160f3b96790ee046adecf926f9e59e8e7d9af", + "desktop/hermes/run_recovery.py": "3d78a3f1f78e024d04b0020e3702c784dfc3723a802bdb87c9cbca25ec827fd3", + "desktop/hermes/sdk_pins.py": "538a5ea8651e1106c7c535ccd73b33b2f56de6c736d926ce6b92c5fb6eb075ce", + "desktop/hermes/service_stop.py": "235b61ad248083f8f0536767ad79473955d96ad1b40189b73c9cfe71439a51c9", + "desktop/installation.py": "13e13d532bedb68f150d1096767c98796de83f74dce54021d286c6c18c521e85", + "desktop/load_feasibility.py": "23e94f3ee770b065fb323f30bb104d8d5732bf710bd3cc9b2a27daa746014dda", + "desktop/mailbox_runtime.py": "aad1c3b383f6954be89ebe6c9f2e680cd286c672d7bb26bd7027402e0a7d6b72", + "desktop/native_owner_adapter.py": "5a79c48911c798310104be07ebb978f9ad9fe4f7ab000c328afd992f75415977", + "desktop/owned_job.py": "90b59dbdae226419fc9680c961c21dfa9e061df08512a7a8089d6f6d11b2ccc4", + "desktop/persistent_host.py": "acff567d5eb6f2c569588bfa684363907897bdb66be8008bc20c10d3ca7c0011", + "desktop/profile.py": "caac6f07fc87ccbf51d97003a0d8cb209d21e04bfc00acda6a5c9e05b2d3fbbc", + "desktop/recipe_dispatch.py": "3a4813e156448305e7880c3cc3ac3b82e992b34a0e05b7f6b74f09bc9e2c6f4b", + "desktop/retained_ui.py": "749b947510377518c452ae7a70fa21c32f27c3f10225f6eb90d87ef03406f5cb", + "desktop/run_gui.py": "eb67c9bac75b72bcc545c234b28c54b8c9c5fbfb3c2183f99e63a47abb3a6ef0", + "desktop/saved_tasks.py": "75703584b3c4cda42aed6de0e4645ea75b2a3b1e2e81c702e919f626cc8ec1b7", + "desktop/serial_listener.py": "048e8ab061e175a978f755801b9a27e3daa292c1dc7dabe3c8a8763be81199b2", + "desktop/startup_pipe.py": "0617c527461b4c437474a947a497300e4c28c06d11f8bb43f1e67fc51a159fae", + "desktop/task_admission.py": "2ca007580bca43678c529599b6296ff4b97c15e210b9daeb651a8f2c4f12b25d", + "desktop/task_handover.py": "8fd90d86635a3b5968a39d32b20e54e67b17c8dc9bb4a90b9fcdcabff97f01b4", + "desktop/ui_intent.py": "ff67208a3890656bc76717ac18c8d0c02a19dc2e09b11b81d88fc1f63c0a29d1", + "desktop/ui_progress.py": "466deda7ba15d73ea779abf3b1b2e690e92217950b15afdbdf8acf9cda3d2de9", + "desktop/video_profile.py": "2ac53c40937e60d984e2903d643d2ee83372760756d1afedf5406b71644b41e4", + "desktop/video_recipe.py": "b255d3f939aa3a1e29449bc397e39b50859fcc0cf8a10201859b2ec2dc50bcff", + "desktop/video_template.py": "1a74d771d83eed358196467857e11c5986cfc6c196b6d172c614224db311a835", + "desktop/window_marker.py": "e2613ef662993be88c332657e4e53c598fdc006634bf8fabf9e0d0a9765c0d36", + "desktop/windows_host_base.py": "ad08c31435bc4da77b43c68321a7c08130ab1c4f7bc688c71191c6497abc97bd", + "execution_clock.py": "c9d04e8e029d4d88830560482e2437cab3d06ef140a942d16543c3be95642cff", + "feedback.py": "c76eba0110659ae67f4a0efd2ca21aff0987af7e9095998e4db9382de20826c3", + "gateway.py": "b2009a8a776bcea4dfc3bedac5296c930ae2211bb8e29d7ccdf76a227fefdef8", + "handoff.py": "6cd7f593698f6d72fc3ac16d9a541436f65b85103bed2420372364262fcb4db1", + "resource_policy.py": "53768d4b95b589bb76d1788a9ccceefe60c05b332b1b1f43dece6b04bfec212a", + "resources.py": "a72e376e4dfc6021e61756812f93275af20c28ec1438faf5eb381a491d3db4d9", + "scheduler.py": "c7f962bd9ef8e44587cadcc02a41fae8a00de7fc15b105848dcdfc4862bd5b4f", + "task_prepare.py": "9cddd6e09b3f5435da08316f8d9ccdfbcff67a336f6f74b420108c4c856223b2", + "task_spec.py": "2975c0c4489ac863502189777699d13d7b2b8aca6d19191247fd860ed4a0ce76", + "task_store.py": "7bf559ea55e0c19155515f321f7505b4d9275648333bcf2c983c2c280bd2e2e6" + } + }, + "limitations": [ + "The provider was scripted. This does not qualify actual IQ3_S model inference or autonomous model-selected retry behavior.", + "Foreground and resource observations and inference-owner actions were scripted. This does not qualify physical foreground coexistence, real GPU or memory handoff, or the installed native inference engine.", + "One worker remained alive throughout. This does not qualify worker replacement or Windows restart recovery.", + "No end-to-end media workflow, audio/video quality, matched baseline or performance improvement was measured.", + "The first compiler operation was terminated and recorded as interrupted. The successful operation was a new permitted call, not continuation of the first child process." + ] +} diff --git a/provenance/video-coexistence/observer-shutdown/predecessor/examples/validation/video_coexistence/observe_adaptive.py b/provenance/video-coexistence/observer-shutdown/predecessor/examples/validation/video_coexistence/observe_adaptive.py new file mode 100644 index 0000000..4850e9d --- /dev/null +++ b/provenance/video-coexistence/observer-shutdown/predecessor/examples/validation/video_coexistence/observe_adaptive.py @@ -0,0 +1,202 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Bounded GET-only status plus independent foreground observations, no control.""" +import argparse +import hashlib +import inspect +import json +import math +import os +from pathlib import Path +import re +import time +import urllib.error +import urllib.request + +from adaptive_inference.desktop.profile import WindowsProfile + +OUTER_SECONDS = 2700 +MAX_BYTES = 64 * 1024 + + +def sha(path): + return hashlib.sha256(Path(path).read_bytes()).hexdigest() + + +class NoRedirect(urllib.request.HTTPRedirectHandler): + def redirect_request(self, req, fp, code, msg, headers, newurl): + return None + + +def invalid_constant(value): + raise ValueError('Non-finite JSON constant: ' + value) + + +def fetch_status(opener, url, timeout): + """Reuse the task-handover observer's bounded response/identity checks.""" + request = urllib.request.Request(url, method='GET', headers={ + 'Accept': 'application/json', 'Accept-Encoding': 'identity'}) + try: + with opener.open(request, timeout=timeout) as response: + if response.status != 200 or response.geturl() != url: + raise ValueError('Unexpected status or response URL') + if response.headers.get_content_type() != 'application/json': + raise ValueError('Expected JSON response') + size = int(response.headers.get('Content-Length', '-1')) + if not 0 <= size <= MAX_BYTES: + raise ValueError('Missing or oversized response length') + raw = response.read(MAX_BYTES + 1) + if len(raw) != size or len(raw) > MAX_BYTES: + raise ValueError('Incomplete or oversized response body') + except urllib.error.HTTPError as error: + error.close() + raise + value = json.loads(raw, parse_constant=invalid_constant) + if (type(value) is not dict or value.get('service') != 'adaptive-inference' + or type(value.get('instance')) is not str + or re.fullmatch(r'[0-9a-f]{32}', value['instance']) is None + or type(value.get('resources')) is not dict + or type(value.get('scheduler')) is not dict + or type(value['resources'].get('admission_allowed')) is not bool): + raise ValueError('Unexpected gateway status schema') + return value + + +def observe(profile_path, run, *, activity_reader, qpc_now, clock_id, + duration_s=OUTER_SECONDS, sample_interval_s=.2, status_interval_s=1): + """Injected reader/clock and short durations support CPU-only component tests. + + The ordinary command uses WindowsActivityReader and the verified QPC clock. + This reader is independent of the controller's reader, not its policy input. + """ + profile_path = Path(profile_path).resolve(strict=True) + profile = WindowsProfile.load(profile_path) + run = Path(run) + if not run.is_absolute() or not run.parent.is_dir(): + raise ValueError('Explicit absolute run under an existing parent required') + run = run.parent.resolve(strict=True) / run.name + terminal = run / 'result.private.json' + output = Path(str(run) + '.api-trace.private.jsonl') + receipt_path = Path(str(run) + '.observer.private.json') + if any(path.exists() for path in (output, receipt_path, terminal)): + raise ValueError('Fresh observer sidecars and nonterminal run required') + url = f'http://127.0.0.1:{profile.gateway.port}/status' + opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect()) + source = Path(__file__).resolve() + reader_source = Path(inspect.getfile(type(activity_reader))).resolve(strict=True) + pins = {'observer': sha(source), 'activity_reader': sha(reader_source), 'profile': sha(profile_path)} + started = time.monotonic() + deadline = started + duration_s + next_status = started + count = valid = failures = activity_errors = 0 + previous_sample = max_gap = instance = None + terminal_exists = False + reason, fault = 'observation_deadline', None + with output.open('x', encoding='utf-8') as stream: + try: + while time.monotonic() < deadline: + stamp = time.monotonic() + gap = None if previous_sample is None else stamp - previous_sample + if gap is not None: + max_gap = max(max_gap or 0, gap) + previous_sample = stamp + row = {'reader_monotonic': stamp, 'reader_wall_time': time.time(), + 'sample_gap_s': gap, 'deadline_clock': clock_id} + # Metadata only; never parse mutable result, status or SQLite files. + terminal_exists = terminal.exists() + row['terminal_path_exists'] = terminal_exists + row['activity_before_deadline_clock'] = qpc_now() + try: + row['activity'] = activity_reader.sample() + if row['activity'].get('errors'): + activity_errors += 1 + except Exception as error: + activity_errors += 1 + row['activity_error'] = {'type': type(error).__name__, 'message': str(error)} + finally: + row['activity_after_deadline_clock'] = qpc_now() + if stamp >= next_status: + row['status_before_deadline_clock'] = qpc_now() + next_status = time.monotonic() + status_interval_s + try: + value = fetch_status(opener, url, min(2., max(.001, deadline - time.monotonic()))) + if instance is not None and value['instance'] != instance: + raise ValueError('Gateway instance changed') + instance = value['instance'] + valid += 1 + failures = 0 + row['gateway_status'] = value + except urllib.error.HTTPError as error: + reason = 'http_error' + fault = row['observation_error'] = {'type': 'HTTPError', 'status': error.code} + except (urllib.error.URLError, TimeoutError, ConnectionError) as error: + failures += 1 + underlying = error.reason if isinstance(error, urllib.error.URLError) else error + refused = (isinstance(underlying, ConnectionRefusedError) + or getattr(underlying, 'winerror', None) == 10061) + row['observation_error'] = {'type': type(error).__name__, + 'reason_type': type(underlying).__name__, 'connection_refused': refused} + row['consecutive_connection_errors'] = failures + if terminal_exists and refused and instance is not None: + reason = 'terminal_path_exists_and_connection_refused' + elif instance is not None and failures >= 3: + reason = 'three_connection_errors_after_response' + except (ValueError, TypeError, UnicodeError) as error: + reason = 'invalid_status_or_identity' + fault = row['observation_error'] = {'type': type(error).__name__, 'message': str(error)} + finally: + row['status_after_deadline_clock'] = qpc_now() + for prefix in ('activity', 'status'): + if prefix + '_before_deadline_clock' in row: + lo, hi = (row[prefix + suffix] for suffix in ('_before_deadline_clock', '_after_deadline_clock')) + if not math.isfinite(lo) or not math.isfinite(hi) or lo > hi: + reason = 'invalid_clock_observation' + fault = {'type': 'ClockOrderingError'} + stream.write(json.dumps(row, allow_nan=False) + '\n') + stream.flush() + count += 1 + if reason != 'observation_deadline': + break + # One loop, no retry burst; blocking HTTP gaps remain in the trace. + time.sleep(max(0, min(deadline - time.monotonic(), + sample_interval_s - (time.monotonic() - stamp)))) + except BaseException as error: + reason = 'observer_error' + fault = {'type': type(error).__name__, 'message': str(error)} + unchanged = pins == {'observer': sha(source), 'activity_reader': sha(reader_source), 'profile': sha(profile_path)} + receipt = {'run': str(run), 'profile': str(profile_path), 'url': url, + 'samples': count, 'valid_responses': valid, 'activity_error_samples': activity_errors, + 'elapsed_s': time.monotonic() - started, 'duration_limit_s': duration_s, + 'sample_interval_s': sample_interval_s, 'status_interval_s': status_interval_s, + 'maximum_sample_gap_s': max_gap, 'gateway_instance': instance, + 'end_reason': reason, 'terminal_path_exists': terminal_exists, + 'consecutive_connection_errors': failures, 'fault': fault, + 'source_sha256': pins, 'sources_unchanged': unchanged, + 'activity_reader_source': str(reader_source), 'deadline_clock': clock_id, + 'active_case_file_contents_read': False, 'task_completion_claimed': False, + 'qualification_claimed': False, + 'scope': 'Independent foreground samples and GET-only gateway status. Not the controller sample stream, GPU attribution, task acceptance or cleanup proof.', + 'timeout_scope': 'Two seconds per blocking HTTP operation and a bounded sampling loop; not a hard OS wall-clock deadline. CPU gaps remain unknown.'} + with receipt_path.open('x', encoding='utf-8') as stream: + stream.write(json.dumps(receipt, indent=2, allow_nan=False) + '\n') + return receipt + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--profile', type=Path, required=True) + parser.add_argument('--run', type=Path, required=True) + args = parser.parse_args(argv) + if os.name != 'nt': + raise ValueError('Windows foreground observation required') + from adaptive_inference import execution_clock + from adaptive_inference.desktop.activity import WindowsActivityReader + clock = execution_clock.verify_execution_clock() + result = observe(args.profile, args.run, activity_reader=WindowsActivityReader(), + qpc_now=execution_clock.execution_now, clock_id=clock) + print(json.dumps(result, allow_nan=False)) + return 0 if (result['end_reason'] == 'terminal_path_exists_and_connection_refused' + and result['sources_unchanged']) else 2 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/provenance/video-coexistence/observer-shutdown/predecessor/tests/test_video_adaptive_observation.py b/provenance/video-coexistence/observer-shutdown/predecessor/tests/test_video_adaptive_observation.py new file mode 100644 index 0000000..8b249da --- /dev/null +++ b/provenance/video-coexistence/observer-shutdown/predecessor/tests/test_video_adaptive_observation.py @@ -0,0 +1,221 @@ +"""Real inert child handles and fake loopback observations; no desktop sampling.""" +import hashlib +from http.server import BaseHTTPRequestHandler, HTTPServer +import importlib.util +import json +from pathlib import Path +import sys +import tempfile +import threading +import time +import unittest +import urllib.error +import urllib.request + + +ROOT = Path(__file__).resolve().parents[1] +RETAINED_JOINS = [] +OBSERVATIONS = [] + + +def load(name): + spec = importlib.util.spec_from_file_location(name, + ROOT / 'examples/validation/video_coexistence' / (name + '.py')) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +parent = load('run_adaptive') +observer = load('observe_adaptive') + + +class FakeActivity: + def __init__(self, before_sample=None): + self.before_sample = before_sample + + def sample(self): + if self.before_sample: + self.before_sample() + return {'schema': 'adaptive.foreground-sample.v2', 'monotonic_s': time.monotonic(), + 'foreground': {'hwnd': 1, 'pid': 123, 'creation_time_100ns': 456, + 'valid': True, 'cpu_cores_used': None, 'cpu_window_s': None}, + 'session_input_idle_s': 61., 'host': {'cpu_busy_fraction': None, + 'ram_available_gib': None, 'commit_available_gib': None}, 'errors': []} + + +class AdaptiveBindingTests(unittest.TestCase): + def setUp(self): + temporary = tempfile.TemporaryDirectory() + self.addCleanup(temporary.cleanup) + self.folder = Path(temporary.name).resolve() + + def profile(self, port=18100): + path = self.folder / 'deployment.json' + interpreter = str(Path(sys.executable).resolve()) + value = {'schema': 'adaptive.desktop.windows-orders.v1', + 'host_python': {'path': interpreter, 'sha256': parent.sha(interpreter)}, + 'worker_python': {'path': interpreter, 'sha256': parent.sha(interpreter)}, + 'hermes': {'source': str(self.folder), 'site_packages': str(self.folder)}, + 'native_owner': {'package': str(self.folder), 'manifest_sha256': '0' * 64, + 'provenance_sha256': '0' * 64}, + 'gateway': {'listen': {'host': '127.0.0.1', 'port': port}, + 'backend': {'kind': 'strata', 'base_url': 'http://127.0.0.1:18098/v1'}}, + 'foreground_policy': 'cooperative-owned-foreground.v1'} + path.write_text(json.dumps(value), encoding='utf-8') + return path + + def server(self, responses, delay=0): + calls = [] + class Handler(BaseHTTPRequestHandler): + def do_GET(self): + calls.append((self.command, self.path)) + code, headers, raw = responses[min(len(calls) - 1, len(responses) - 1)] + if delay: + time.sleep(delay) + self.send_response(code) + for name, value in headers.items(): + self.send_header(name, value) + self.end_headers() + self.wfile.write(raw) + + def log_message(self, *args): + pass + + server = HTTPServer(('127.0.0.1', 0), Handler) + thread = threading.Thread(target=lambda: server.serve_forever(poll_interval=.01), + name='fake-adaptive-status') + thread.start() + closed = False + def close(): + nonlocal closed + if closed: + return + server.shutdown() + server.server_close() + thread.join(30) + joined = not thread.is_alive() + RETAINED_JOINS.append({'kind': 'fake_http_server', 'thread_ident': thread.ident, + 'joined': joined}) + self.assertTrue(joined, 'Retained fake status server must join') + closed = True + self.addCleanup(close) + return server.server_port, calls, close + + @staticmethod + def status(instance='a' * 32): + raw = json.dumps({'service': 'adaptive-inference', 'instance': instance, + 'resources': {'admission_allowed': False}, 'scheduler': {}}).encode() + return 200, {'Content-Type': 'application/json', 'Content-Length': str(len(raw))}, raw + + def observe(self, profile, run, reader, **options): + settings = {'duration_s': 3, 'sample_interval_s': .01, 'status_interval_s': .03} + settings.update(options) + result = observer.observe(profile, run, activity_reader=reader, + qpc_now=time.perf_counter, clock_id='injected-component-clock', + **settings) + rows = [json.loads(line) for line in Path(str(run) + '.api-trace.private.jsonl').read_text().splitlines()] + OBSERVATIONS.append({'receipt': result, 'rows': rows}) + return result + + def test_preparation_preserves_installed_command_task_and_input_identity(self): + profile = self.profile() + task = ROOT / 'examples/tasks/inventory/task.json' + inputs = task.parent / 'inputs' + run = self.folder / 'fresh-run' + command, returned, identity = parent.prepare(profile, run, task, inputs) + self.assertEqual(command, [str(Path(sys.executable).resolve()), '-I', '-B', '-m', + 'adaptive_inference', 'desktop', '--profile', str(profile), '--run', str(run), + '--task-spec', str(task), '--task-inputs', str(inputs)]) + self.assertEqual(returned, run) + self.assertEqual(identity['task_budget']['execution_seconds'], 900) + self.assertEqual(identity['input_sha256']['inventory.csv'], + hashlib.sha256((inputs / 'inventory.csv').read_bytes()).hexdigest()) + self.assertEqual(parent.OUTER_SECONDS, 2700) + self.assertFalse(run.exists()) + changed = json.loads(task.read_text(encoding='utf-8')) + changed['budget']['execution_seconds'] = 899 + wrong = self.folder / 'wrong-task.json' + wrong.write_text(json.dumps(changed), encoding='utf-8') + with self.assertRaisesRegex(ValueError, 'Unsupported task contract or execution budget'): + parent.prepare(profile, run, wrong, inputs) + + def test_actual_inert_children_retain_exit_and_emergency_join(self): + for name, code, timeout, expected, emergency in ( + ('normal', 'print("inert child")', 30, 0, False), + ('failed', 'raise SystemExit(7)', 30, 7, False), + ('timeout', 'import time; time.sleep(60)', .1, None, True), + ): + with self.subTest(name=name): + run = self.folder / name + result = parent.supervise([sys.executable, '-I', '-B', '-c', code], + run, {'fixture': 'inert Python only'}, timeout_s=timeout) + RETAINED_JOINS.append({'kind': 'inert_child', **result}) + self.assertTrue(result['retained_case_handle_exited']) + self.assertIsInstance(result['exit_code'], int) + self.assertEqual(result['emergency_termination_attempted'], emergency) + self.assertEqual(result['timed_out'], emergency) + self.assertFalse(result['qualification_claimed']) + self.assertEqual(result['normal_parent_exit'], name == 'normal') + if expected is not None: + self.assertEqual(result['exit_code'], expected) + self.assertEqual(json.loads(Path(str(run) + '.supervisor.private.json').read_text()), result) + with self.assertRaisesRegex(ValueError, 'Fresh'): + parent.supervise([sys.executable, '-c', 'raise SystemExit(99)'], run, {}) + + def test_observation_keeps_unknown_cpu_gaps_and_never_parses_terminal(self): + port, calls, close = self.server([self.status()], delay=.05) + profile = self.profile(port) + run = self.folder / 'observed' + def finish_after_response(): + if calls: + close() + run.mkdir(exist_ok=True) + # Deliberately not JSON: existence is the only active case read. + (run / 'result.private.json').write_text('unread terminal marker') + result = self.observe(profile, run, FakeActivity(finish_after_response), + duration_s=1, status_interval_s=10) + self.assertEqual(result['end_reason'], 'observation_deadline') + self.assertTrue(result['terminal_path_exists']) + self.assertEqual(result['valid_responses'], 1) + self.assertFalse(result['active_case_file_contents_read']) + self.assertFalse(result['task_completion_claimed']) + self.assertTrue(result['sources_unchanged']) + rows = [json.loads(line) for line in Path(str(run) + '.api-trace.private.jsonl').read_text().splitlines()] + self.assertGreater(len(rows), 1) + self.assertEqual(result['maximum_sample_gap_s'], + max(row['sample_gap_s'] for row in rows if row['sample_gap_s'] is not None)) + self.assertTrue(all(row['activity']['foreground']['cpu_cores_used'] is None for row in rows)) + self.assertTrue(all(row['activity']['session_input_idle_s'] == 61 for row in rows)) + for row in rows: + for prefix in ('activity', 'status'): + if prefix + '_before_deadline_clock' in row: + self.assertLessEqual(row[prefix + '_before_deadline_clock'], + row[prefix + '_after_deadline_clock']) + self.assertEqual(calls, [('GET', '/status')]) + + def test_changed_gateway_instance_is_not_combined_into_one_run(self): + port, calls, _ = self.server([self.status(), self.status('b' * 32)]) + result = self.observe(self.profile(port), self.folder / 'changed', FakeActivity()) + self.assertEqual(result['end_reason'], 'invalid_status_or_identity') + self.assertEqual(result['gateway_instance'], 'a' * 32) + self.assertEqual(result['valid_responses'], 1) + self.assertFalse(result['task_completion_claimed']) + self.assertEqual(calls, [('GET', '/status'), ('GET', '/status')]) + + def test_redirect_and_oversize_are_refused_without_followup(self): + for response, expected in ( + ((302, {'Location': '/other', 'Content-Length': '0'}, b''), urllib.error.HTTPError), + ((200, {'Content-Type': 'application/json', 'Content-Length': str(observer.MAX_BYTES + 1)}, b''), ValueError), + ): + with self.subTest(response=response[0]): + port, calls, close = self.server([response]) + opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), observer.NoRedirect()) + with self.assertRaises(expected): + observer.fetch_status(opener, f'http://127.0.0.1:{port}/status', 2) + self.assertEqual(calls, [('GET', '/status')]) + close() + + +if __name__ == '__main__': + unittest.main() diff --git a/provenance/video-coexistence/observer-shutdown/record.json b/provenance/video-coexistence/observer-shutdown/record.json new file mode 100644 index 0000000..3f44003 --- /dev/null +++ b/provenance/video-coexistence/observer-shutdown/record.json @@ -0,0 +1,132 @@ +{ + "date": "2026-11-09", + "status": "Source/component correction independently reviewed; physical workflow qualification remains open", + "scope": "GET-only observer shutdown classification; source/component and owned loopback evidence, not physical qualification", + "base_revision": "f362ed0ed8c0f9f6552bd71c9c3ba9e8a847aa3a", + "source_binding": { + "scope": "Frozen four-file candidate before integration onto newer canonical documentation", + "observer_sha256": "224af2ec6ff166ded5c0f04a972edf1d2b8436b782ca38d2ba8857933d47c395", + "final_test_sha256": "7a4e9779ee61cf0bee30482bc49f94cfc002bceef5c6bc40473a805392a843cd", + "guide_sha256": "7f168b8ab3a4d03397bc9a87cbb59d2c0b306e2f75bc6464fdc7ba3e618f93b4", + "bibliography_sha256": "f3069355a3d64b47b7c0aba2f6ee5818080093ee46e67a94de9e0d299931beab", + "historical_observer_sha256": "668b2f1f2f917d2629265e02e8a4b11b022cc2babd0635a4783c75e375dfcff2" + }, + "change": { + "normal_blocking_operation_timeout_s": 2, + "known_terminal_gateway_blocking_operation_timeout_s": 5, + "longer_bound_requires": [ + "A gateway instance previously accepted by the existing status parser", + "Terminal-result existence observed before the request" + ], + "remaining_outer_allowance_clamps_selected_timeout": true, + "no_request_starts_with_exhausted_allowance": true, + "actual_refusal_predicate_unchanged": true, + "existing_three_connection_error_policy_unchanged": true, + "additional_retry_loop": false, + "terminal_contents_read": false, + "new_error_fields": [ + "underlying errno", + "underlying Windows error number" + ] + }, + "diagnostic": { + "environment": "Windows, Python 3.11.9; owned disposable loopback sockets only", + "cases": 9, + "valid_response_cases": 1, + "closed_listener_cases_at_two_seconds": { + "count": 3, + "outcome": "URLError wrapping TimeoutError during connect; not classified as refusal" + }, + "closed_listener_cases_at_five_seconds": { + "count": 3, + "outcome": "URLError wrapping actual ConnectionRefusedError, WinError 10061, during connect", + "elapsed_range_s": [ + 2.0, + 2.032 + ] + }, + "accepted_but_stalled_response_cases": { + "count": 2, + "bounds_s": [ + 2, + 5 + ], + "outcome": "Response-header TimeoutError at both bounds; neither classified as closure" + }, + "all_owned_sockets_closed": true, + "all_owned_threads_joined": true, + "supervisor_exit_code": 0, + "timed_out": false, + "result_sha256": "2044b3a77af0013e32186f6fdc08afff752b15166a6011d35b2ad33b1736cf1f", + "supervisor_sha256": "39930eaf51b5dd1ce14a9da3b50ace5b17b7ca60729e096656e9c1bca913478e", + "cleanup_sha256": "9dd6a23baa652d6def18eea4e3601d07f3b974dea470a8be911df3434fa5765a" + }, + "validation": { + "initial_component_suite": { + "tests": 10, + "failures": 0, + "errors": 0, + "skips": 0, + "unittest_elapsed_s": 4.013, + "test_source_sha256": "5ce1d6933c6771714fa161ce947a97a1aed46e650747ddfcbec7fa5c639e2f57", + "result_sha256": "90f3008b31eb8e044b5dd3bf12a59cd72d561d77a2c005336ef786ce63591fe0", + "log_sha256": "92ec11636f5d3cf336e2c0fb8d51e6d45e71a874d8511e7873ca7bf8934754ba", + "includes_actual_owned_listener_closure": true + }, + "deterministic_clock_followup": { + "reason": "Remove a real-time scheduling premise from the unknown-instance prerequisite test and reuse its logical clock for deadline clamping", + "tests": 2, + "failures": 0, + "errors": 0, + "skips": 0, + "unittest_elapsed_s": 0.047, + "result_sha256": "f2af10d9120caa4ce0f53813ff29022dc025779ba71fa35677c2b73ce9fa3312", + "log_sha256": "52ebe4ff7549d4cf2908b73a7b1bdeb51dd3edc01328dcc6677f2b68400a7f5c", + "observer_guide_bibliography_unchanged": true, + "actual_socket_test_rerun": false + }, + "interpretation": "Ten component tests followed by two affected-test checks, not twelve distinct tests or a final full-suite rerun", + "meaningful_checks": [ + "Actual listener refusal after checked identity and terminal existence", + "Wrapped timeout, response timeout and reset remain failure", + "Each missing prerequisite keeps the normal timeout and cannot establish closure", + "Terminal timeout is clamped to the original remaining allowance", + "Existing changed-instance, redirect, size, existence-only and owned-child checks remain" + ], + "reviewer_reran_tests": false + }, + "historical_evidence": { + "automatic_physical_03_observer_exit_code": 2, + "automatic_physical_03_end_reason": "three_connection_errors_after_response", + "original_failure_preserved": true, + "historical_physical_attempt_replayed": false + }, + "limits": [ + "The loopback diagnostic reproduces the suspected Windows behavior; it does not reconstruct unrecorded historical socket phases.", + "The timeout bounds each blocking HTTP operation, not an absolute wall-clock deadline across connection, response and body operations.", + "A timeout, reset, terminal existence alone or accepted task result cannot establish observer closure.", + "No runtime task allowance, inference quality contract, TCP setting, firewall setting, dependency or physical qualification threshold changes.", + "A new physical observation remains necessary before claiming this candidate is qualified in the full IQ3_S foreground workflow." + ], + "references": [ + "https://github.com/python/cpython/issues/85480", + "https://docs.python.org/3/library/urllib.error.html", + "https://docs.python.org/3/library/urllib.request.html" + ], + "retained_predecessors": { + "examples/validation/video_coexistence/observe_adaptive.py": { + "path": "provenance/video-coexistence/observer-shutdown/predecessor/examples/validation/video_coexistence/observe_adaptive.py", + "sha256": "668b2f1f2f917d2629265e02e8a4b11b022cc2babd0635a4783c75e375dfcff2" + }, + "tests/test_video_adaptive_observation.py": { + "path": "provenance/video-coexistence/observer-shutdown/predecessor/tests/test_video_adaptive_observation.py", + "sha256": "7c26abb6b121bc280dd88e2a82f1e481225bcbe0127b16daf8a02e86bf57a5b2" + } + }, + "integration": { + "source_commit_before_evidence_publication": "7e7c20e7913f6d10d15be30cb211c78c84068b93", + "parent_roadmap_revision": "a83844b5fed20bf01dcfd61d4061b00961b787cc", + "observer_and_final_test_bytes_unchanged": true, + "bibliography_addition": "Parent roadmap adds separate agent engineering references; the recorded pre-integration bibliography identity is historical." + } +} From 3a05cac72065562a796acb85acdd19f0c304d7c8 Mon Sep 17 00:00:00 2001 From: Midhat Nashar <35053211+midhatn@users.noreply.github.com> Date: Mon, 9 Nov 2026 07:43:46 +0300 Subject: [PATCH 3/3] Retain validated roadmap integration in canonical evidence --- CHANGELOG.md | 6 + HANDOFF.md | 6 + PLAN.md | 6 + docs/evidence.md | 10 ++ provenance/ci-fixtures/2026-11-09/record.json | 113 ++++++++++++++++++ 5 files changed, 141 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a81db8..dafd3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,12 @@ These changes belong to the development source. The published `0.4.0a1` tag and The automatic-recovery source passed the complete **1,595-test local core suite** and all **16 additional workflow commands**, with the earlier failed test-double attempt retained. Source checks, installed SDK fixtures and physical IQ3_S evidence remain separately identified in the [evidence ledger](docs/evidence.md) and [local validation record](provenance/automatic-tool-recovery/local-validation-2026-11-08.json). These results do not complete M3, M4, M5, daily-use qualification or the full Adaptive-Hermes-Media-Autonomous Test. +The corrected CI-fixture/roadmap successor subsequently passed all **14 original +hosted jobs**, including **12 full-core and 12 distribution checks**, and merged +as [PR #40](https://github.com/midhatn/adaptive-inference/pull/40). Its +[record](provenance/ci-fixtures/2026-11-09/record.json) preserves the successful +successor and the earlier failed or incomplete attempts separately. + ## 0.4.0a1 — supported Windows reference alpha (2026-11-01) This prerelease packages the supported Windows/IQ3_S/Hermes reference, ordinary diff --git a/HANDOFF.md b/HANDOFF.md index 606acde..fe901cd 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -63,6 +63,12 @@ passing across direct/aliased callers and historical-source assertions. Use the exact current PR checks before integrating; historical or local passes do not override a failed required check. Main branch protection is enabled. +[PR #40](https://github.com/midhatn/adaptive-inference/pull/40) has now merged +at `d33c979e1047f9c125ab94788fcbb2b3d8ef6c12` with its exact reviewed tree. +All 14 original hosted jobs passed, including 12 full-core and 12 distribution +checks; the [canonical record](provenance/ci-fixtures/2026-11-09/record.json) +retains those outcomes without changing earlier failures or qualification scope. + **Latest bounded M4 gate — 9 November 2026:** the [installed active-compiler interruption case](provenance/compiler/installed-active-tool-interruption-2026-11-09.json) passed 61 independent closed-evidence checks. One actual Hermes SDK worker diff --git a/PLAN.md b/PLAN.md index 5276265..b4659db 100644 --- a/PLAN.md +++ b/PLAN.md @@ -75,6 +75,12 @@ passed 49 closed-audit checks at the exact-known-result boundary described in the [evidence ledger](docs/evidence.md#physical-iq3_s-foreground-recovery). That result does not complete M4 or qualify successful computer-reboot recovery. +The roadmap/engineering-practices and CI-fixture follow-up +[PR #40](https://github.com/midhatn/adaptive-inference/pull/40) is also merged, +at `d33c979e1047f9c125ab94788fcbb2b3d8ef6c12`, after all 14 original hosted +jobs passed. Its [retained validation](provenance/ci-fixtures/2026-11-09/record.json) +preserves earlier failures. All 77 production runtime modules remain unchanged. + **Latest bounded gate — 9 November 2026:** an installed Hermes SDK worker interrupted an actual compiler test, retained its feedback and completed a distinct successful compiler call within the same task and original budget. diff --git a/docs/evidence.md b/docs/evidence.md index 315d66b..017be9d 100644 --- a/docs/evidence.md +++ b/docs/evidence.md @@ -73,6 +73,16 @@ and the original compiler source assertion. The retained test child joined normally, with no timeout or source change during the check. Production behavior and prior failed outcomes are unchanged; the new head still requires full CI. +The corrected successor `142a838de9cd8f41a1f027c13578301f11077bb7` +subsequently passed both original hosted workflows: all 14 jobs, 12 full-core +checks, 12 distribution checks and both offline/source-delta checks passed. +[PR #40](https://github.com/midhatn/adaptive-inference/pull/40) merged through +the protected branch at `d33c979e1047f9c125ab94788fcbb2b3d8ef6c12`, with +exactly the reviewed tree `c18247124cadbf9b3503467a37a569c9370daa6d`. +The correction record retains the successful run/job identities and independent +terminal-audit hashes alongside every earlier failure. This is source/package +validation, with no new installed, model or physical qualification. + ## Installed active-tool interruption and observer correction — 9 November 2026 The [installed SDK/compiler record](../provenance/compiler/installed-active-tool-interruption-2026-11-09.json) diff --git a/provenance/ci-fixtures/2026-11-09/record.json b/provenance/ci-fixtures/2026-11-09/record.json index fcba1ad..8aeac8a 100644 --- a/provenance/ci-fixtures/2026-11-09/record.json +++ b/provenance/ci-fixtures/2026-11-09/record.json @@ -127,6 +127,119 @@ }, "reference": "https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces" }, + "hosted_successor_validation": { + "date": "2026-11-09", + "head": "142a838de9cd8f41a1f027c13578301f11077bb7", + "conclusion": "success", + "scope": "Exact-source hosted CI and distribution checks; no new installed, model, physical or reboot qualification", + "actions_app_id": 15368, + "original_attempts_only": true, + "jobs_passed": 14, + "core_steps_passed": 12, + "distribution_steps_passed": 12, + "offline_steps_passed": 2, + "source_delta_steps_passed": 2, + "workflows": [ + { + "id": 42560726971, + "event": "push", + "attempt": 1, + "url": "https://github.com/midhatn/adaptive-inference/actions/runs/42560726971", + "conclusion": "success", + "jobs": [ + { + "id": 128976344376, + "name": "offline-source", + "conclusion": "success" + }, + { + "id": 128976344579, + "name": "portable (ubuntu-latest, 3.11)", + "conclusion": "success" + }, + { + "id": 128976344580, + "name": "portable (ubuntu-latest, 3.12)", + "conclusion": "success" + }, + { + "id": 128976344602, + "name": "portable (windows-latest, 3.12)", + "conclusion": "success" + }, + { + "id": 128976344659, + "name": "portable (macos-latest, 3.12)", + "conclusion": "success" + }, + { + "id": 128976344676, + "name": "portable (macos-latest, 3.11)", + "conclusion": "success" + }, + { + "id": 128976344728, + "name": "portable (windows-latest, 3.11)", + "conclusion": "success" + } + ] + }, + { + "id": 42560730165, + "event": "pull_request", + "attempt": 1, + "url": "https://github.com/midhatn/adaptive-inference/actions/runs/42560730165", + "conclusion": "success", + "jobs": [ + { + "id": 128976354376, + "name": "offline-source", + "conclusion": "success" + }, + { + "id": 128976354531, + "name": "portable (ubuntu-latest, 3.12)", + "conclusion": "success" + }, + { + "id": 128976354549, + "name": "portable (windows-latest, 3.11)", + "conclusion": "success" + }, + { + "id": 128976354617, + "name": "portable (ubuntu-latest, 3.11)", + "conclusion": "success" + }, + { + "id": 128976354642, + "name": "portable (macos-latest, 3.12)", + "conclusion": "success" + }, + { + "id": 128976354652, + "name": "portable (macos-latest, 3.11)", + "conclusion": "success" + }, + { + "id": 128976354703, + "name": "portable (windows-latest, 3.12)", + "conclusion": "success" + } + ] + } + ], + "ordinary_protected_merge": { + "pr": 40, + "commit": "d33c979e1047f9c125ab94788fcbb2b3d8ef6c12", + "tree": "c18247124cadbf9b3503467a37a569c9370daa6d", + "tree_matches_reviewed_head": true, + "admin_bypass": false + }, + "terminal_receipt_sha256": "f266eec17b6665a46ec8da256d3da683119516ab96d2465612d2fa8423da2faf", + "independent_terminal_audit_sha256": "940ccc7653b1a9986226b97cbefccff4bd0350fb6561947797ed53f525378b9e", + "earlier_failed_runs_and_incomplete_local_sweep_unchanged": true + }, "research": [ { "url": "https://github.com/python/cpython/blob/v3.12.10/Lib/test/support/__init__.py",