mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-09 18:15:50 +00:00
Integrate current main with verified Patrol action history
Preserve the incoming delivery-health ordering and demo install-link changes alongside the qualified Patrol action history. Retain prior source bindings and record the merged build and browser checks so the shared proof receipt reflects the integrated tree.
This commit is contained in:
@@ -1,21 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "57ead19484c49d003287309d0734875405da9040",
|
||||
"verified_at": "2026-09-06T20:11:45.553223Z",
|
||||
"base_sha": "3853124a391ed451f75402f07951c0142e6b5ad8",
|
||||
"verified_at": "2026-09-06T20:40:07.804772Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/components/AI/FindingsPanel.tsx",
|
||||
"frontend-modern/src/components/patrol/ApprovalSection.tsx",
|
||||
"frontend-modern/src/features/actions/ActionDecisionPacket.tsx",
|
||||
"frontend-modern/src/features/actions/ActionReviewDialog.tsx",
|
||||
"frontend-modern/src/features/alerts/useAlertOverviewState.ts"
|
||||
"frontend-modern/src/components/DemoBanner.tsx"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/components/AI/FindingsPanel.tsx": "f506a26757b4c0ea3adf3f77af10214bfd31578b7122d3904a9b3a7272d1e146",
|
||||
"frontend-modern/src/components/patrol/ApprovalSection.tsx": "6a18d67d5d3d0a8335589bdf199c340eb4775aea3442dc094971d7f924a0dcc5",
|
||||
"frontend-modern/src/features/actions/ActionDecisionPacket.tsx": "2f1fd68ec333e7f9e8792d74ba8d7e6a2e95755c82b9c7121d847469a31f931b",
|
||||
"frontend-modern/src/features/actions/ActionReviewDialog.tsx": "49e12cfd44686bd657ddddfb167c5956ec693b6f5d45d9d43c1e3a2e858a6c7f",
|
||||
"frontend-modern/src/features/alerts/useAlertOverviewState.ts": "64d0b891e7ad228e8590da859dc25e825b6164c8cf76a01983a219d6cd079b23"
|
||||
"frontend-modern/src/components/DemoBanner.tsx": "13fd8dea552d97f1df866438f7ba38eec01f25907dc68a0bec672e1793c3fc97"
|
||||
},
|
||||
"backend_content_sha256": {
|
||||
"internal/agentcapabilities/transcript.go": "356c4ca201470407988ff9b2c1fb848619ed38e9d8db844e7390adce0f93ec19",
|
||||
@@ -27,19 +19,22 @@
|
||||
"internal/ai/tools/tools_propose.go": "43d720c78a010b72f53f7e4e9e1b7b2a763e7f1ac555edb921b7cdca3e82fd51",
|
||||
"internal/ai/patrol_findings.go": "d5eeb386f025cca338ac328b1d4ec7a2bf51150023454e2b61670f196fcf0356",
|
||||
"internal/api/ai_handlers.go": "f8c9b24dc684346da4bddab066540fd43ca4999f5fd379c4dc34b5293f78394c",
|
||||
"internal/api/patrol_action_reconciliation.go": "bc5da1a8050b94271dcdc88841a0ce3329e1773bd01c8746068391a71740ffa2"
|
||||
"internal/api/patrol_action_reconciliation.go": "bc5da1a8050b94271dcdc88841a0ce3329e1773bd01c8746068391a71740ffa2",
|
||||
"internal/monitoring/monitor.go": "63c8ef4867c07c96b4cd7c4316b5b8646f11b7e9e43a5233471956f76f33d553",
|
||||
"internal/monitoring/system_alerts.go": "53ed4f02784636363da273883415331066d8b2484ca50e6b506e69990abb149d"
|
||||
},
|
||||
"enterprise_base_sha": "3d9f4e3051d38027355a2a1f36b8c7f672a09b65",
|
||||
"enterprise_content_sha256": {
|
||||
"internal/investigation/orchestrator.go": "d56fd512dc47f8a2453559e89863da5d24dffc0a5977abb8f1ea7a82655cd9e8"
|
||||
},
|
||||
"binary_sha256": "859d5d2de84cfd2264caa7dbcf5f080e3b1d06b5876df2c81dc0e00872f5e779",
|
||||
"binary_sha256": "0c19f9b9265eff2214e79ab6b2e4a6b19a5645eb467c994c44af70b3d6593317",
|
||||
"routes": [
|
||||
"/qualification (isolated Overview component on :5199)",
|
||||
"/patrol (Activity, All history)",
|
||||
"/actions?action=act_dcc3b52e5451810e49466daf9a6fccb0",
|
||||
"/actions?action=act_ee0b736f0430e472e896a456ba3cb6eb",
|
||||
"Pulse Assistant contextual panel from resolved Patrol findings"
|
||||
"Pulse Assistant contextual panel from resolved Patrol findings",
|
||||
"/qualification (isolated DemoBanner component on :5198)"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
@@ -59,11 +54,20 @@
|
||||
"Incoming merged alert delivery diagnosis ordering: hold older request, add alert to start newer request, render current notifications-disabled state, release older ready response, verify current state and both cards remain. Existing Patrol and login proof is retained in the prior committed receipt and runtime source is unchanged.",
|
||||
"Real persisted approved/verified and rejected findings remain reviewable after resolution. Durable investigation outcome agrees with authoritative action, with original prose retained. No stale Fix Queued status. Exact action links remain available.",
|
||||
"Completed and Rejected action headers, State when planned and Plan expiry copy, inert settled action controls, independently verified recovery and explicit unavailable rollback. Policy, evidence and delivery disclosures expand and collapse.",
|
||||
"Assistant handoff shows the exact finding with completed/rejected action context and Chat: Read-only. Browser provider readiness POST is deliberately blocked, so its visible route error is a rendering check and does not retest the provider. No prompt is submitted."
|
||||
"Assistant handoff shows the exact finding with completed/rejected action context and Chat: Read-only. Browser provider readiness POST is deliberately blocked, so its visible route error is a rendering check and does not retest the provider. No prompt is submitted.",
|
||||
"Incoming main DemoBanner install link: visible in demo policy, absent outside demo, link hover/focus, exact external setup destination opened in a new tab with noopener/noreferrer, keyboard dismissal and reload persistence. Real backend mock mode remains off. External destination content is intercepted because only navigation is under test."
|
||||
],
|
||||
"interactions": [
|
||||
"scripts/check-alert-diagnosis-ordering.mjs passed at 1440, 900 and 390 by 1000. Actual pixels inspected at desktop and narrow sizes. This is scripted component evidence, not proof of installed delivery or recipient receipt. Screenshots in /tmp/pulse-alert-diagnosis-ordering/.",
|
||||
"Final Pro binary and final frontend content exercised in Playwright at 1440, 900 and 390 by 1000. Keyboard open/review, safety disclosure, exact action navigation, direct deep-link reload, policy/evidence/delivery disclosure keyboard toggles, Escape and close-button dismissal, review focus return where retained, Assistant open/close, scrolling and page overflow checks. Desktop/intermediate/phone pixels inspected including deepest evidence and Assistant overlay.",
|
||||
"Private artifacts: tmp/patrol-gemini-38/live-action-browser/. Final complete matrix passed after removing redundant back-to-back full-page navigations from the proof driver. Earlier proof attempts hit an intermittent bootstrap connection screen. No bootstrap fix or general availability claim is made. API writes blocked except login."
|
||||
]
|
||||
"Private artifacts: tmp/patrol-gemini-38/live-action-browser/. Final complete matrix passed after removing redundant back-to-back full-page navigations from the proof driver. Earlier proof attempts hit an intermittent bootstrap connection screen. No bootstrap fix or general availability claim is made. API writes blocked except login.",
|
||||
"After integrating main eef4ea21e73aedaa69380574b1fdf4d9dbdee3c8, repeated the complete real Patrol/Actions/Assistant matrix on binary 0c19f9b9265eff2214e79ab6b2e4a6b19a5645eb467c994c44af70b3d6593317. All three widths passed and pixels were reinspected. Incoming DemoBanner separately passed at the same widths using an isolated Vite cache and actual project styling. Prior frontend hashes remain byte-identical and are retained above. Merged monitoring regressions, API action tests, 37 component tests and type-check pass."
|
||||
],
|
||||
"prior_frontend_content_sha256": {
|
||||
"frontend-modern/src/components/AI/FindingsPanel.tsx": "f506a26757b4c0ea3adf3f77af10214bfd31578b7122d3904a9b3a7272d1e146",
|
||||
"frontend-modern/src/components/patrol/ApprovalSection.tsx": "6a18d67d5d3d0a8335589bdf199c340eb4775aea3442dc094971d7f924a0dcc5",
|
||||
"frontend-modern/src/features/actions/ActionDecisionPacket.tsx": "2f1fd68ec333e7f9e8792d74ba8d7e6a2e95755c82b9c7121d847469a31f931b",
|
||||
"frontend-modern/src/features/actions/ActionReviewDialog.tsx": "49e12cfd44686bd657ddddfb167c5956ec693b6f5d45d9d43c1e3a2e858a6c7f",
|
||||
"frontend-modern/src/features/alerts/useAlertOverviewState.ts": "64d0b891e7ad228e8590da859dc25e825b6164c8cf76a01983a219d6cd079b23"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { createSignal, onMount, Show } from 'solid-js';
|
||||
import { InlineNotice } from '@/components/shared/InlineNotice';
|
||||
import { presentationPolicyIsDemoMode } from '@/stores/sessionPresentationPolicy';
|
||||
|
||||
const DEMO_INSTALL_URL = 'https://pulserelay.pro/#setup';
|
||||
|
||||
export function DemoBanner() {
|
||||
const [dismissed, setDismissed] = createSignal(false);
|
||||
|
||||
@@ -29,7 +31,19 @@ export function DemoBanner() {
|
||||
dismissLabel="Dismiss demo banner"
|
||||
dismissTitle="Dismiss"
|
||||
>
|
||||
Demo instance with mock data (read-only)
|
||||
<span>Demo instance with mock data (read-only)</span>
|
||||
<span aria-hidden="true"> · </span>
|
||||
{/* The public demo is where pulserelay.pro sends curious visitors, and
|
||||
without this it was a dead end: no way back except browser history.
|
||||
Install guidance, not an upsell, so it stays in demo mode. */}
|
||||
<a
|
||||
href={DEMO_INSTALL_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="font-medium underline underline-offset-2 hover:no-underline"
|
||||
>
|
||||
Run Pulse on your own hardware
|
||||
</a>
|
||||
</InlineNotice>
|
||||
</Show>
|
||||
);
|
||||
|
||||
@@ -57,6 +57,17 @@ describe('DemoBanner', () => {
|
||||
expect(screen.getByText('Demo instance with mock data (read-only)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('links the demo back to the install steps in a new tab', async () => {
|
||||
presentationPolicyIsDemoModeMock.mockReturnValue(true);
|
||||
|
||||
await renderBanner();
|
||||
|
||||
const link = screen.getByRole('link', { name: 'Run Pulse on your own hardware' });
|
||||
expect(link).toHaveAttribute('href', 'https://pulserelay.pro/#setup');
|
||||
expect(link).toHaveAttribute('target', '_blank');
|
||||
expect(link).toHaveAttribute('rel', 'noopener noreferrer');
|
||||
});
|
||||
|
||||
it('stays hidden when demo mode is disabled', async () => {
|
||||
await renderBanner();
|
||||
|
||||
|
||||
@@ -2683,8 +2683,9 @@ func TestDefaultOrgMonitorSharesCanonicalRuntimeTokenInventory(t *testing.T) {
|
||||
Scopes: []string{config.ScopeAgentExec},
|
||||
}}
|
||||
config.Mu.Unlock()
|
||||
monitor.mu.Lock()
|
||||
monitor.state.Hosts = []models.Host{{
|
||||
// GetMonitor starts polling concurrently; host fixtures must use the
|
||||
// state-owned lock, not monitor.mu, to synchronise with snapshots.
|
||||
monitor.state.UpsertHost(models.Host{
|
||||
ID: "agent-fresh-token",
|
||||
Hostname: "fresh-token-host",
|
||||
Status: "online",
|
||||
@@ -2692,8 +2693,7 @@ func TestDefaultOrgMonitorSharesCanonicalRuntimeTokenInventory(t *testing.T) {
|
||||
AgentVersion: "6.2.2",
|
||||
TokenID: "fresh-agent-token",
|
||||
CommandsEnabled: true,
|
||||
}}
|
||||
monitor.mu.Unlock()
|
||||
})
|
||||
|
||||
diagnostics := monitor.GetAgentFleetDiagnostics("6.2.2", now)
|
||||
agent := requireAgentDiagnostic(t, diagnostics, "agent-agent-fresh-token")
|
||||
|
||||
@@ -1124,7 +1124,8 @@ type Monitor struct {
|
||||
deadManConfigMu sync.RWMutex
|
||||
deadManConfig notifications.DeadManConfig
|
||||
deadManConfigLoadErr error
|
||||
lastDeliveryHealthCheck time.Time // throttles the notification-delivery system alert evaluation; guarded by mu
|
||||
deliveryHealthProjectionMu sync.Mutex // serializes delivery-health reads and alert projection
|
||||
lastDeliveryHealthCheck time.Time // throttles the notification-delivery system alert evaluation; guarded by mu
|
||||
configPersist *config.ConfigPersistence
|
||||
discoveryService *discovery.Service // Background discovery service
|
||||
activePollCount int32 // Number of active polling operations
|
||||
|
||||
@@ -57,7 +57,18 @@ func (m *Monitor) evaluateNotificationDeliveryAt(now time.Time, force bool) {
|
||||
return
|
||||
}
|
||||
|
||||
health := notificationMgr.DeliveryHealth()
|
||||
m.projectNotificationDeliveryHealth(alertManager, notificationMgr.DeliveryHealth)
|
||||
}
|
||||
|
||||
// projectNotificationDeliveryHealth serializes the entire read/apply operation.
|
||||
// Locking only the alert mutation lets a paused, older health read overwrite a
|
||||
// newer reconciliation (either resurrecting a dismissed warning or hiding a
|
||||
// new failure). Queue callbacks enter here after releasing the queue lock.
|
||||
func (m *Monitor) projectNotificationDeliveryHealth(alertManager *alerts.Manager, readHealth func() notifications.DeliveryHealth) {
|
||||
m.deliveryHealthProjectionMu.Lock()
|
||||
defer m.deliveryHealthProjectionMu.Unlock()
|
||||
|
||||
health := readHealth()
|
||||
if health.Healthy {
|
||||
alertManager.ClearSystemAlert(alerts.NotificationDeliveryAlertType)
|
||||
return
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/notifications"
|
||||
)
|
||||
|
||||
@@ -130,3 +131,71 @@ func TestEvaluateNotificationDeliveryIsSafeWithoutAMonitor(t *testing.T) {
|
||||
var m *Monitor
|
||||
m.evaluateNotificationDelivery(time.Now())
|
||||
}
|
||||
|
||||
// Hold the older snapshot between read and apply while a newer reconciliation
|
||||
// tries to enter. Exercise both stale-clear and stale-raise failure modes
|
||||
// without a database, queue workers, or notification destinations.
|
||||
func TestProjectNotificationDeliveryHealthOrdersSnapshots(t *testing.T) {
|
||||
healthy := notifications.ClassifyQueueHealth(map[string]int{})
|
||||
failed := notifications.ClassifyQueueHealth(map[string]int{string(notifications.QueueStatusDLQ): 1})
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
old, current notifications.DeliveryHealth
|
||||
}{
|
||||
{"new_failure_survives_old_clear", healthy, failed},
|
||||
{"dismissal_survives_old_failure", failed, healthy},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
manager := alerts.NewManagerWithDataDir(t.TempDir())
|
||||
t.Cleanup(manager.Stop)
|
||||
m := &Monitor{}
|
||||
read := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
oldDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(oldDone)
|
||||
m.projectNotificationDeliveryHealth(manager, func() notifications.DeliveryHealth {
|
||||
close(read)
|
||||
<-release
|
||||
return tc.old
|
||||
})
|
||||
}()
|
||||
<-read
|
||||
// This assertion is independent of scheduling: the snapshot must
|
||||
// already be protected before reading, not just when applying it.
|
||||
if m.deliveryHealthProjectionMu.TryLock() {
|
||||
m.deliveryHealthProjectionMu.Unlock()
|
||||
t.Error("health read is not protected by the projection lock")
|
||||
}
|
||||
newRead := make(chan struct{})
|
||||
newDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(newDone)
|
||||
m.projectNotificationDeliveryHealth(manager, func() notifications.DeliveryHealth {
|
||||
close(newRead)
|
||||
return tc.current
|
||||
})
|
||||
}()
|
||||
select {
|
||||
case <-newRead:
|
||||
// Ensure the newer state applies before releasing the stale
|
||||
// snapshot when checking the unprotected implementation.
|
||||
<-newDone
|
||||
t.Error("new health read overtook an unfinished projection")
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
close(release)
|
||||
<-oldDone
|
||||
<-newDone
|
||||
active := false
|
||||
for _, alert := range manager.GetActiveAlerts() {
|
||||
if alert.Type == alerts.NotificationDeliveryAlertType {
|
||||
active = true
|
||||
}
|
||||
}
|
||||
if active == tc.current.Healthy {
|
||||
t.Errorf("delivery warning active = %v, latest health healthy = %v", active, tc.current.Healthy)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user