SetupEngine.IsInstalled is computed but never reaches the wizard UI, so the
first-install flow repeats verbatim on a machine that already has Hub. Adds the
reinstall screen, the mirror semantics it triggers, the list of user data that
must survive it, and a /reinstall silent flag.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The owner used the product and the router proved itself: the log shows a real
failover chain codex-orch -> ag-orch-fallback -> opengo-3 driven by actual quota
errors. Everything around that core failed.
Hub stopped launching because customtkinter vanished from the Hermes venv, most
likely wiped by that app's repair-install; pythonw swallows the traceback, so it
looks like the window never opens. The wizard offers five providers while the
router config defines profiles for three, so find_free_slot returns a
non-existent claude-orch. The test action still reaches the agy subprocess, which
opens a browser on expired credentials. The routing "configure" button only
raises a toast about drag-and-drop.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adopts the graph half of the 104-point n8n-derived brief and states plainly what
is excluded: n8n's canvas edits its own workflow execution engine, while Hermes
Hub's engine resolves a role into a provider/account/model for a single call.
Execution trees, agent-as-tool contracts, cancellation and review loops have no
layer to sit on here.
Adds the requirement that has been missing all along: screenshots taken with a
live connected account, not an empty configuration.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
active_calls is permanently zero: state_store reads LeaseManager.get() while
RouterEngine constructs its own instance, so the router's leases are invisible
to the snapshot. And psutil.cpu_percent(interval=None) returns 0.0 on its first
call, so the dashboard shows CPU 0% at startup — a measured-looking number that
is not a measurement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The mockup's headline blocks divide three ways against the code at 2b2ccd8:
already available (latency, readiness, events, routing chain), derivable but not
yet exposed (per-provider aggregates, call share, host metrics via the unused
psutil dependency, active leases), and absent by design (task queues, service
window, incidents, provider RPS/SLA).
A6 supplies the second group; B5 redesigns against the contract and restores the
four account actions that d0d15ae dropped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Latency, token usage and failover counts flow through router_metadata on every
call and are then discarded; nothing in the project accumulates them. Four of
the seven headline numbers on the mockups are therefore honestly derivable from
our own calls rather than fabricated. RPS, SLA and host resource metrics stay in
Active Limitations.
Also carries the three debts forward for the third time: HKCU in the installer,
fastapi/uvicorn as required dependencies, and YAML comment loss.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
accounts_view went from four user actions to one in d0d15ae: test, set_main,
set_orchestrator and assign_role lost their triggers, and assign_role is now
unreachable entirely although its handler and modal still exist. Also scopes
what the approved mockups can and cannot drive, since their headline numbers
are exactly the metrics Gap 12 says the backend does not measure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The agy subprocess still inherits the entire environment, so every provider key
travels into it; _safe_env() was written for exactly this and has never been
called. That is the oldest unfixed finding in the project.
Also scopes out the "AI news" proposal: lifecycle supervisor, skill registry and
MCP lifecycle target modules this repo deleted as dead code or never had, so
only credential isolation and the provider explain trace are adopted from it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex built phases 2-6 against contract v1.0 while Antigravity shipped v1.1, so
eight fields now exist in the snapshot and render nowhere: plan_code,
plan_source, active_quota_status, active_quota_label, quota_status,
failover_reason, unavailable_reason and seq. PlanBadge was built in phase 1 and
still has nothing to display.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The redesigned views are pure renderers: update_data returns early instead of
falling back to HubStateStore. That made the guard check skip for all nine
views, so the invariant silently stopped protecting the crash it was written
for. The store-access precondition is dropped; the isinstance guard is asserted
either way.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A2 closed the gaps that blocked the UI, but three declared gaps (4, 5, 12)
vanished from the contract without being closed. Gap 12 was the instruction
telling the UI to render N/A for latency, RPS and cost — it disappeared exactly
as Codex starts the dashboard. The release asset still returns 404.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seven of the twelve backend gaps in UI_STATE_CONTRACT.md block the UI outright:
no targeted event has a publisher, plan provenance never reaches
ProfileViewModel, and AgentViewModel/PipelineNode lack the fields the Team and
Routing screens need. Verified on c6876e9, alongside the release asset still
returning 404 and YAML comment loss now at 5 to 2 rather than 5 to 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex was handed Task A by mistake and duplicated the state-layer work that
Antigravity had already merged. This reissues its own scope, rebased on the
facts at 39b1f92: PHASE 1 is merged, docs/UI_STATE_CONTRACT.md now exists, and
the twelve backend gaps it declares are mapped to what the UI may and may not
render.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0d9005f changed start_profile_oauth to return (session_id, auth_url, port) and
updated the tests, but not its callers. The wizard still unpacked two values, so
every Antigravity connection raised ValueError inside _init_antigravity_oauth,
the handler swallowed it, and the authorization URL never appeared — the primary
onboarding flow has been dead since that commit.
Also stores the listener port as wizard.oauth_port, which the single-session
invariance test reads to prove that repeat "open browser" clicks reuse the
existing listener instead of binding a new one.
Both defects were caught only by tests that the headless run skips, which is why
they survived several reviews.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Added HubStateStore targeted delta update methods (apply_delta_quota_updated, apply_delta_account_added, apply_delta_account_removed, apply_delta_route_changed)
- Added seq sequence freshness tracking in HubStateStore to drop stale out-of-order responses
- Added trigger_refresh_provider in HermesRefreshScheduler
- Bound multi-bucket quotas to specific model families (Claude vs Gemini) with truthful is_estimated tracking
- Connected OAuth completion to targeted account added events across all providers
- Pinned antigravity_provider package root to repo via __init__.py and added import invariant verification
- Added unit tests in tests/test_state_layer_and_event_driven_quota.py and tests/test_import_invariants.py
- Zero modifications to UI zone files (views, components, theme, wizard, hermes_hub_app.py)
The headless guard matched any test whose name contained "ui", "view" or
"wizard", so static checks that never touch the toolkit were skipped too. That
is how four real failures stayed invisible: they lived in modules the name
filter silently removed. Selection is now the `ui` marker alone; modules that
import the GUI stack already guard themselves with pytest.importorskip, which
tests/test_import_invariants.py enforces.
Headless goes from 152 to 156 passed — four tests that were being skipped by
accident now actually run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Running the suite with customtkinter present exposes four failures the headless
run never reaches: hermes_hub_app mutates sys.path so tests load the stale
installed plugin copy, and two OAuth lifecycle tests fail outright. conftest's
name-based skip hid both.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PHASE 1 of the UI redesign. Adds semantic colour roles that keep brand gold out
of health states, layout and typography aliases so views stop hand-rolling
padding, and the reusable widgets the redesign depends on: PlanBadge, QuotaBar,
QuotaBucketWidget (stable key plus in-place update), SearchField, FilterButton,
ActionButton, IconButton, EmptyState, RouteTargetWidget, AccountCardWidget,
AgentCardWidget, ConfirmDialog, Toast, plus ellipsize/tooltip helpers for long
account identities.
test_unknown_quota_is_supported_explicitly compared inspect's resolved
annotation object against source strings, so it could only pass while being
skipped; components.py has no `from __future__ import annotations`. Rewritten
to assert through typing.get_args that QuotaBar.set_value accepts None.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Launching the app died with "'dict' object has no attribute 'readiness'".
TeamView.__init__ forwarded its legacy app_state dict into update_data(snapshot),
whose guard only handled None, so {} reached snapshot.readiness. TeamView is the
default view, so the window never appeared.
Fixes the call site and hardens every snapshot guard to fall back on anything
that is not a HubSnapshot. Adds tests/test_view_startup_contract.py, which
checks both conditions statically and therefore runs headless; verified to fail
on the pre-fix sources.
The GUI-import invariant previously matched the literal string "customtkinter",
which missed modules pulling it transitively. Rewritten over the AST, it
immediately found test_codex_opencode_wizard.py importing router.ui.components
without pytest.importorskip — the same defect that took the release gate down
once before, hidden until now behind conftest's name-based skip.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The UI redesign draft duplicated eight Plan A phases on the same files
(accounts_view, routing_view, hermes_hub_app, unified_health), which would have
put two agents into the same merge conflicts. Ownership is now split by file
path, with docs/UI_STATE_CONTRACT.md as the interface between them.
Four draft items were already implemented at 50fde5f and are marked as such
rather than reassigned: OAuth URL copy-before-open, keyed account card reuse,
the no-fake-metrics rule, and the importorskip guard.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Implemented 3-tier release feed status (MANIFEST_LIVE, PACKAGE_LIVE, PACKAGE_HASH_VERIFIED)
- Added reproducible package and checksum builder in scripts/build_dist.py
- Preserved user header comments across YAML saves in router_config.py
- Connected model_timeout_seconds, monitoring_interval_seconds, and auto_monitoring to runtime
- Guarded global gemini:antigravity credential swap with _AGY_INVOCATION_LOCK to eliminate concurrent subprocess race
- Added concurrency regression test in tests/test_antigravity_concurrency.py
- Added interprocess file locking (_FileLock) for router_state.json in health_tracker.py
- Sandboxed APPDATA and USERPROFILE in tests/test_installer.py
- Exported roadmap modules in router/__init__.py
- Verified 151 passing tests (100%) and 7/7 release gate checks
Every Round 4 finding re-checked against HEAD 63c0385 instead of being carried
forward as still-open. Four are already fixed (customtkinter collection guard,
_CM_LOCK scope, gemini:antigravity restore, session affinity TTL), two are
partial, one is obsolete (web stack moved to legacy/), and five remain open.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ruff check .` failed on every push since CI was added (1221 findings), so the
job aborted before pytest and the release gate ever ran. Adds [tool.ruff]
selecting bug-catching rules only (E9, F63, F7, F82, F811); style and
modernization rules stay off until their ~1200 findings are burned down.
That selection immediately surfaced seven live defects:
- hermes_hub_app: HubModal used but never imported, so the "Назначить" role
modal raised NameError — the handler added to close an earlier review
finding could never open.
- hermes_hub_app: three `after(0, lambda: ...(e))` callbacks referencing the
except-bound name, which Python unbinds at block exit, so every UI error
path raised NameError instead of reporting the error.
- auto_assigner: build_team_hierarchy referenced an undefined `is_main` and
crashed on every call.
- ui/assets and live_provision_and_validate: annotations naming Any/Tuple
without importing them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Full audit of the project (93 findings) plus the review record for rounds 1-5:
P0 release blockers, 65482e8, 42dfe2c, the v0.1.1 candidate verdict, and the
review of the previously unreviewed OAuth/wizard/quota work. Includes the
remediation task with acceptance criteria that 42eddb3 was verified against.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
deepseek_adapter.py had never been importable: it referenced ProviderAdapter
and ProfileConfig, neither of which exists in src/, and profile.extra, which
RouterProfileConfig does not define. Rewritten against BaseProviderAdapter with
the three missing abstract methods and registered as "deepseek".
Adds tests/test_import_invariants.py, which walks every shipped module and
fails on broken internal references while skipping absent optional GUI extras.
It also asserts that test modules importing customtkinter call
pytest.importorskip, since a missing guard aborts collection of the whole
session and takes the release gate with it.
Adds a headless CI job that uninstalls customtkinter and runs the suite, so
that invariant is enforced rather than remembered.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>