Skip to content

feat(sync-health): sync health on fleet health, the fleet audit over MCP, and the agent card (trinity-enterprise#707) - #3036

Merged
vybe merged 28 commits into
devfrom
AndriiPasternak31/ent707
Sep 30, 2026
Merged

vybe merged 28 commits into
devfrom
AndriiPasternak31/ent707

Conversation

@AndriiPasternak31

@AndriiPasternak31 AndriiPasternak31 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Sync health on the surfaces people and orchestrators actually read: fleet health, the fleet audit over MCP, and the agent card

Fixes abilityai/trinity-enterprise#707 (PR-B of two).

Base: dev. #3035 (trinity-enterprise#706) has merged into dev (squash 9690bc89f), so this PR is no longer stacked; the diff against dev is ent#707 only (29 files: fleet health + get_fleet_health sync block, the fleet sync audit + get_fleet_sync_audit, the agent card / Overview sync chip, docs, tests). dev was merged in at d64ba2332 (via #3035's final head 6f4ef993b first, so its squash resolves as a true 3-way merge — no #3035 or #2996 content is reverted).

Problem

#706 made the backend decide sync health, but the surfaces an operator or an orchestrator looks at still didn't show it:

So the 2026-09-24 audit (49 agents with auto-sync off, 40 unpushed commits, 791 dirty files) had to be assembled by hand inside containers.

What changes

Read path only. No schema, poller, scheduler or base-image change.

  • Fleet health (GET /api/monitoring/status, MCP get_fleet_health)
    • Each agent gets a sync block: binding, auto_sync_enabled, ahead, behind (origin on the agent's own branch), dirty_files, last_successful_push_at, divergence_age_s, state, reason, recommendation and frozen. An agent with no git binding gets sync: null.
    • Fleet totals go in sync_summary: {git_bound, diverged, frozen, auto_sync_off, dirty, red, yellow}.
    • A red agent gains one issues[] entry: sync: <reason> — <recommendation>.
    • Sync is an annotation, as the heartbeat is. It never changes status, the sort, or the healthy/degraded counts (D11).
    • The sync data is computed first and applied only if that succeeds. A fault degrades to sync: null / sync_summary: null, never to the "aggregation failed" payload.
    • One query, the shared reader from feat(sync-health): divergence age is the signal — red and frozen at >24h, persisted on the row (trinity-enterprise#706) #3035.
  • Fleet sync audit (GET /api/fleet/sync-audit)
  • MCP (Invariant feat: SMARTS trading pipeline with Telegram notifications and Miro visualization #13, all three surfaces in sync)
    • New tool get_fleet_sync_audit, which returns the audit unchanged (client.ts, tools/monitoring.ts, and the access.ts policy row).
    • get_fleet_health passes sync, sync_summary and the issues strings through, next to the kept issues_count.
    • routers/fleet.py's header is now # mcp: monitoring.ts (get_fleet_sync_audit).
  • Access (D12). The REST gate is unchanged: accessible_agent_names, so admins see all and others see their owned and shared agents. An agent-scoped key resolves to its owner and sees the owner's set, because an orchestrator's sweep runs as an agent. The policy row is none (no agent target), the same as get_fleet_health.
  • No raw error text. sync_view now computes reason / freeze_reason with the agent-written last_error_summary withheld, while state, freeze and the recommendation still read it (a denied push still says credential is read-only). This settles the question feat(sync-health): divergence age is the signal — red and frozen at >24h, persisted on the row (trinity-enterprise#706) #3035's body left to bug: workspace canvas panel flickers + charts broken in update_panel #707 (D12), for every backend surface: fleet health, the audit, the dashboard batch, /git/sync-state and feat(sync-health): divergence age is the signal — red and frozen at >24h, persisted on the row (trinity-enterprise#706) #3035's sync_diverged item question. The raw text stays only in the explicit last_error_summary field of the two per-agent reads it was already on.
  • Agent card
    • Fleet tile (AgentTile.vue): one sync chip replaces sync failing ×N and git ✓, so the strip doesn't grow.
    • Agent Detail → Overview: it keeps the whole /git/sync-state payload and renders the same line in the state colour.
      • It shows — when there is no observation or the read failed. It used to say ok for every agent that hadn't failed a push, including agents with no git binding.
      • The attention count still counts failed syncs (D13).
    • The formatter is utils/syncSummary.js: display only, with now passed in.

Behaviour changes to know before merging

  • The grid tile now shows a sync chip for every observed git-bound agent (red / yellow / green), not only after a failed push.
  • The Overview footprint says Sync: — instead of Sync: ok for an agent with no observation or no git binding.
  • A failed sync's reason on the dashboard dot tooltip and /git/sync-state no longer includes the first 120 characters of the git error (e.g. last sync failed (seen on 3 polls)). The recommendation still names the fix. The error itself is still in last_error_summary on those per-agent reads.
  • Not changed: the scheduler's skip_reason for a sync_failing freeze still carries the error excerpt. The scheduler calls the vendored policy directly and is out of this read-only PR's scope. This is recorded under Known Limitations.

Evidence

Check Result
Tests written failing first test_ent707_fleet_health_sync.py 10/10 red → green; test_fleet_sync_audit.py 5 new red (9 old green) → 14/14; MCP monitoring.test.ts 7/7 red → green; frontend specs 12 red → green; the I2 tests (tile + helper) red → green
Neighbouring backend suites, pytest-randomly seeds 1, 2, 12345 339 passed ×3 (sync-health, freeze, monitoring, fleet-audit, #73 / #1595 / #2742 / #1677)
Each commit on its own (6 commits, after the rebase onto f800e91b1) backend subset 242 → 252 → 257 ×4 passed; MCP npm test 550 → 557 passed and tsc --noEmit clean; frontend vitest 3693 → 3728 passed
MCP full suite 557 passed, 0 failed (includes TOOL_ACCESS_POLICY totality)
Frontend npm run test:unit 179 files / 3731 tests passed (raw-colour, loading-gate and source-text ratchets inside)
npm run check:tokens / npm run build OK / OK
Raw colours per file (PR-A tip → this) AgentTile 0/0/3 → 0/0/3; OverviewPanel 0/150/0 → 0/150/0; new syncSummary.js 0/0/0 (semantic only). Loading gates 70 → 70
tests/lint_sys_modules.py 140 / baseline 140, no new violations
Full unit suite, verify-local's exact invocation (cd tests; pytest unit -m "not slow", py3.13): base f800e91b1 vs tip base 40 failed / 18607 passed; tip 40 failed / 18621 passed. The same 40 node-ids, 0 only at the tip: ent477 definitions (5) + git refresh hook (10), ent666 objective join (21), retention floor (4). All pass in isolation on both trees, so they are pre-existing order-dependent failures (docker_service stub pollution)
/verify-local (global, VERIFY_PYTHON=python3.13, foreground, sibling stack, --skip-agent per plan: no base-image change) Unit stage: see the row above; it fails fast on the 40 pre-existing, so the Docker stages were run with --skip-unit: build+import-smoke OK, boot+health OK, integration 70 passed / 13 skipped → RESULT: PASS
Live, through the real images (verify stack + a labelled probe container, git rows seeded via the backend's own db) /api/monitoring/status: the sync block, the issue string and sync_summary exactly as specified, with status unchanged; /api/fleet/sync-audit: dirty_tree: true, new keys, summary; error marker absent from both payloads
Live MCP over the real transport get_fleet_sync_audit advertised and returns the audit; get_fleet_health carries sync, sync_summary and issues
Live D12 An agent-scoped key of a non-admin owner sees exactly the owner's agent through both MCP tools and REST, and never the admin-owned agent
Real browser, both themes (throwaway harness: real CSS, the tile's --gv-* blocks lifted from FleetGrid.vue) Tile chips: kind per state, text, tooltip, no clipping, no chip for unknown; contrast matches the sibling chips (light 4.58–4.63, dark crit 6.41 / warn 9.99). The Overview line on chrome failed AA at 700/400 (warning 4.47, danger-dark 3.73), so it steps up one tier to 800/300 → 5.43–7.82. No raw error in the DOM
Security scans on the diff (keys, emails, IPs, env/cred files, compose/Dockerfile, new backend dirs, env reads, modes, host paths, enterprise-docs guard pattern) clean
.claude / src/backend/enterprise gitlinks unchanged (547b39fc / f769d07d), equal to origin/dev

Decisions (posted on trinity-enterprise#706 on 2026-09-27; none overruled as of this writing)

# Decision Taken here
D11 Red sync and AgentHealthSummary.status An annotation only; status, sort and counts unchanged
D12 get_fleet_sync_audit for agent keys Callable, scoped to the owner's accessible agents, no raw error text
D13 The Overview attention badge Still counts consecutive_failures

Not verified

  • AC 6. An orchestrator's /sync-fleet-to-head or corbin's sweep running on the new surface against a real github: agent (the numbers matching in-container git within one poll) was not run. The live checks used seeded rows, not a real repository.
  • Components on their real page. The tile and Overview were verified in a harness (real CSS, both themes), not in situ on the Dashboard / Agent Detail with the real stack's fetches.
  • The agent stage of verify-local (--skip-agent; this PR has no base-image change).
  • PostgreSQL: no pytest runs on PG in CI, and none ran locally. This PR has no schema change, and the one query it reads is feat(sync-health): divergence age is the signal — red and frozen at >24h, persisted on the row (trinity-enterprise#706) #3035's (SQLAlchemy Core).

Reviewer notes

  • This touches src/frontend/, so it needs the ui label for frontend-e2e.
  • MCP tool count 129 → 130 (architecture/mcp-server.md).
  • Docs: requirements/github.md §11.19 (the fleet-surfaces block), requirements/infrastructure.md §12.8, architecture/{api-endpoints,mcp-server}.md, feature-flows/{git-sync-health,agent-monitoring,dashboard-grid-view}.md, a dated feature-flows.md index row, and the user docs for monitoring, managing agents and GitHub sync.

🤖 Generated with Claude Code

Not stacked any more. #3035 merged; the diff against dev is this PR's ent#707 changes only.

…-enterprise#706)

Amends §11.8 (health now measures agreement with the repository, not only
push success) and adds §11.19: the four persisted columns, the one policy
module and its scheduler mirror, the work-agent predicate, the state rules,
the freshness-guarded freeze and the one-item-per-episode alert.

Refs Abilityai/trinity-enterprise#706
…ty-enterprise#706)

services/sync_freeze_policy.py::classify decides an agent's sync state
(green / yellow / red / unknown), the operator-readable reason, the
recommendation and whether its schedules freeze. Stdlib only, never raises,
vendored byte-identically to src/scheduler/ (the #1088 precedent) with a
parity test. The scheduler's SYNC_FAILURE_FREEZE_THRESHOLD now comes from
the mirror, so there is one number.

Nothing calls classify yet; the poller, the gates and the surfaces follow.

Refs Abilityai/trinity-enterprise#706
…count, last push (trinity-enterprise#706)

agent_sync_state gains diverged_since, dirty_files, dirty_since and
last_successful_push_at, on both tracks: the SQLite migration
agent_sync_state_divergence and Alembic 0080_agent_sync_state_divergence
(parent 0079_telegram_group_context, the live head), plus schema.py and
tables.py. All nullable, no backfill: the clocks start at the first poll
after upgrade, which is the 24 h soak before any divergence freeze.

The upsert takes a KEEP sentinel for the two clocks so a caller can say
clear (None) as distinct from unchanged, which _merged() cannot express.

Refs Abilityai/trinity-enterprise#706
…d the last push (trinity-enterprise#706)

On every poll, inside the leased cycle: diverged_since is set once when the
working tuple (origin on the agent's own branch) shows ahead or behind,
cleared when both are known 0 (or ahead is 0 with no upstream), and kept
when ahead could not be computed. dirty_files comes from the changes_count
the status call already makes, with dirty_since as its own episode clock.
last_successful_push_at is the later of the heartbeat's #3011 record, an
operator Push and the stored value; an image older than #3011 falls back to
its last success cycle. Agent timestamps are validated (explicit offset,
not in the future) before they can win a max().

Refs Abilityai/trinity-enterprise#706
…ce freeze (trinity-enterprise#706)

When a divergence freeze is in force, the poller (inside its leader lease)
raises one sync_diverged item with the deterministic id
sync-diverged-{agent}-{diverged_since}. create_item's (agent_name,
request_id) conflict target makes repeat polls, a restart and a fail-open
double leader converge on one row; a new episode has a new id. The prefix
is platform-reserved so an agent cannot pre-create the id and silence the
alarm about itself. No item for red-but-not-frozen; not auto-resolved. The
context carries counts and the recommendation, no agent-authored error text.

Refs Abilityai/trinity-enterprise#706
… agents (trinity-enterprise#706)

Both enforcement points now decide with the one policy module:

- src/scheduler/database.py: sync_freeze_reason() returns why an agent's
  cron firing is frozen (or None), reading the divergence inputs in the same
  fail-open query; should_freeze_schedules() is its bool view. A reason
  string, not a (bool, reason) tuple, because a tuple is truthy and a caller
  on the old shape would freeze the fleet.
- src/scheduler/service.py: the skipped row's skip_reason carries the
  reason ("Git sync frozen: diverged 0 behind / 7 ahead for 26h"), and the
  comment no longer calls the row count bounded.
- routers/internal.py: sync-health-status keeps its keys and adds
  freeze_reason, divergence_age_s and work_agent via the new
  services/sync_health_view.py, which the poller now uses too.

Refs Abilityai/trinity-enterprise#706
…verdict (trinity-enterprise#706)

GET /api/agents/sync-health and GET /api/agents/{name}/git/sync-state add
state, reason, recommendation, binding, freeze, the divergence age and the
new columns; every pre-existing key is unchanged. MCP get_git_sync_state
passes the backend JSON through, so it carries them too with no TS change.

The batch now reads through one shared query, db.list_sync_health_rows
(agent_git_config joined to live ownership, left-joined to
agent_sync_state), so the dot and the poller see the same set of agents.

Refs Abilityai/trinity-enterprise#706
…(trinity-enterprise#706)

utils/syncHealth.js no longer owns a threshold: classifySyncHealth maps the
backend's state (unknown -> gray) and syncHealthLabel shows its reason and
recommendation on hover. The 24 h / 7 d / behind-is-red rules are gone from
the frontend, so the dot cannot disagree with the scheduler's freeze.

Visible change: an agent ahead of origin with auto-sync off used to render
gray (it had never synced); it now renders the backend's yellow or red.

Refs Abilityai/trinity-enterprise#706
…freeze (trinity-enterprise#706)

Architecture (agent-lifecycle Git Sync Health, database DDL, background
services, backend catalog, api-endpoints), the git-sync-health flow (data
model, poller, the new §2b policy and freeze, the dot, tests, controls,
known limitations), the github-sync journey note, the feature-flows index
row, the RD-03 invariant note, and the user docs: what the sync state
means, when schedules pause and resume, the new Operating Room entry.
The MCP get_git_sync_state description names the new fields (the tool is
a pass-through; no behaviour change).

Refs Abilityai/trinity-enterprise#706
…nterprise#706)

When the agent cannot compute its working tuple (a git timeout sends
ahead_working/behind_working as null), the poller correctly KEEPS
diverged_since, but _coerce_counter still stored the unknown counts as 0.
The row then read "diverged 25 h" next to 0/0: the freeze fired with the
self-contradicting reason "diverged 0 behind / 0 ahead" and the episode's
one sync_diverged item carried ahead 0 / behind 0.

Pass None (upsert's "keep the prior value") for the two working counts
exactly when the clock is kept on an unknown tuple. Scoped to that case:
with no episode to keep, an unknown tuple is still stored as 0 (#2827).

Found by /review on #3035. The new test was red before the fix
(assert (0, 0) == (7, 31)); the control arm is green before and after.
…the agent card (trinity-enterprise#707)

The requirement for the #707 read surfaces, written before the code: a
per-agent sync block and fleet totals on /api/monitoring/status, the
recommendation in issues[], the fleet sync audit's new columns and its MCP
tool, the agent-card sync chip, access scoping, and no raw git error text
in any surface's reason.

Refs Abilityai/trinity-enterprise#707
…trinity-enterprise#707)

GET /api/monitoring/status annotates every agent with a sync block read
through the shared reader and the one policy: binding, auto_sync_enabled,
ahead/behind on the agent's own branch, dirty_files, last_successful_push_at,
divergence_age_s, state, reason, recommendation and frozen, or null for an
agent with no git binding. The response gains fleet totals in sync_summary,
and a red agent gains one 'sync: <reason> — <recommendation>' issue.

Sync is an annotation like the heartbeat layer: status, the sort and the
health counts are unchanged. The merge is computed before it is applied and
degrades on its own to null on a fault, never into the aggregation-failed
payload.

sync_view now takes reason and freeze_reason from the verdict computed with
the git error withheld, so no surface's reason carries the agent-written
stderr (this also covers the sync_diverged item's question). The
recommendation still reads the error. The scheduler's skip row, which calls
the policy directly, is unchanged.

Refs Abilityai/trinity-enterprise#707
…rdict (trinity-enterprise#707)

build_fleet_sync_audit keeps every pre-#707 key and adds, per agent, ahead,
behind, dirty_files, diverged_since, divergence_age_s,
last_successful_push_at, state, reason, recommendation, binding,
auto_sync_enabled and frozen from the same sync_view call every other
surface uses. dirty_tree is now real (dirty_files > 0) instead of a
hard-coded false, so the summary's in_sync and dirty are real too; the
summary adds diverged, frozen, auto_sync_off and red.

Refs Abilityai/trinity-enterprise#707
… (trinity-enterprise#707)

get_fleet_health passes each agent's sync block (null without a git
binding), the fleet sync_summary and the issues strings through, next to the
kept issues_count, so an orchestrator sees the red-sync recommendation.

New get_fleet_sync_audit reads GET /api/fleet/sync-audit with the caller's
key and returns the audit unchanged. No agent target, so its policy row is
'none': the backend scopes the rows through accessible_agent_names, and an
agent key sees its owner's set (decision D12), as get_fleet_health already
does. routers/fleet.py's '# mcp:' header now names it (Invariant #13).

Refs Abilityai/trinity-enterprise#707
…st push (trinity-enterprise#707)

The Fleet tile carried 'sync failing ×N' (with the raw git error as its
tooltip) and a bare 'git ✓'; an agent 15 commits ahead with auto-sync off
showed neither. It now carries one sync chip: the kind follows the backend's
state (red crit, yellow warn, green calm, nothing for unknown), the text is
'↑7 ↓0 · 12 dirty · pushed 3h ago', and the tooltip is the reason and the
recommendation plus the absolute push time. One chip replaces two, so the
strip does not grow; a problem leads it and a calm fact trails it.

Agent Detail → Overview kept only consecutive_failures and said 'ok' for
every agent that had not failed a push, including agents with no git binding.
It now keeps the whole /git/sync-state payload and renders the same numbers
in the state colour; it sits on a chrome fill, so status ink steps up one tier (800 light / 300 dark: the 700/400 tier measured 4.47:1 and 3.73:1 there in the browser), '—' with no
observation or after a failed read. The attention count still counts failed
syncs (D13).

The formatter is its own module, utils/syncSummary.js, taking 'now' from the
caller: syncHealth.js keeps its #706 guarantee of no clock and no counts.
Semantic tokens only; per-file raw-colour and loading-gate counts unchanged.
Specs mount AgentTile and OverviewPanel.

Refs Abilityai/trinity-enterprise#707
… and the agent card (trinity-enterprise#707)

Architecture: the monitoring.ts row gains get_fleet_sync_audit and the
sync pass-through (130 tools); api-endpoints gains the /api/monitoring/status
row and the sync-audit additions. Feature flows: git-sync-health (§4 audit,
§5 the card, files, tests, the dirty_tree limitation closed, the scheduler
skip row's error excerpt recorded), agent-monitoring (the sync merge),
dashboard-grid-view (the chip) and a dated index row. User docs: monitoring,
managing agents, GitHub sync.

Refs Abilityai/trinity-enterprise#707
@AndriiPasternak31 AndriiPasternak31 added the ui PR touches the frontend UI — triggers Playwright e2e tests label Sep 27, 2026
#3005 (skill sets) landed 0080_agent_skill_sets on 0079. This branch's
Alembic revision becomes 0081_agent_sync_state_divergence on top of it,
and the SQLite entry follows agent_skill_sets in MIGRATIONS. No change
to what the migration does; docs updated to the new revision id.
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

✅ Alembic head check clear — merging this PR into dev leaves one head (0082_agent_sync_state_divergence).

Previously flagged; resolved.

Evaluated on the version line only — this PR also conflicts with dev in 6 unrelated file(s), which do not change this verdict but must be resolved before merge:

docs/memory/feature-flows.md
docs/memory/feature-flows/git-sync-health.md
docs/memory/requirements/github.md
docs/user-docs/integrations/github-sync.md
src/backend/services/sync_health_view.py
tests/registry.json

Advisory — this check does not block merge. · head_sha: 827baeae1b516327165f3e5726b9dceccb4b0a99 · run

…kes the state tint (trinity-enterprise#707)

From manual sign-off on the sign-off stack:
- The tile chip's ⟳ read as a dot at 9.5px. It is now its own aria-hidden
  element at 2x, in a fixed 11px box so the chip keeps its 12.22px height,
  and nudged up 0.09em: the fallback font draws its ink 1.5px below the box
  centre (measured in the browser; 0.12px after the nudge).
- The Overview 'Sync:' pill takes the state tint as a whole on red and
  yellow (status-* 50 / 900/30 fill, inset ring so it keeps its siblings'
  height). Green and unknown keep the neutral chrome of the sibling pills.
@AndriiPasternak31

Copy link
Copy Markdown
Contributor Author

Manual sign-off done on a local stack, with seeded agents in the red (frozen), yellow and green states plus one with no git binding. Grid, List and Agent Detail → Overview were checked in the browser.

Two changes came out of it, in 8603d22b5:

  • The tile chip's ⟳ is 2x and centred. At 9.5px it read as a dot. It's now its own aria-hidden element in a fixed 11px box, so the chip keeps its 12.22px height (measured: every chip on the grid is the same height). It's also nudged up 0.09em, because the fallback font draws its ink 1.5px below the box centre. After the nudge it's 0.12px off, measured in the browser.
  • The Overview "Sync:" pill takes the state tint as a whole on red and yellow: a status-* 50 / 900-at-30% fill with an inset ring, so it stays the same height as the pills next to it. Green and unknown keep the neutral grey of the sibling pills.

Tests: new specs for the pill tint and the glyph element, and the two chip-text assertions updated because the glyph is no longer followed by a space character. npm run test:unit 3740/3740, both ratchets, check:tokens and build all green.

Still a draft: it's stacked on #3035 and gets rebased onto dev once that merges.

#3005 (skill sets) landed 0080_agent_skill_sets on 0079. The ent#706
Alembic revision this branch carries becomes 0081_agent_sync_state_divergence
on top of it, and the SQLite entry follows agent_skill_sets in MIGRATIONS --
the same resolution as #3035's merge 9229f08. Docs name the new revision id.
@github-actions

Copy link
Copy Markdown

⚠️ Nightly unit-suite check skipped — merge conflict against dev.

Resolve by running git merge dev locally and pushing the result. The next nightly run will re-test once the conflict is gone.

@github-actions

Copy link
Copy Markdown

⚠️ Live-instance suite skipped — merge conflict against dev.

Resolve by merging dev locally and pushing the result; the next nightly re-tests.

Alembic 0081_agent_sync_state_divergence -> 0082, down_revision
0081_portal_messages_unread_idx; SQLite entry after
portal_messages_unread_index. Docs/registry conflicts keep both sides.
@AndriiPasternak31
AndriiPasternak31 marked this pull request as ready for review September 29, 2026 18:46
@AndriiPasternak31

Copy link
Copy Markdown
Contributor Author

@dolho ready for review when you have a moment. This is stacked on #3035 (it carries #3035's commits and its 0082 migration), so it merges after #3035. CI is green at 9aabd14e5, and the manual browser sign-off on Grid / List / Agent Detail is in the comment above.

@AndriiPasternak31 AndriiPasternak31 self-assigned this Sep 29, 2026
# Conflicts:
#	docs/memory/feature-flows.md
# Conflicts:
#	docs/memory/feature-flows.md
#	docs/memory/feature-flows/git-sync-health.md
#	docs/memory/requirements/github.md
#	docs/user-docs/integrations/github-sync.md
#	src/backend/services/sync_health_view.py
#	tests/registry.json
@vybe

vybe commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

merge-train: this PR is on the train. /validate-pr and /review both came back READY with no criticals, and the tests run the real routes and mounted components. I made one mechanical edit: the body now says Fixes abilityai/trinity-enterprise#707, since this is the last PR for it. I also removed the duplicate footer. AC 6 (the live sweep) still needs to happen before ent#707 is closed at release.

Notes, not blocking:

  • MonitoringPanel issue count. The new sync: … entry in issues[] is added to that panel's count, which is shown in danger colour. stores/monitoring.js:337-341 (handleHealthEvent) then replaces issues with the WS payload, which doesn't include the sync entry, so the entry disappears until the next poll. Either accept that or carry the entry across.
  • .chip-icon-x15 (AgentTile.vue:776) is unused.
  • The fleet-block branch in utils/syncSummary.js has no frontend consumer. Only its spec uses it.
  • The freeze_reason redaction also applies to GET /api/internal/agents/{name}/sync-health.

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

merge-train: validated (validate-pr + review, mechanical fixes noted on the PR); merged individually — batch under 4, no train assembled.

@vybe
vybe merged commit 6949b30 into dev Sep 30, 2026
28 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ui PR touches the frontend UI — triggers Playwright e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants