Every commit touching user-visible frontend source rewrote the single
frontend-modern/browser-verification.json, so any two open frontend pull
requests conflicted as soon as either merged. A merge could not repair
the loser: concluding it staged main's frontend files as if the author
had changed them, and the guard then demanded a receipt for files the
author never ran. The only honest recovery was a rebuild on the new main
and a fresh browser pass, as #2415 -> #2418 -> #2433 showed on 2-3
October.
Each commit now adds its own receipt under
frontend-modern/browser-verification/, named by the fingerprint of the
content it verified. Receipts are only ever added, so git merges them
unaided. The guard keeps every binding it had: the commit's own parent,
the exact changed paths and their content digests, a desktop and a
narrow viewport, non-empty routes, states and interactions, and the
range check that each changed file's final content matches a receipt
from a non-merge commit in the range.
Concluding a merge no longer counts content a merged parent already
carries as the commit's own change. Merged content that neither parent
holds is flagged and still needs a follow-up receipt in range mode, and
a merge commit may not record one.
The shared file is retired: no commit may write it again. A commit that
modified it while its own parent still carried it stays valid evidence
in range mode, so a pull request that still rewrites it is repaired by
merging main and taking main's copy rather than by a rebuild. The file
is deleted in a change of its own, because deleting it here would
conflict with every pull request that still rewrites it.
--write-template writes the skeleton at the path the staged content
requires. --prune deletes receipts already on main. A change that
records a receipt may not delete one: git pairs the two as a rename,
which conflicts with any other change deleting the same receipt.
## What changed and why
Alert cleanup now retires an expired flapping episode together with its
suppression deadline. Previously either cleanup owner could remove the
deadline but leave the episode latched, suppressing notifications
without a newly bounded cooldown. Active alert identity, unexpired
suppression and ordinary repeat-notification cooldown remain unchanged.
TrueNAS troubleshooting now distinguishes stale polling, missing
telemetry, authentication failures and session turnover. The existing
guides preserve polling evidence before a manual connection test updates
timestamps, use the actual 60-second default and avoid credential resets
or destructive reproductions. This addresses the diagnostic gaps exposed
by #2382, #2396 and #2400; it does not claim those native incidents are
all resolved.
## Reviewed source included
Complete unpublished range:
`0e90236d3bb901dc84d4d07e70d4d2a09d5e6705..971ebde8afdc0f55dd0ea77a0358467caf62ea73`
(five commits, including additive integration history).
- TrueNAS guide candidate `4b0f23e0a7a7d03df391f83896cbf4b48657bd24` (2
October).
- Alert cleanup candidate `d813c936e464eb09bb9ae7e6c7ac33716acde378` (2
October).
- Integration tips `e9294420`, `71382de0`, `971ebde8` preserve the
original candidates and published main ancestry without rewriting them.
## Validation
- Accepted alert candidate evidence: six parent-failing cleanup cases
pass on final; full alerts correctness/races, ten focused race repeats,
notification correctness, library vet/build and 132 governance checks
pass. The integrated alert files are identical to that candidate. These
proofs used Go 1.26.7; declared Go 1.26.8 CI remains required.
- Accepted guide evidence: seven guide checks, three polling controls
and 25 Docs/viewer tests; desktop Chromium and phone WebKit
navigation/readability checks. Integrated guide/test files are unchanged
from that candidate.
- Exact combined head: seven guide checks pass in offline source proof
`source-x4fm8fxy`, exit 0, complete output and completed guest shutdown.
The preceding confined lightweight attempt could not create loopback
sockets (five errors); it is an execution-context limitation, not a
pass.
- Publication provenance and whitespace checks pass; live main matches
the range base.
Required exact-head CI and independent source review still gate landing.
Real-duration alert queue/HTTP acceptance remains with its existing
owner after an interrupted proof; native monitoring acceptance and
containing-release delivery remain separate. No release, tag or
deployment is changed by this PR.
[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
TableCell and TableHead composed `px-2 sm:px-3 py-*` ahead of the
caller's class, but Tailwind emits every padding utility at the same
specificity in spacing-scale order, so a caller's `px-1` lost to the
base `px-2` and `sm:px-1.5` lost to `sm:px-3` wherever it sat in the
string. Measured on 2026-10-02: DiskList cells carried 16px of
horizontal padding on a 390px phone although their class asked for
8px, and 24px at 640-1023px although it asked for 12px; the Workloads
header cells sat at 8/12px above data cells at 6/8px; the platform
helper's `px-1.5 sm:px-2` had never taken effect on a cell since it
was introduced in May. PR #2409 and PR #2418 worked around it with
layout-mode `!px-1` overrides.
Apply the convention TableHeader already uses for borders and
TableBody for dividers to padding: when the caller's class names a
side's padding (`p-*`, `px-*`, `pl-*`, `pr-*`, `py-*`, `pt-*`, `pb-*`,
with or without `!`), the base padding for that side is left out, so
the caller's utility is the only one on the element. Sides the caller
leaves alone keep the base, so a `pr-2` column still gets its left
inset, and prefixed-only variants such as `lg:px-0` keep layering on
the base as before. The base is unchanged when nothing is passed, and
the class is resolved inside the JSX attribute so layout-mode class
switches stay reactive.
Platform tables pick up the helper's declared 6/8px cell padding,
which lines data cells up under the 6px headers the stylesheet already
pins, and their header rows drop to the helper's 2px vertical padding.
Both cleanup owners removed only the deadline, leaving a latched episode that could suppress notifications without a new cooldown. Share dispatch expiry with cleanup, preserve unexpired policies and active occurrence identity, and verify renewed delivery, bounded rearming, diagnosis and one-shot callbacks without bypassing ordinary repeat-notification cooldown.
Change-source: pulse-maintainer
Preserve existing polling evidence before a connection test updates its ledger. Explain freshness, optional telemetry and session turnover using the read-only projection, correct the 60-second default, and keep diagnostic steps free of credentials and destructive reproductions.
Contract-Neutral: Documentation and existing-guide checks only; no runtime, API, transport, identity, access or retention change.
Change-source: pulse-maintainer
Retain both History/storage and Reporting contracts; keep the upstream browser receipt unchanged. Earlier accepted receipts remain in history, with runtime blobs preserved.
Change-source: pulse-maintainer
Recognise matching notify_unsubscribed events before and after acknowledgement, preserve unrelated streams and clean log completion, and retain only numeric errno with fixed diagnostics. Keep nonempty Apps inventory and poll-ledger progress while rejected stats recover on the next ordinary poll. The reporter supplied a source-derived envelope, not a retained capture; native acceptance and delivery remain separate.
Change-source: pulse-maintainer
The patrol_digest report schedule kind has been on main since early
September, but only the API could create one: the Settings > Reporting
form had no way to choose it, so the weekly "what Patrol did" email never
reached the customers it was built for, the ones who have stopped opening
Pulse. The schedule form now has a Report type choice between the
performance report and the Patrol weekly summary. The choice shows only
while the session reports Patrol can run (AI on with a provider), and an
existing summary schedule keeps showing its type so it stays editable
after AI is switched off.
Choosing the summary reduces the form to a name, weekday, time, timezone
and optional recipients. Cadence is pinned to weekly and delivery to
email, and the format, resource picker, tag filter, retention, attach and
save-copy controls are hidden because the server fixes those values; the
save path no longer demands a resource scope for this kind. The schedules
table describes a summary as "Patrol activity, last 7 days". Model, hook
and panel tests cover the gating, the pinned fields and the saved
payload, and the browser receipt records a Pro-licensed pass at 1280 and
375 pixels, including a run without an email destination that surfaces
the server's message naming the missing setting.
The existing drawer fetches all metrics without a metric query field. Explain metrics-object and single-metric points responses so readers do not wait for an absent CPU request or mistake missing CPU and measured zero for the same result.
Contract-Neutral: Clarify existing History diagnostics and protect mirrored documentation; no runtime, API, identity, access, retention or response-shape change.
Change-source: pulse-maintainer
## What changed and why
Reduce configuration-fetch audit overhead without hiding access events
(#2320). The suppression tracker now remembers at most 4,096
organisation/agent pairs and uses an expiry index instead of repeated
full-map scans. Existing agents keep daily suppression; unremembered
agents at capacity are conservatively audited on every fetch.
Token/configuration changes and every failure remain audited. No audit
history is deleted.
The Apprise confidentiality test now observes the autonomous queue
dispatcher instead of starting a competing manual batch. Its lifecycle,
retry, configuration and confidentiality assertions remain intact.
Preserve the exact 6.4.6 release-line mapping for the already-published
packet. The fallback to main incorrectly rejected its immutable source
during convergence. This maps that version only, not future patches, and
changes no candidate product source.
## Included source and upstream reconciliation
Complete new publication range:
`e6540da45da4de4bcb954463373d8ea9f196eb56..35d8c7c13d3665b2ffe2fe4fd61e8952c46e21b1`.
- Reviewed Delivery correction:
`3578b0044740087e7589f19bca450245f610aced`.
- Reviewed Core changes: `e225bf2abcb4189dcedd457292248e72a945dc74` and
`6ab5383412f156f286c38623f9b86f2b0d3151f0`, integrated by
`94ce8496b7d79e9b77d43493adc432c954d5e8b7`.
- Additive upstream merge: `35d8c7c13d3665b2ffe2fe4fd61e8952c46e21b1`,
incorporating published Proxmox table repairs at
`e6540da45da4de4bcb954463373d8ea9f196eb56`.
Every reviewed commit identity and ancestry is preserved. No upstream
frontend source or dependency manifest changes in this proposal.
## Validation and limits
Recorded independent-review evidence: Core returned four complete
source-proof receipts covering connected
configuration-fetch/authorization tests, five race repeats,
notifications correctness/races and ten queue-fixture repeats,
vet/builds, 135 governance tests, 23 docs-sync tests and frontend
production build/budgets. Its fixed controls retained a maximum of 4,096
keys versus 12,288 in the predecessor; saturated in-memory lookup
measurements are not installed write-rate or CPU acceptance.
Delivery returned a complete offline source proof of 947 release-control
tests (8 skipped), with guest shutdown. The exact-version tests reject
unrelated versions. These are reviewed owner returns, not new
qualification executions by this publication gate.
On the combined committed source, provenance, whitespace, ancestry,
canonical completion, browser-verification scope, control-plane audit,
all 62 shipped-document mirrors and the 211-file public-document check
pass. Core runtime and Go manifests are byte-identical to the reviewed
Core candidate; release-control source/policy is byte-identical to the
reviewed Delivery candidate; frontend runtime and graph are
byte-identical to current upstream. Sufficient heavy/browser evidence
was therefore not replayed.
Core used Go 1.26.7 rather than declared 1.26.8, with an explicit
backend embed fixture and a separately proved frontend build. Required
exact-head CI remains necessary before landing. This proposal does not
establish installed outcomes, release readiness, successful convergence
or stable delivery, and does not replace or recut the published v6.4.6
candidate.
[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
Accept exact candidate 34f73590bb with retained source and browser proof. Resolve only the shared receipt pointer; runtime content remains exact to its non-merge proof commits.
Change-source: pulse-maintainer
The Workload Controllers table printed Job and CronJob timestamps
exactly as the agent reports them, so at 1440px the Detail cell read
"Last success: 2026-10-02T19:59:14.523639+01:00" in a 119px column
and was cut off before the time. A user could not tell when the job
last ran.
The cell now says "Last success 31m ago" or "Completed 2h ago" through
the shared platform relative-time formatter, with the absolute local
time on hover, matching how the Events table shows its observed time.
The Detail column takes 12% instead of 9% so every label the column
can produce fits whole, taken from the Kind, Current and Available
columns which hold short values. The Detail span truncates to the cell
with an ellipsis instead of being cut at the cell edge.
Contract-Neutral: presentation of existing Kubernetes controller time
fields only; owning unified-resources clause updated; no
frontend-primitives clause, primitive, or API change
Repeated device paths and UnRAID group names must not attach foreign SMART, temperatures or topology counts to a pool. Preserve canonical children, require host or scoped native ownership for inferred membership, and compare exact device identifiers. Cover live target changes and real desktop/phone presentations.
Change-source: pulse-maintainer
Avoid a needless subscription when Apps has no inventory, preserve second-attempt stream cleanup and bounded log-tail completion, and record only classified local socket-close causes without private wire data. This repairs the empty-inventory path in #2396; the exact rejection envelope and the cause of #2400 remain unconfirmed.
Change-source: pulse-maintainer
The mock generator stamped each job's last and next sync once at
startup, with the last sync up to an hour old on a 15-minute schedule.
Most jobs therefore started out overdue, and on a long-running demo
every job read as hours overdue in red while still reporting Healthy.
The demo's Replication tab looked broken to anyone evaluating Pulse.
Replication timing now follows the fixture clock. Each job keeps a
stable phase inside its schedule window, so a healthy job last synced
less than one interval ago and is next due in the future, and a
failing job's last good sync falls behind by its failure count.
The target node was picked by index and could equal the source, which
showed a job replicating pve1 to pve1. The source is no longer a
candidate, and an estate without a second node gets no jobs.
Preserve candidate 7c48430d5a and its exact runtime bytes alongside the reviewed main frontier. Resolve only the shared browser receipt file to the candidate receipt; original non-merge receipts remain the integration evidence.
Change-source: pulse-maintainer
Preserve the reviewed local tip and incorporate current upstream additively. The frontend/table changes are disjoint from the audit, Apprise fixture and exact-version release mapping corrections.
Change-source: pulse-maintainer
The Workload Controllers table printed Job and CronJob timestamps
exactly as the agent reports them, so at 1440px the Detail cell read
"Last success: 2026-10-02T19:59:14.523639+01:00" in a 119px column
and was cut off before the time. A user could not tell when the job
last ran.
The cell now says "Last success 31m ago" or "Completed 2h ago" through
the shared platform relative-time formatter, with the absolute local
time on hover, matching how the Events table shows its observed time.
The Detail column takes 12% instead of 9% so every label the column
can produce fits whole, taken from the Kind, Current and Available
columns which hold short values. The Detail span truncates to the cell
with an ellipsis instead of being cut at the cell edge.
Contract-Neutral: presentation of existing Kubernetes controller time fields only; owning unified-resources clause updated; no frontend-primitives clause, primitive, or API change
Preserve exact Core candidate commits after independent diff and retained-proof review. Bound suppression state without evicting recent agents or hiding audit events; retain the real-worker confidentiality assertions.
Change-source: pulse-maintainer
Real Chromium and WebKit first taps selected the latest reading instead of the tapped observation. Select and retain the actual sample across compatibility focus and mouse events, shared-chart focus transfer and matching refreshes. Reject pan, cancellation and multiple contacts without preventing native scrolling. Preserve mouse and keyboard inspection, source ownership, units and access.
Change-source: pulse-maintainer
The mock generator stamped each job's last and next sync once at
startup, with the last sync up to an hour old on a 15-minute schedule.
Most jobs therefore started out overdue, and on a long-running demo
every job read as hours overdue in red while still reporting Healthy.
The demo's Replication tab looked broken to anyone evaluating Pulse.
Replication timing now follows the fixture clock. Each job keeps a
stable phase inside its schedule window, so a healthy job last synced
less than one interval ago and is next due in the future, and a
failing job's last good sync falls behind by its failure count.
The target node was picked by index and could equal the source, which
showed a job replicating pve1 to pve1. The source is no longer a
candidate, and an estate without a second node gets no jobs.
On a 1440px screen the Mail Gateway table showed a spam count as "3…"
and the Ceph table showed its Services column as "mon…". Both tables
use fixed layout and sized only their always-on columns, so the
optional ones split the few percent left over. Every visible column
now takes a weighted share through the shared platform helper.
Replication hid the reason a job failed. The error column had 8% of
the row and clipped mid-word, mid-width layouts dropped the column,
and the phone expansion omitted it. Rows now expand at every width to
the full job record with the whole error first, the error column is
wider, and search matches error text.
Backups sortable headers rendered in sentence case beside uppercase
plain headers because a button resets text-transform. Coverage rows
truncated node and workload names while short cells sat half empty,
and empty evidence cells repeated their header ("No PVE backup file")
until they truncated too. They now read "None" with the long form on
hover.
On phones the Ceph row clipped every value and the pool usage bar
clipped "50.1%" to "0.1%". The phone projection keeps five values
that fit whole. The FSID column, which never fit, leaves the row and
stays searchable and whole in the expansion.
Contract-Neutral: presentation sizing and labels only; owning unified-resources clause updated; no storage-recovery or frontend-primitives clause, API or primitive change
Keep at most 4096 remembered org/agent pairs and use an expiry index instead of repeated full-map scans. Audit unremembered overflow deliveries conservatively, preserving daily suppression for established agents, changed-config/token events and every failed fetch. Cover capacity, expiry, concurrency and the real handler contract.
Change-source: pulse-maintainer
Convergence used main's unmapped fallback and rejected the immutable 6.4.6-rc.1 source. Retain its exact-version release/v6.4 policy for artifact convergence and promotion without mapping future patches or accepting unrelated source.
Change-source: pulse-maintainer
A one-point stored History response drew no visible series and pointer inspection mapped it into a fictional one-millisecond window. Show a centred marker and one real timestamp, including measured zero, and share timestamp geometry with pointer inspection. Preserve multi-point rendering, keyboard access, empty states and request ownership.
Chromium and WebKit reproduce the predecessor defect. Twelve final desktop and phone states verify real canvas pixels, pointer and keyboard inspection, and sparse-to-empty refreshes. Synthetic presentation proof does not establish native collection or release availability.
Change-source: pulse-maintainer
Keep the last successful readings and source visible with an accessible warning until recovery or selection replacement. Stop promising future collection for empty history. Cover failure and recovery with request-state and two-engine browser controls.
Change-source: pulse-maintainer
Add focus, bounded sample navigation and polite timestamp/value announcements without changing pointer or target ownership. Keep wrapped tooltip readings inside their box. Validate parent failure and final desktop/phone browser states.
Change-source: pulse-maintainer
Reset pointer state only when the selection changes, not on supplied sample updates. Add a mounted pointer-refresh regression and recheck delayed-target browser states.
Change-source: pulse-maintainer
Invalidate superseded requests and polling, clear stale samples on selection changes, and exercise late completions in runtime and browser regressions.
Contract-Neutral: Correct request ownership in the existing shared chart without changing API, props, entitlements or public surface; dedicated runtime regressions accompany the repair.
Change-source: pulse-maintainer
Governance run 37016171545 rejected the original malformed timestamp even though a later reviewed receipt corrected it for identical runtime bytes. Use the existing fail-closed integration-range guard in CI while retaining per-commit canonical checks. Exercise the workflow shell with valid additive correction and invalid missing-base or unverified-content fixtures.
Change-source: pulse-maintainer
The browser run already covered these exact runtime bytes. Correct its UTC timestamp and bind this receipt-only correction to its own parent. Document that missing capacity remains unavailable without changing shared row APIs or layout.
Change-source: pulse-maintainer
Do not turn absent used capacity into an empty pool or invented free space.
Preserve independently observed bytes and explicit percentages, with guarded
derivation only from known inputs. Cover live drawer transitions and
desktop/phone browser states.
Contract-Neutral: Correct presentation of existing nullable capacity fields; no shared primitive API, layout, collector schema or recovery authority changes.
Change-source: pulse-maintainer
Distinguish live values, VM history, stored targets and chart request results without Debug logging, agent re-enrolment or credential exports.
Contract-Neutral: Documentation-only clarification of existing PBS History reads; no runtime, API, identity, permission or retention changes.
Change-source: pulse-maintainer
First step of the release simplification plan. create-release.yml loses
candidate_qualification and release_readiness, which only restated other
jobs' results. Their exact predicates move into publish_release_tag,
publish_docker, publish_helm_chart, activate_release and
release_commit_verdict, with cancellation unchanged.
recover-release-activation accepts both run shapes, so v6.4.6 and older
runs recover exactly as before. Failure-only diagnostics and the chart
artifact nobody downloads keep 3 days instead of 14 or 90. Only lines
cut from main after this land use it (release/v6.6 from 7 Oct). Reviewed
with gpt-6.1-sol, SAFE TO MERGE; predicate equivalence sampled over
2,784 cases.
The first snapshot control also stalled the unrelated app-stat stream. Model its valid empty reply and the proper stream field shapes in the two-connection poller fixture without weakening timeout, session, inventory or recovery assertions. Clarify that initial transport negotiation is bounded separately from each serialized RPC operation; runtime timeout policy is unchanged.
Contract-Neutral: Correct synthetic fixture responses and clarify the existing budget description; no runtime or wire behaviour changes.
Change-source: pulse-maintainer
Apply the configured timeout to serialized JSON-RPC operations, subscriptions and permitted read retries. Let cancelled waiters leave without dispatching or poisoning the active session. Preserve modern transport selection and action no-replay; distinguish caller deadlines from successful bounded log tails.
A required-method timeout must preserve cached host identity and previous success while allowing the next connection and recovery poll to proceed. Optional telemetry remains unavailable without blocking usable inventory. This repairs reproduced source paths, not a verified diagnosis or native resolution of issue #2382.
Change-source: pulse-maintainer
Preserve every accepted commit while incorporating the published setup-node advisory-cache repair. Upstream changes a separate workflow; retain the proved runtime and documentation candidate bytes.
Change-source: pulse-maintainer
The scheduled watch reads each release line's lockfile in the default
branch's cache scope, so it deliberately writes no dependency cache.
It sets no cache input, but the pinned setup-node enables npm caching
on its own once package.json names npm as its packageManager. Nothing
declares that today; adding it later would silently reopen a
default-branch cache write here. Set package-manager-cache: false, as
five release workflows already do, assert it in the workflow test, and
say how the contract's no-dependency-cache promise is kept.