mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-26 05:29:45 +00:00
92f20dafdbece689a67dcf22994ff37d81d39a93
15578 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
92f20dafdb |
Hide Re-investigate while a planned fix is queued
A Patrol investigation can plan a fix and then fail, normally on the final summary call. The run then shows a queued fix with a failed status, and the Investigation section offered Re-investigate because it only looked at the status. Another run would plan a second action for the same problem while the first still waits for approval. The section now hides Re-investigate while the outcome is fix_queued, as it already did for a completed run with a queued fix. The failed run's note uses the muted style of an interrupted run, because the fix awaiting approval is what the operator acts on, not the error. The Pro orchestrator keeps the plan's outcome on such a run and refuses the re-investigation request too. |
||
|
|
c67f8bd500 |
Describe the Assistant surface as Pulse Mobile chat (#3139)
## Why The web chat window and its backend routes are retired (#3126, #3137), so the `pulse_assistant` surface now only serves Pulse Mobile's chat. Its manifest description still called it "the contextual explanation, approval, and handoff surface for Patrol findings". That text is public in three places: the overview at the top of `docs/AI.md`, the MCP README surface block, and the External agents settings panel. ## What changes - **Manifest description.** It now reads "The chat surface Pulse Mobile uses for operator questions over the same governed tools, approvals, and verification; the in-app web chat window is retired." The generated docs blocks follow it. The surface ID, label, affordances and tools are unchanged. - **Browser fallback.** `frontend-modern/src/api/agentCapabilities.ts` no longer gives an undeclared Assistant surface `interactiveQuestions`. That matches the backend default of tools only. - **Tests and contracts.** The Go, Python docs-policy and frontend tests that pinned the old text and fallback are updated. The settings panel test now asserts that no "Interactive questions" badge appears. The ai-runtime and api-contracts contracts carry dated notes. Checked on a private stack built from this branch, at desktop and phone sizes: - **What it shows:** under External agents → Developer details → Patrol access model, Pulse Assistant has "Native surface" and "Actions" badges and the new description, and no "Interactive questions" badge appears. - **Layout and errors:** no horizontal overflow and no console errors. The receipt is included. ## Not in this PR - The dead `highlight.js` chunk rule in `vite.config.ts`. It matches nothing since the package was removed. The commit guard requires a build-proof file change for any edit there, which isn't worth it for a no-op. |
||
|
|
a32356ae94 |
Describe the Assistant surface as Pulse Mobile chat
With the web chat window and its backend routes retired (#3126, #3137), the pulse_assistant surface only serves Pulse Mobile's chat. Its manifest description still called it the explanation and handoff surface for Patrol findings, and that text is public: the docs/AI.md overview, the MCP README surface block, and the External agents settings panel all show it. It now names the Pulse Mobile role and says the web chat is retired. The browser's fallback affordances for an Assistant surface with none declared still offered interactive questions. They now match the backend default of tools only, so no client path advertises a question tool nobody can answer. |
||
|
|
e7c765c9ec |
Bound Unix teardown fixture controls independently (#3138)
The macOS Intel job in Unified Agent Native Verification has been
failing `TestInstallSHUpstartTeardownRequiresObservedQuiescence` with
`signal: killed` at 60.01s (main run 39883120153, and run 39952586718 on
#3135). It is not a required check, so nobody noticed.
## Cause
The test ran all 17 `agent_upstart_teardown.py` controls in one Python
process under one `time.Minute` context. In both failing logs every
control that finished was `ok`, and the process was killed partway
through
`test_unconfirmed_job_retirement_restores_exact_config_and_retains_manual`,
the 16th control. That control finishes in about 0.2s on its own.
The same suite takes 41.3s on a passing macOS Intel run (39956150130),
23.0s on macOS ARM64 and 13.9s on Linux x64. So the one-minute limit was
measuring how fast the runner was, not catching a stuck Upstart
teardown. `scripts/install.sh` is not at fault. The Legacy teardown
suite in the same block (18 controls) takes 35.1s on Intel against the
same limit.
The NAS suite hit this exact problem earlier and was fixed in
|
||
|
|
c909b4db79 |
Bound Unix teardown fixture controls independently
The macOS Intel native job killed the DSM Upstart suite at its single one-minute alarm after sixteen controls had passed (main 39883120153, PR 39952586718). Passing runs take 41s on Intel, 23s on ARM and 14s on Linux, so the budget measured runner speed, not a stuck teardown. The Legacy suite sits at 35s against the same alarm. Run each discovered systemd, OpenRC, legacy and Upstart control through the shared helper under its existing bound, as the NAS and TrueNAS suites already do. Required controls stay mandatory, and skipped, absent or failed controls still fail. Installer deadlines are unchanged. |
||
|
|
070d2a0e53 |
Remove chat backend routes only the web chat used (#3137)
## Why The in-app Assistant chat window was retired in #3126. Several backend routes existed only for that window, and Pulse Mobile, the remaining chat client, never called them. This removes them and the code that only they reached. ## What changes - **Routes removed:** - `GET /api/ai/assistant/surface-tools` - `POST /api/ai/workflow-prompts/render` - `POST /api/ai/question/{id}/answer` - the `/summarize`, `/fork`, `/undo`, `/redo`, `/steer`, `/diff`, `/revert` and `/unrevert` sub-resources under `/api/ai/sessions/{id}`. These now return `404` after the existing `ai:chat` scope check. - **Code removed behind them:** - the handlers and their `AIService` methods; - the chat service's fork, undo, redo, steer, summarize, answer-question and surface-tool methods; - the session store's fork and turn-undo operations and the persisted redo stack (`can_redo` leaves session summaries); - session compaction, the agent loop's steering inbox and question-answer wait, the `steered` message flag, and the `question` and `steer_applied` stream events. - **The question tool is no longer offered.** Interactive chat still offered `pulse_question`, and Pulse Mobile has no way to answer it, so a Mobile turn that called it waited until the request ended. Now: - no profile offers the tool; - the agent loop answers any call with a refusal telling the model to ask in its reply text, the same path Patrol runs already took; - the chat system prompt no longer mentions a clarification tool; - the agent capabilities manifest's Pulse Assistant surface no longer declares the `interactiveQuestions` affordance, so the prompt's affordance text, the fallback governance names, and the generated `docs/AI.md` and `cmd/pulse-mcp/README.md` surface blocks drop it. The External agents settings panel stops showing an "Interactive questions" affordance for Pulse Assistant. The shared `pulse_question` declaration stays. - **Docs and contracts:** - `docs/API.md` lists the remaining routes under "Chat Sessions (Pulse Mobile)", including the rename route Mobile already used. - The ai-runtime and api-contracts rules for the removed workflows are rewritten, and the dependent contracts carry dated notes. ## Unchanged - `/api/ai/chat` and the session list, create, rename, delete, messages and abort routes, which are the Pulse Mobile relay route list. - `/api/ai/status`, which the dev health check uses. - `/api/ai/workflow-prompts/activity`, which Patrol uses. - Patrol investigations, provider routing, model-boundary sanitizing and approvals. - Old session files: they load as before and drop the redo stack and `steered` flags on their next write. ## Not in this PR - The Pulse Assistant surface description in the manifest, which still calls it a handoff surface for Patrol findings. It now describes Pulse Mobile chat and can be reworded with the surface contract when Mobile retires. - The browser's legacy fallback in `frontend-modern/src/api/agentCapabilities.ts`. It still gives Pulse Assistant `interactiveQuestions` when a manifest declares no affordances at all. A same-version server always declares them, so that path is unused; it changes with the surface description. - The dead `highlight.js` chunk rule in `vite.config.ts`. ## Release note > Removed API routes the retired in-app chat window used: Assistant surface tools, workflow-prompt rendering, question answers, and the session summarize, fork, undo, redo, steer, diff, revert and unrevert routes. Pulse Mobile's chat is unaffected. |
||
|
|
b044d3cdec |
Preserve PVE authentication endings while reading responses (#3136)
## Summary `TestClientConstructionHonoursContext` (`pkg/proxmox`) has been failing the required "Backend tests (rest-0)" check on unrelated PRs: #3132 (`json/cancel`) and #3134 (`form/cancel`) on 23 Oct, each with `authentication failed: invalid authentication response: expected a complete ticket and CSRF token`. **Cause.** When a PVE password login ends while its response is being read (the caller cancels, the caller's deadline passes, or the client's own login timeout expires), Go's HTTP transport can still return the server's answer to the abandoned request. The fixture answers a departed client with an empty 200, and the PVE client reported that answer as a malformed session instead of the context error. The PBS client got this fix on 18 Oct ( |
||
|
|
dcbfd2d86d |
Remove chat backend routes only the web chat used
The in-app Assistant chat window was retired in #3126. Several backend routes existed only for it, and Pulse Mobile, the remaining chat client, never called them: assistant surface-tools, workflow-prompt render, question answers, and the session summarize, fork, undo, redo, steer, diff, revert and unrevert sub-resources. This removes them along with the handlers, service and session-store methods, the persisted redo stack, session compaction, mid-turn steering and the question-answer wait behind them. The question tool needed more than a route removal. Interactive chat still offered pulse_question, and Pulse Mobile has no way to answer it, so a Mobile turn that called it waited until the request ended. No profile offers it now. The agentic loop answers any call with a refusal that tells the model to ask in its reply text, the path Patrol runs already took, and the chat prompt drops its clarification-tool advice. The capabilities manifest's Assistant surface stops declaring interactive questions, so the generated docs and fallback governance names follow it. /api/ai/chat and the session list, create, rename, delete, messages and abort routes Pulse Mobile uses are unchanged, as is /api/ai/status. Old session files load as before and drop the redo stack and steered flag on their next write. docs/API.md lists the remaining chat routes, and the subsystem contracts record the removal. |
||
|
|
ae19fcf5d6 |
Preserve PVE authentication endings while reading responses
A password login can end while its response is being read: the caller cancels, its deadline passes, or the client's own login timeout expires. Go's HTTP transport can still hand back the server's answer to that abandoned request. The construction fixture answers a departed client with an empty 200, which surfaced as "invalid authentication response" and failed TestClientConstructionHonoursContext and its client-timeout twin intermittently in the required backend check. The shared password exchange now reports the ending instead of diagnosing that answer, keeps transport errors that already carry the ending, and starts no form attempt after an ended JSON refusal. This mirrors the PBS client fix from 18 October. Renewal shares the exchange, so the same rule applies when its own bounded timeout ends a login. Complete sessions are still accepted, and construction still discards a client whose caller has ended. A new control ends the login during response reading, by caller cancellation and by a simulated login deadline, for every unusable answer; without this change all of its cases fail. It also checks that both encoding attempts carry the one login budget the client timeout sets. The TLS fixture now aborts a request whose client has left rather than answering it with an empty 200. |
||
|
|
751dfcce0a |
Read action token file modes portably on macOS (#3135)
The agent installer checked an --action-token-file's permissions with GNU-only stat -c. BSD stat on macOS rejects it, the mode read came back empty, and every action token file was refused as group- or world-accessible. It now uses the existing portable_path_mode helper, which the collector token path already relies on. The ingress test runs both token options through the real installer functions and the compiled token reader; on the macOS runner it is the proof for this fix. Contract-Neutral: portability fix restoring the documented --action-token-file option; no contract change |
||
|
|
47dc675165 |
Keep the token ingress fixture independent of umask
t.TempDir creates its directory through the process umask. Under 0002 the directory is group-writable, the installer correctly refuses to trust the reader inside it, and every valid case failed on hosts with that umask while passing on CI runners. Contract-Neutral: test fixture hardening; no contract change |
||
|
|
bf7e66e045 |
Drop installer NUL cases main does not implement
The ingress test came from a line whose installer also refused NUL bytes in token files. Main's installer does not, so those cases failed on every runner. The remaining cases cover what this branch fixes: both token options read through the real installer and token reader. Contract-Neutral: test scope correction for the portability fix; no contract change |
||
|
|
d167d7f492 |
Count each finished Patrol investigation once in the weekly digest (#3134)
## Why The Patrol weekly digest's **Investigated** line, on the Patrol page and in the weekly email, was unreliable: - It counted `investigation_outcome` lifecycle rows and read their outcome from `metadata["outcome"]`. The only writer of those rows (`updateInvestigationOutcomeLocked`) writes `prev_outcome`/`next_outcome`, so every counted row was labelled `unknown`. - Those rows are later steps of an earlier run's fix (approved, run, verified), not investigations. Meanwhile the `investigation_updated` rows every run writes were ignored, apart from one `LastInvestigatedAt` fallback count per finding. - So two runs on one finding counted once, a fix approved after last week's run counted as this week's investigation, and the email could print "Investigated: 3" followed by "No issues needed a closer look." ## What changes - `digestInvestigationRuns` counts one investigation per run that finished in the window, from the rows the findings store already writes for every run. That includes a run whose own fix was verified before it closed, and a run whose status a regression cleared mid-run. A fix that moves on afterwards relabels its run instead of counting again. - A run that failed with no outcome is labelled `failed`, the bucket name telemetry already uses. It sits after `timed_out` in `PatrolDigestOutcomeOrder`. - The summary-field fallback is removed. Every run since the v6 release writes lifecycle rows, and the fallback could count one run in two adjacent weeks. - Email: `failed` reads "ended with an error". When the count is above zero but no outcome in the breakdown has wording, the line shows the count alone instead of "No issues needed a closer look." or the watch-only sentence. - `internal/ai/findings.go` is unchanged; the digest reads the rows the store writes today. Known limits: lifecycle rows carry no session ID, so a late end written by an older run is read as the end of whichever run is open. That's the store-side stale-write problem, which is being closed separately with session checks. A run whose rows the 50-row lifecycle has already dropped is not counted. ## Proof - `internal/ai/patrol_digest_test.go` drives the store's own writers: the orchestrator's start and end updates, `UpdateInvestigationOutcome`, `ReconcileInvestigationOutcome`, `RecoverStuckInvestigation`, and `Resolve` + `Add` for a mid-run regression. The scenario covers ten finished runs plus one still running. The old code reports 8 investigations, 3 of them `unknown`. - `internal/api/contract_test.go` follows the scheduled email's path: store writers → `AISettingsHandler.BuildPatrolDigest` → `renderPatrolDigestEmail`. The old code reports two runs as `1 unknown`. - `internal/api/patrol_digest_email_test.go` checks the `failed` wording and the non-zero-count guard. - On pulse-dev, `go vet` passes for `./internal/ai/` and `./internal/api/`, and the full `./internal/ai/` package and every `PatrolDigest` test in `./internal/api/` pass. The new tests fail against the previous digest and email (negative controls via `-overlay`). ## Related The Patrol page's This week card gets the same `failed` wording and the same non-zero-count guard in #3132. Until that lands, a week with only failed runs reads "No issues needed a closer look." on the card. That's what those runs already showed as `unknown`, so it isn't a regression. |
||
|
|
e78f17979e |
Read action token file modes portably on macOS
The agent installer checked an --action-token-file's permissions with GNU-only stat -c. BSD stat on macOS rejects it, the mode read came back empty, and every action token file was refused as group- or world-accessible. It now uses the existing portable_path_mode helper, which the collector token path already relies on. The ingress test runs both token options through the real installer functions and the compiled token reader; on the macOS runner it is the proof for this fix. Contract-Neutral: portability fix restoring the documented --action-token-file option; no contract change |
||
|
|
3b5d789aeb |
Show unclassified and interrupted Patrol investigation outcomes (#3132)
Since rcourtman/Pulse#3129, Patrol records two more investigation
outcomes:
- `unclassified`: a run that finished without a readable conclusion. It
stays retry-eligible and presents as needs-attention in the loop state.
- `interrupted`: a run that a restart or shutdown cut short. It has no
cooldown and runs again.
The frontend's outcome lists did not know either value, so the findings
store dropped both to "no outcome".
## What changes
- `stores/aiIntelligence.ts` keys the outcomes it accepts by the
`InvestigationOutcome` union in `api/patrol.ts`. `api/ai.ts` now reuses
that union, so the next backend outcome fails type-check instead of
being dropped. The import is type-only because several tests mock
`@/api/patrol` wholesale.
- Every outcome map in `aiFindingPresentation.ts` gains both values:
- `unclassified` reads "No clear conclusion" in the warning tone, joins
the Needs Attention queue and sorts beside `needs_attention`. Its
workflow step asks the operator to read the summary.
- `interrupted` reads "Interrupted — will retry" in the neutral tone and
stays out of the attention queue. Its collapsed row says "Will retry"
instead of "Needs review".
- Neither counts as a failed fix.
- `InvestigationSection` offers Re-investigate after both outcomes. An
interrupted run's note renders as information, not in the error style.
- The This week card names both outcomes with the digest email's wording
and order (`PatrolDigestOutcomeOrder`, `patrol_digest_email.go`).
- Two drawer defects the new labels exposed are fixed:
- The Patrol record showed a red "Failed" run status beside "will
retry". Like the investigation header, it now shows the run status only
until the run has an outcome.
- The record badge used `formatIdentifierLabel` ("Unclassified", "Fix
Executed", "Cannot Fix"). It now uses the shared outcome labels that the
header uses.
- On phones the longer badge broke "Re-investigate" across two lines.
The header row now wraps.
Telemetry is unchanged: the two outcomes already map to `in_progress`
and `other`.
A second commit lines the This week card up with the digest-count
backend change:
- The card names the digest's new `failed` bucket ("ended with an
error"), for a run that ended on an error with no outcome. It sits right
after timeouts, as in `PatrolDigestOutcomeOrder` and the email.
- When none of a week's investigations has a worded outcome, the
Investigated tile shows the count alone. It used to say "No issues
needed a closer look" under a non-zero count. The email already does
this.
- That commit has its own receipt (`196f770aad8a3198.json`) from a pass
over live and fulfilled digest responses at 1440 and 390.
## Before and after
Private Pulse Pro stack: pulse-enterprise#139 built against this tree on
`ec40662af4`, with a mock estate and seeded findings, investigation
sessions and a week of runs.
| Surface | main | this branch |
| --- | --- | --- |
| This week, Investigated tile | 5, "No issues needed a closer look." |
5, "3 finished without a clear conclusion, 2 interrupted by a restart."
|
| Needs Attention filter | 1 | 4 (three unclassified, one
needs_attention) |
| Interrupted finding row | Needs review | Will retry |
| Unclassified drawer | raw `unclassified`, no Re-investigate, green
"Completed" record badge | "No clear conclusion", Re-investigate |
| Interrupted drawer | raw `interrupted`, red "Failed" record badge, red
restart note | "Interrupted — will retry" (neutral), muted restart note
|
| 390px drawer header | Re-investigate splits over two lines | the row
wraps; the button stays whole |
## Verification
- Vitest on pulse-dev:
- The 9 touched test files pass, 447/447.
- The wider api/stores/utils/AI/patrol/pages/shared run passed 8,858
tests. The one failure was `agentInstallCommand.test.ts`, caused by the
script's umask; it passes 90/90 with the default umask.
- Negative control: 26 of the new assertions fail against main's
sources.
- The Frontend CI job was replayed on pulse-dev:
- Format check, headers, cpd, type-check, build and bundle size passed.
- The lint chain caught a semicolon in the new "Read summary" copy.
After the fix every audit passed.
- The full suite passed 22,156 tests. Three docs-reading files timed out
at 5 s under load (`shippedDocFragments`, `operationalUpgradeHelp`,
`externalAgentGuide`); they pass alone, 8/8.
- Browser receipt
`frontend-modern/browser-verification/f1f2d9ca0f32c251.json`: headless
Chromium at 1440 (dark and light) and 390 on the final bytes, exercising
the This week card, the finding list and the review drawers of an
unclassified, an interrupted and a needs_attention finding.
- Governance: the per-commit canonical completion guard (with the
Contract-Neutral trailer), the receipt range check, and the contract and
registry audits all pass.
The presentation is documented in `patrol-intelligence.md`. ai-runtime
and api-contracts already describe both outcomes from #3129, so the
commit carries a Contract-Neutral trailer for them and for
frontend-primitives.
|
||
|
|
bd5a2946de |
Keep stale investigation runs from overwriting newer finding state (#3133)
The private investigation orchestrator writes a finding's investigation fields through `orchestratorFindingsAdapter` (`internal/api/ai_handlers.go`), and every write was applied unconditionally. A run still in flight when its finding was resolved and detected again, or when the stuck-run sweep gave it up and a retry started another run, overwrote the newer state with its own session, status, outcome and attempts. A stale `resolved` made the regressed occurrence terminal, so Patrol stopped investigating it. - Closing writes go through `FindingsStore.CompleteInvestigation`. It records them only while the finding still names the run: the session must be the finding's current one, and the run must not predate the finding's latest regression. If the finding still shows a run whose result does not count as running, the run is closed through `RecoverStuckInvestigation`, the way the sweep would close it. - Admission goes through `FindingsStore.StartInvestigation`. It refuses a run whose session started before that regression, so a delayed admission cannot install an earlier occurrence's attempt count or replace a newer run. - Both compare a run's start only with an occurrence start this process observed: a regression, or the finding's removal by `ClearAll` or `Cleanup`, after which the same ID detected again is a new finding. The store keeps these unpersisted, with the clock's monotonic reading, for a day, which no run outlives. A regression an earlier process recorded predates every run of this one, so a clock set back across a restart cannot make every new run look stale. Records and action outcomes keep comparing with `LastRegressionAt` (`acceptsInvestigationResult` is unchanged), since they also judge sessions of earlier processes. - The adapter takes each run's start from the orchestrator's session store, so no `aicontracts` type changes and pulse-enterprise needs no change. Tests: `TestOrchestratorRunResultSkipsALaterOccurrence` and `TestOrchestratorRunResultSkipsANewerRun` failed on the previous code (the stale `resolved` landed; an older run overwrote the run that replaced it). Store-level cases cover both new methods, regressions this process observed and ones an earlier process recorded. Mutation controls that remove each guard each fail a test: the session check, the regression boundary, the interrupted close, the start lookup, the admission fence, judging live runs against the persisted time, recording the observed regression, stamping removals in `ClearAll` and `Cleanup`, and the retention prune. `go test ./internal/ai/... ./internal/api/` passed on pulse-dev. Not covered here: - A run whose finding snapshot predates a regression, but whose session is recorded after it, is still installed with the snapshot's attempt count. - When two runs are dispatched for one finding at once, the run admitted last is the finding's run, and only its result is recorded. - Patrol's post-run sync can repeat a newer run's push notification after a stale run's close is refused, because the sync cannot tell which run it started. |
||
|
|
b633e54105 |
Count each finished Patrol investigation once in the weekly digest
The weekly digest counted investigation_outcome lifecycle rows and read their outcome from a metadata["outcome"] key the findings store never writes, so every one of them showed as "unknown". Those rows are later steps of an earlier run's fix (approved, run or verified), not new investigations, while the investigation_updated rows every run writes were ignored apart from one fallback count per finding. Two runs on one finding counted once, a fix approved after last week's run counted as this week's investigation, and the email could print "No issues needed a closer look." under a non-zero count. The digest now counts one investigation per run that finished in the window, from the rows the store already writes for every run, including a run whose own fix was verified before it closed. A fix that moves on afterwards relabels its run instead of counting again. A run that failed with no outcome is "failed", the bucket telemetry already uses; the email words it "ended with an error" and no longer denies a non-zero count. The summary-field fallback is removed: every run since the v6 release writes lifecycle rows, and the fallback could count one run in two adjacent weeks. The tests drive the findings store's own writers rather than hand-built rows, which is how the wrong metadata key went unnoticed. |
||
|
|
a408736bc4 |
Keep stale investigation runs from overwriting newer finding state
The private investigation orchestrator writes a finding's investigation fields through orchestratorFindingsAdapter, and every write was applied unconditionally. A run still in flight when its finding was resolved and detected again, or when the stuck-run sweep gave it up and a retry started another run, overwrote the newer state with its own session, status, outcome and attempts when it finished. A stale "resolved" made the regressed occurrence terminal, so Patrol stopped investigating it. Closing writes now go through FindingsStore.CompleteInvestigation, which records them only while the finding still names the run: the session must be the finding's current one and the run must not predate the finding's latest regression. Admission goes through StartInvestigation, which refuses a run whose session started before that regression, so a delayed admission cannot install an earlier occurrence's attempt count or replace a newer run. Both compare a run's start only with an occurrence start this process observed: a regression, or the finding's removal by ClearAll or Cleanup, kept unpersisted with the clock's monotonic reading for a day. A regression an earlier process recorded predates every run of this one, so a clock set back across a restart cannot make every new run look stale. Records and action outcomes keep comparing with LastRegressionAt, since they also judge sessions of earlier processes. The adapter takes each run's start from the orchestrator's session store, so the aicontracts boundary and the enterprise orchestrator are unchanged. |
||
|
|
04a49ed72a |
Name failed digest runs and keep the Investigated tile honest
The digest's investigation counts gain a "failed" bucket for a run that ended on an error with no outcome, the name telemetry already uses. The digest email words it "ended with an error" and places it right after timeouts. The This week card now does the same. When none of a week's investigations had an outcome the card has wording for, the Investigated tile still said "No issues needed a closer look" (or the watch-only sentence) under a non-zero count. Like the email, the tile now shows the count alone and keeps those sentences for a week with no investigations. |
||
|
|
5c7d2c6e67 |
Show unclassified and interrupted Patrol investigation outcomes
Since rcourtman/Pulse#3129 Patrol records two more investigation outcomes: unclassified, for a run that finished without a readable conclusion, and interrupted, for a run that a restart or shutdown cut short. The frontend's outcome lists did not know either value, so the findings store dropped both to no outcome. An unclassified run never reached Needs Attention and offered no Re-investigate, an interrupted row asked the operator to review it, the drawer showed the raw keys, and the This week card said "No issues needed a closer look" under five investigations. The store now keys the outcomes it accepts by the InvestigationOutcome union in api/patrol.ts, which api/ai.ts reuses, so the next outcome fails type-check instead of vanishing. Every presentation map gains both values. Unclassified reads "No clear conclusion" in the warning tone and joins the attention queue. Interrupted reads "Interrupted — will retry" in the neutral tone, its row says "Will retry", and its run note is no longer styled as an error. The This week card uses the digest email's wording and order. A browser pass on a Pro stack found two drawer defects the new labels exposed. The Patrol record showed a red Failed run status beside "will retry", and on phones the longer badge broke Re-investigate across two lines. Like the investigation header, the record now shows its run status only until the run has an outcome, its badge uses the shared outcome labels, and the header row wraps. Contract-Neutral: ai-runtime and api-contracts already record the unclassified and interrupted outcomes (#3129); the API clients only mirror them and the record badge reuses the shared outcome label. The Patrol presentation is documented in patrol-intelligence.md. |
||
|
|
ec40662af4 |
Record Patrol investigation conclusions as a typed tool call (#3129)
Patrol investigations inferred their outcome from prose markers, so a bold or bulleted RESOLVED line became the terminal needs_attention and the finding was never investigated again. Timeouts clustered on subscription agents. Patrol's 60s stream idle timeout bounds a whole buffered CLI turn there, three investigations queued on one serial CLI slot, and the final summary call was capped at 20s. - Add patrol_conclude_investigation, an investigation-only, side-effect-free capture of resolved, cannot_fix or needs_attention. It is carried on InvestigationRunResult and OrchestratorInvestigationResult. - After a conclusion, the next turn is the summary. The final turn offers only the tools that end a run. A deadline reserve makes the current turn final before the run times out, and the summary call may use the time left. - Give investigation turns on subscription agents a 4-minute turn budget, a 25-minute default timeout and one investigation at a time. These limits are republished on config reload without holding the service lock across the Patrol lock. - Derive Patrol's outer deadline from the effective timeout. Sessions now carry their own deadline, and the stuck-run sweep measures a live run against it, so neither a lowered timeout nor the previous run's time resets a running investigation. - Record a run orphaned by a restart as interrupted, not timed out. A first investigation cut short that way used to stay running for good, which also blocked a manual re-run. Recovery applies only while the finding still shows that run, takes a result the run's session already recorded, and keeps an action outcome such as fix_queued. - Add unclassified and interrupted outcomes, with lifecycle, telemetry bucket, digest and record handling. |
||
|
|
85fb199ff5 |
Merge main into feat/patrol-typed-conclusions
Main gained the Assistant retirement (#3126), which also appended notes to the agent-lifecycle, ai-runtime, api-contracts and storage-recovery contracts. Keep both: main's notes, then this change's. |
||
|
|
af89e4a7d5 |
Retire the Assistant chat window; alert Investigate runs Patrol (#3126)
## Why The in-app Pulse Assistant chat window was a bet that did not pay off: usage looked like curiosity rather than a daily tool, and it was shrinking. People who want to talk to their infrastructure already use Claude Code, Codex, or another MCP client, and Pulse MCP gives those agents the same governed tools. Keeping a second, weaker chat in the web app cost a launcher on every page, hand-off buttons on alerts, findings, approvals and drawers, and tens of thousands of lines of drawer code. ## What changes - **Gone from the web app:** - The Assistant drawer and its launcher (desktop and phone). - The command-palette Assistant commands. - Every "Open in Assistant", "Explain with Assistant", "Discuss" and "Ask Assistant" hand-off. These were on alerts, incident timelines, Patrol findings, approvals, run history, the attention workbench and resource drawers. - The Assistant settings page with its session maintenance section. - The chat store, the chat client, the dev stream fixture and the generated SSE types. - Helpers and tests that only those surfaces used. - **Alert Investigate runs Patrol.** On alert cards and History rows, Investigate starts a targeted Patrol run (`POST /api/ai/patrol/run` with the alert's resource, identifier and type). It confirms where the findings will appear and ignores repeat clicks while the request is in flight. The run route needs an admin with `ai:execute`. Security status therefore gains `settingsCapabilities.patrolRun`, and Investigate only appears where it is true. The field is derived from the same snapshot as the other admin capabilities, with no new token validation. Where that snapshot and the route disagree for org-scoped admins, organization-bound tokens and a cookie beside a token, it follows the route. - **External agents settings.** The page that hosts Pulse MCP setup is now **External agents** at `/settings/pulse-intelligence/agents`. The old `/settings/pulse-intelligence/assistant`, `?tab=assistant` and `system-ai-assistant` links redirect there with the hash kept. - **AI availability store.** `stores/aiAvailability.ts` holds the session `assistantEnabled` fact that gates Patrol entry points and Investigate. It now clears and refreshes on organization switch, refreshes after provider removal, and drops answers that arrive after a newer one. The old chat store did none of these. - **Chat backend unchanged.** Patrol investigations run on the chat service, and Pulse Mobile chat uses it until 31 March 2027, so `/api/ai/chat`, the session endpoints and the stream contract stay. A new contract snapshot pins `steer_applied`, which only the deleted TypeScript projection used to cover. - **Dependencies.** `highlight.js` is removed (only the chat's code highlighter used it). - **Governance.** - Subsystem contracts, `registry.json` and the readiness proofs drop the retired surfaces. - ai-runtime loses about 1,600 lines of drawer rules and keeps every backend session, stream and provider rule. - `docs/AI.md` points chat users at MCP. The production build is about 100 kB of gzipped JavaScript smaller. The `Chat`, `vendor-highlight` and `aiControlLevelPresentation` chunks are gone. ## Not in this PR - Backend endpoints that only the web chat called. Which endpoints Pulse Mobile still needs must be checked first. - The "Pulse Assistant" surface entry in the agent capabilities manifest, which also generates the overview block at the top of `docs/AI.md`. That surface still describes the chat engine Pulse Mobile uses, so it changes together with the endpoint cleanup. - The dead `highlight.js` chunk rule in `vite.config.ts`. - Landing-page copy in pulse-pro. ## Release note > Pulse Assistant, the chat window inside the web app, is retired. To ask about your infrastructure in plain language, connect Claude Code, Codex, or another MCP client under **Settings → Pulse Intelligence → External agents**. On an alert, **Have Patrol investigate** now starts a Patrol check on that resource, and the findings appear on the Patrol page. Pulse Mobile's chat keeps working until the app retires on 31 March 2027. |
||
|
|
af97a87b47 |
Bump dompurify and vitest past the 22 October advisories (#3128)
## Why `npm audit` now fails the required **Frontend** check on every branch. Advisories published on 22 October cover dompurify ≤3.4.16 (GHSA-cg2g-xh64-v4q7 critical, GHSA-mrv2-3pqr-j5jr, GHSA-c6gr-qvv4-5v3f) and @vitest/mocker <4.1.12 (GHSA-qxg6-89mg-rqv8, GHSA-xp26-3454-mjgp). ## What changes - `frontend-modern/package.json`: dompurify `^3.4.17`, vitest and @vitest/coverage-v8 `^4.1.12`. - `package-lock.json`: only what those updates resolve. That is the vitest family; the chai, obug, std-env and tinyrainbow versions its ranges now pick; and a single picomatch 4.0.7 instead of the nested copies. Newer npm's bundled wasm entries for `@tailwindcss/oxide-wasm32-wasi` are left out, as on main. - `dependencySecurity.test.ts`: the vitest floor moves to 4.1.12 and the dompurify floor to 3.4.17, with the advisory IDs. `npm audit` reports 0 vulnerabilities after a clean install, and the dependency security and markdown sanitizer tests pass. Dependabot's group PR #3058 includes these bumps among unrelated ones and has been blocked since 21 October. |
||
|
|
b3b5ad6e1f |
Record Patrol investigation conclusions as a typed tool call
Patrol investigations inferred their outcome from prose markers, so a bold or bulleted RESOLVED line became the terminal needs_attention and the finding was never investigated again. Timeouts clustered on subscription agents. Patrol's 60s stream idle timeout bounds a whole buffered CLI turn there, three investigations queued on one serial CLI slot, and the final summary call was capped at 20s. - Add patrol_conclude_investigation, an investigation-only, side-effect-free capture of resolved, cannot_fix or needs_attention. It is carried on InvestigationRunResult and OrchestratorInvestigationResult. - After a conclusion, the next turn is the summary. The final turn offers only the tools that end a run. A deadline reserve makes the current turn final before the run times out, and the summary call may use the time left. - Give investigation turns on subscription agents a 4-minute turn budget, a 25-minute default timeout and one investigation at a time. These limits are republished on config reload without holding the service lock across the Patrol lock. - Derive Patrol's outer deadline from the effective timeout. Sessions now carry their own deadline, and the stuck-run sweep measures a live run against it, so neither a lowered timeout nor the previous run's time resets a running investigation. - Record a run orphaned by a restart as interrupted, not timed out. A first investigation cut short that way used to stay running for good, which also blocked a manual re-run. Recovery applies only while the finding still shows that run, takes a result the run's session already recorded, and keeps an action outcome such as fix_queued. - Add unclassified and interrupted outcomes, with lifecycle, telemetry bucket, digest and record handling. |
||
|
|
f330c76481 |
Merge the dompurify and vitest bump into the Assistant retirement
Both branches edit the frontend manifest, lockfile and dependency floor list: this branch drops highlight.js, the bump raises dompurify and vitest past the 22 October advisories that fail the required audit. Each conflict keeps the bump's versions without highlight.js. |
||
|
|
2d0232ce69 |
Bump dompurify and vitest past the 22 October advisories
npm audit started failing the required Frontend check on every branch once GHSA-cg2g-xh64-v4q7 (critical, SAFE_FOR_TEMPLATES bypass), GHSA-mrv2-3pqr-j5jr and GHSA-c6gr-qvv4-5v3f were published against dompurify <=3.4.16, and GHSA-qxg6-89mg-rqv8 and GHSA-xp26-3454-mjgp against @vitest/mocker <4.1.12. dompurify moves to 3.4.17, vitest and @vitest/coverage-v8 to 4.1.12, and the dependency security test raises its floors to match. The deployment-installability contract records the reviewed floors, as the 30 September DOMPurify bump did. The lockfile takes only what those updates resolve: the vitest family, the chai, obug, std-env and tinyrainbow versions its ranges now pick, and one deduplicated picomatch 4.0.7 in place of the nested copies. The bundled wasm entries newer npm writes for @tailwindcss/oxide-wasm32-wasi are left out, as they are on main. Dependabot's group PR 3058 carries these bumps among others but has been blocked on unrelated failures since 21 October. |
||
|
|
1d0f7f1f78 |
Keep record drawer headings whole on phones (#3127)
Keep record drawer headings whole on phones
A record drawer's owner link repeated the object's name next to the
heading that already shows it. On a phone the button took about half
the header, so a long heading such as a Kubernetes pod's broke mid-word
across four lines ("paymen / ts-worker- / 58484d44f7 / -f6gzh").
The link now reads "Open" below the sm breakpoint and "Open <name>"
above it, keeping the full label as its accessible name and tooltip.
On a 390px phone the button drops from 192px to 70px and the pod
heading wraps once, at a hyphen. Desktop is unchanged.
|
||
|
|
89f8de5b65 |
Pin the MCP-first AI overview in the docs contract test
The Pulse Intelligence overview contract still required docs/AI.md to say the Assistant is the in-app Pro surface beside pulse-mcp. That paragraph went with the chat window, so the test now pins what the doc says instead: Pulse has no in-app chat window, MCP clients connect through pulse-mcp, and capabilities land in the canonical manifest first. |
||
|
|
cd514df7f9 |
Keep record drawer headings whole on phones
A record drawer's owner link repeated the object's name next to the
heading that already shows it. On a phone the button took about half
the header, so a long heading such as a Kubernetes pod's broke mid-word
across four lines ("paymen / ts-worker- / 58484d44f7 / -f6gzh").
The link now reads "Open" below the sm breakpoint and "Open <name>"
above it, keeping the full label as its accessible name and tooltip.
On a 390px phone the button drops from 192px to 70px and the pod
heading wraps once, at a hyphen. Desktop is unchanged.
|
||
|
|
28172a907b |
Retire the Assistant chat window; alert Investigate runs Patrol
Pulse Assistant was a chat drawer beside the monitoring UI. Operators who want to talk to their infrastructure already use Claude Code, Codex, or another MCP client, and Pulse MCP gives those agents the same governed tools. Keeping a second, weaker chat in the web app cost a 66 kB chunk, a launcher on every page, hand-off buttons on alerts, findings, approvals and drawers, and thousands of lines of OpenCode-parity drawer code, for a feature the maintainer judged ineffective. This removes the browser chat and everything that only fed it: - The drawer, its launcher (desktop and phone), the command-palette Assistant commands, the Escape and blocking-dialog shell plumbing, and the chat store, client, dev stream fixture and generated SSE types. - Every "Open in Assistant", "Explain with Assistant", "Discuss" and "Ask Assistant" hand-off on alerts, incident timelines, Patrol findings, approvals, run history, the attention workbench and resource drawers, with their context models and the chat-only helpers they used. - The Assistant settings page and its session maintenance section. The page becomes "External agents" at /settings/pulse-intelligence/agents; the old /assistant path, ?tab=assistant and system-ai-assistant redirect there with the hash kept. Alert Investigate now starts a targeted Patrol run (resource, alert ID and type) and says where the findings land, and it ignores repeat clicks while the request is in flight. The run route needs an admin with ai:execute, so security status gains settingsCapabilities.patrolRun, derived from the same snapshot as the other admin capabilities but following the route for org-scoped admins, organization-bound tokens and a cookie beside a token, and Investigate is offered only where it is true. The Patrol tool transcript block moves to components/patrol. A small aiAvailability store holds the session assistantEnabled fact that gates Patrol entry points and Investigate; it now clears and refreshes on organization switch, refreshes when organization loading settles on another org and after provider removal, and ignores answers that arrive after a newer one. The backend chat service stays: Patrol investigations run on it and Pulse Mobile chat uses it until 31 March 2027, so /api/ai/chat, the session endpoints and the stream contract are unchanged. A new contract snapshot pins the steer_applied event that only the deleted TypeScript projection used to cover. highlight.js, used only by the chat's code highlighter, leaves the dependencies, as does the DOMPurify carve-out that kept its fence hints. Contracts, registry and readiness proofs drop the retired surfaces; the ai-runtime contract loses about 1,600 lines of drawer rules while keeping every backend session, stream and provider rule. The production build ships about 100 kB less gzipped JavaScript. |
||
|
|
86ff402d11 |
Let Docker and vSphere host rows show their workloads (#3124)
Let Docker and vSphere host rows show their workloads On the Proxmox overview a node row narrows the guest table to that node and the chevron opens the node's drawer. Docker and vSphere host rows opened the drawer instead, so seeing one host's containers or VMs meant scrolling past every other host's group or finding the Host filter. Docker and vSphere host rows now work like Proxmox nodes: a click narrows the containers or VMs to that host, titles the table "...on <host>" with Show all hosts, marks the row and moves focus to the heading; a second click clears it. The chevron opens the drawer and stays visible on phones, where the host column takes the width Uptime did not need so names stay readable. Docker scopes through the ?host= filter its containers table already read; every host now stays listed and the scoped one is marked, where that filter used to hide the others (the Docker link from a VM drawer lands the same way). A vSphere host scopes to the scope the VM table already gave its own VMs, matched within its vCenter connection, so host and VM rows cannot disagree. The name button, scoped heading and focus handling move into shared PlatformHostScope pieces that the Proxmox overview now uses too. The Docker Swarm tab's host table has nothing under it to scope and keeps opening the drawer on a row click. |
||
|
|
3745f4738d |
Let Docker and vSphere host rows show their workloads
On the Proxmox overview a node row narrows the guest table to that node and the chevron opens the node's drawer. Docker and vSphere host rows opened the drawer instead, so seeing one host's containers or VMs meant scrolling past every other host's group or finding the Host filter. Docker and vSphere host rows now work like Proxmox nodes: a click narrows the containers or VMs to that host, titles the table "...on <host>" with Show all hosts, marks the row and moves focus to the heading; a second click clears it. The chevron opens the drawer and stays visible on phones, where the host column takes the width Uptime did not need so names stay readable. Docker scopes through the ?host= filter its containers table already read; every host now stays listed and the scoped one is marked, where that filter used to hide the others (the Docker link from a VM drawer lands the same way). A vSphere host scopes to the scope the VM table already gave its own VMs, matched within its vCenter connection, so host and VM rows cannot disagree. The name button, scoped heading and focus handling move into shared PlatformHostScope pieces that the Proxmox overview now uses too. The Docker Swarm tab's host table has nothing under it to scope and keeps opening the drawer on a row click. |
||
|
|
904f1104be |
Let Coverage restore evidence details wrap (#3121)
Let Coverage restore evidence details wrap
The Coverage drawer's restore evidence ran long snapshot descriptions
("checkout-web-02 local rollback snapshot") straight past the drawer
edge with no tooltip. The cells asked for whitespace-normal, but the
global `.table-fixed td` rule forces nowrap and outranks a utility
class on the cell, so the request did nothing.
Put the wrap on each value's span instead, the way the shared drawer
detail tables already do, and pin it in the Coverage test.
|
||
|
|
177325cb03 |
Let Coverage restore evidence details wrap
The Coverage drawer's restore evidence ran long snapshot descriptions
("checkout-web-02 local rollback snapshot") straight past the drawer
edge with no tooltip. The cells asked for whitespace-normal, but the
global `.table-fixed td` rule forces nowrap and outranks a utility
class on the cell, so the request did nothing.
Put the wrap on each value's span instead, the way the shared drawer
detail tables already do, and pin it in the Coverage test.
|
||
|
|
863bdbfbec |
Wrap drawer sentences and open fleet details under the tile (#3120)
Wrap drawer sentences and open fleet details under the tile
Two leftovers from the drawer consistency pass.
TrueNAS pool and disk drawers cut their summary sentences off with an
ellipsis ("ZFS pool archive is DEGRADED and ..."), so the one line that
explains the pool's state was the one a user could not read. Those
rows, and vSphere's hardware upgrade and Tools install errors, now
wrap; short values stay on one line.
The availability fleet view opened its drawer below the whole grid, so
clicking a tile near the top of a large fleet showed nothing nearby.
The drawer is now a full-width grid item right after the tile it
belongs to, so it opens beneath that tile's row. The fleet list is
reconciled by id so live updates refresh the open drawer instead of
remounting it, and closing it returns focus to the tile.
|
||
|
|
30024b0f85 |
Wrap drawer sentences and open fleet details under the tile
Two leftovers from the drawer consistency pass.
TrueNAS pool and disk drawers cut their summary sentences off with an
ellipsis ("ZFS pool archive is DEGRADED and ..."), so the one line that
explains the pool's state was the one a user could not read. Those
rows, and vSphere's hardware upgrade and Tools install errors, now
wrap; short values stay on one line.
The availability fleet view opened its drawer below the whole grid, so
clicking a tile near the top of a large fleet showed nothing nearby.
The drawer is now a full-width grid item right after the tile it
belongs to, so it opens beneath that tile's row. The fleet list is
reconciled by id so live updates refresh the open drawer instead of
remounting it, and closing it returns focus to the tile.
|
||
|
|
2130d8962b |
Stop release CI depending on Microsoft's package server (#3118)
Stop release CI depending on Microsoft's package server GitHub's Ubuntu runner image ships an apt source for packages.microsoft.com. Every job that runs `apt-get update` therefore fails when that server does. On 22 October it returned 403 Forbidden for about an hour, failing the required Repo Boundary Audit on the v6.6.0-rc.6 preparation twice, so the release train abandoned the candidate and will only retry 12 hours later. None of these jobs installs anything from that source: the audit needs ripgrep, the release build zip/unzip, the dry run docker-compose and the browser qualification jq/curl/ssh plus Playwright's dependencies, all from Ubuntu. Before refreshing apt, remove only source files that name packages.microsoft.com. A runner without one is left alone, and a cleanup failure never fails the step. Contract-Neutral: CI runner package-source hygiene only; no runtime, release artifact or public contract change. |
||
|
|
de2c9729c2 |
Let TrueNAS record drawers open the object they are about (#3119)
Record drawers on the other platforms link to their owner ("Open sdc")
wherever the owner's page can narrow to it. TrueNAS tabs only read a
storage kind filter, so its alert and protection drawers had no link:
a link that lands on a whole tab leaves the user hunting for the row.
TrueNAS tabs now keep their table search in the URL (?q=), the way the
Kubernetes and Machines pages do. The page-path helper emits it for
TrueNAS pools, datasets, disks, apps, VMs and shares (not for a disk
named only by an alert, which may not match a row), so the alert and
protection drawers gain the owner link and land on the tab already
narrowed, with the filter visible in the search box. Systems keep an
unfiltered link-free Overview because that table has no search box.
Also:
- The Coverage drawer's restore evidence wraps long snapshot and
archive names instead of cutting them; table rows still use one line.
- The disk-live-refresh browser proof waited for the full "Replace Now"
verdict on the phone, where the health column shows "Replace"; it now
waits for the label the layout shows, and passes on phone WebKit.
|
||
|
|
3704f006f3 |
Let TrueNAS record drawers open the object they are about
Record drawers on the other platforms link to their owner ("Open sdc")
wherever the owner's page can narrow to it. TrueNAS tabs only read a
storage kind filter, so its alert and protection drawers had no link:
a link that lands on a whole tab leaves the user hunting for the row.
TrueNAS tabs now keep their table search in the URL (?q=), the way the
Kubernetes and Machines pages do. The page-path helper emits it for
TrueNAS pools, datasets, disks, apps, VMs and shares (not for a disk
named only by an alert, which may not match a row), so the alert and
protection drawers gain the owner link and land on the tab already
narrowed, with the filter visible in the search box. Systems keep an
unfiltered link-free Overview because that table has no search box.
Also:
- The Coverage drawer's restore evidence wraps long snapshot and
archive names instead of cutting them; table rows still use one line.
- The disk-live-refresh browser proof waited for the full "Replace Now"
verdict on the phone, where the health column shows "Replace"; it now
waits for the label the layout shows, and passes on phone WebKit.
|
||
|
|
0149b8a85f |
Stop release CI depending on Microsoft's package server
GitHub's Ubuntu runner image ships an apt source for packages.microsoft.com. Every job that runs `apt-get update` therefore fails when that server does. On 22 October it returned 403 Forbidden for about an hour, failing the required Repo Boundary Audit on the v6.6.0-rc.6 preparation twice, so the release train abandoned the candidate and will only retry 12 hours later. None of these jobs installs anything from that source: the audit needs ripgrep, the release build zip/unzip, the dry run docker-compose and the browser qualification jq/curl/ssh plus Playwright's dependencies, all from Ubuntu. Before refreshing apt, remove only source files that name packages.microsoft.com. A runner without one is left alone, and a cleanup failure never fails the step. Contract-Neutral: CI runner package-source hygiene only; no runtime, release artifact or public contract change. |
||
|
|
7737469045 |
Release v6.6.0-rc.6 (#3115)
Release preparation for `v6.6.0-rc.6`. After this merges, the release workflow runs at this exact head `63d35086b19ebd76043ddbad3e246d42826c1d66`. |
||
|
|
df7a9004bd |
refactor/drawer cleanup (#3117)
- **Build every record drawer on the shared record panel** - **Record the Kubernetes chunk size and new record drawer labels** - **Finish moving the last bespoke drawers onto the shared shape** |
||
|
|
b08fbd8a24 |
Match the Mail Gateway window check to the shared drawer
The PMG help says to compare the same time window in PMG, and its docs check pinned the old drawer's `stats()?.timeframe` read. The Mail tab is now headless mail-flow content inside the shared resource drawer and names the window on its Mail flow card from the same field (`stats?.timeframe`); the check now accepts either spelling, so the help still has to describe a window the drawer actually shows. |
||
|
|
c58668b610 |
Build every record drawer on the shared record panel (#3114)
Alerts, events, tasks, services and backup rows open a "record" drawer: something that happened to an object, not an object to manage. Many of them were hand-built grids that looked like a different product: no heading, no status, no way to close from inside, raw timestamps, labels cut to three, and no link to the object they were about. - The five Docker Swarm tables, Docker networks and storage usage, Proxmox replication and coverage now open the shared record panel: the subject's name and the row's own status dot, a one-line summary, wrapping sections with human dates, and an "Open <name>" link where the app can focus the owning object (and none where it would only land on a whole tab). - Recoverable restore points had no drawer, and on phones lost their location entirely; they now open one. - Kubernetes events, ConfigMaps, Secrets and RBAC objects opened the full object drawer, which never showed an event's message and offered maintenance windows on it. They open record drawers instead; a Secret's key names and values are never shown. - Coverage allowed several rows open at once; it now opens one, like every other table. - Agent Doctor targets open one at a time with a proper header, shared notices and a closed Details for connection evidence. - Availability fleet tiles behave as disclosures and open the same table-row drawer as the table view. |
||
|
|
4aa3d7aa5f |
Finish moving the last bespoke drawers onto the shared shape
The earlier drawer changes left a few surfaces that still looked or behaved differently, plus code nothing used any more: - Proxmox Mail Gateway had two drawers for one gateway; the row now opens the shared resource drawer with mail flow as its Mail tab. The Ceph cluster drawer gains the canonical header, Overview (alerts, Worth checking, capacity history, cards, closed Details) and Manage, and its dot follows the row instead of reading grey on missing health. - The Kubernetes cluster drawer's Deployments tab repeated the Workloads table with dead-end rows; it is gone, and Namespaces opens the Workloads tab scoped to the cluster and namespace with one action. The Docker Swarm drawer tab could not be reached from any page; gone. - On phones, windowed tables (Kubernetes deployments, pods) and the Storage tab's grouped pools stopped at 70-90% of the width: a colspan=100 spacer or colspan=99 group row created phantom columns that took the width hidden columns left. Those rows now span exactly the visible header cells. - The node and Docker host drawers gain the guest drawer's Open action; the Docker host withholds readings while offline or stale, lists operator state first in Manage and shows its action history. A guest's physical disks open the same disk Overview as the Storage tab, with the guest agent's disk temperature alerts. - One "No active alerts" line, one timestamp row and wrapping alert subjects replace per-drawer copies; PBS and PMG alerts match on their instance id so drawers find them without a metrics target. - Removes the old disk detail and its presentation code, the Swarm and Kubernetes Deployments drawers and their presentation helpers, and drawer state nothing read. |
||
|
|
63d35086b1 |
Prepare v6.6.0-rc.6
Release candidate cut from main at
|
||
|
|
345babc6a8 |
Record the Kubernetes chunk size and new record drawer labels
The Kubernetes events, ConfigMap/Secret/RBAC and policy rows now build record drawers in the page chunk, which measures 21732 bytes gzipped on a fresh build of this branch. Its recorded baseline (18267) predated earlier growth on the Kubernetes tabs, so the chunk crossed its limit by 0.71 kB. Lazy-loading the record panels was tried and made the chunk larger (their shared pieces already live in common chunks), so this records the measured size and leaves every other chunk's baseline unchanged. Two docs checks pinned the old hand-written markup: replication help matched `>Guest</dt>`-style labels, and Agent Doctor help matched a "· Privilege:" sentence fragment. The same labels are now shared detail rows and section labels; the checks match those, so the help still has to name labels the drawers actually show. |
||
|
|
750292e21d |
Build every record drawer on the shared record panel
Alerts, events, tasks, services and backup rows open a "record" drawer: something that happened to an object, not an object to manage. Many of them were hand-built grids that looked like a different product: no heading, no status, no way to close from inside, raw timestamps, labels cut to three, and no link to the object they were about. - The five Docker Swarm tables, Docker networks and storage usage, Proxmox replication and coverage now open the shared record panel: the subject's name and the row's own status dot, a one-line summary, wrapping sections with human dates, and an "Open <name>" link where the app can focus the owning object (and none where it would only land on a whole tab). - Recoverable restore points had no drawer, and on phones lost their location entirely; they now open one. - Kubernetes events, ConfigMaps, Secrets and RBAC objects opened the full object drawer, which never showed an event's message and offered maintenance windows on it. They open record drawers instead; a Secret's key names and values are never shown. - Coverage allowed several rows open at once; it now opens one, like every other table. - Agent Doctor targets open one at a time with a proper header, shared notices and a closed Details for connection evidence. - Availability fleet tiles behave as disclosures and open the same table-row drawer as the table view. |
||
|
|
e67fff71d5 |
Bring storage and platform drawer content up to the guest drawer (#3113)
The Proxmox guest and node drawers lead with what is wrong, chart the last day and keep the long tail in one closed Details. Other drawers did not: - Storage pool and physical-disk details on the Proxmox Storage tab had no heading, status dot or collapse control, no alerts line or Worth checking, a pool chart defaulting to 7 days with its own picker, a disk "History" and "Live I/O" section, and every config, topology and SMART table open by default. Both are now object drawers in the shared detail row, with Overview (alerts, Worth checking, 24-hour charts, facts, ZFS/SMART cards, closed Details) and Manage (operator state). A disk alert that already states the verdict is not repeated beneath it, and worn flash is stated once by the verdict. - TrueNAS ZFS errors and disk risk, vSphere datastores that are not accessible, Kubernetes containers that are not ready and cordoned nodes sat two or three closed disclosures deep and never reached Worth checking. Platform sections now join the same split as every other drawer: warnings lift, cards show, the rest waits in Details. - Kubernetes Deployments, autoscalers, Services, Ingresses and volumes had no section at all, so columns hidden on phones were unreachable. Docker image and volume drawers showed nothing about the image or volume. No field is dropped; it moves to where the guest drawer keeps it. |