Release-level regression pass over the alert lifecycle found two seams
with store/scheduler-level coverage but untested wiring:
- ingestRecoveryPointsBestEffort (#1580): a complete enumeration deletes
in-scope points the source stopped reporting, an empty enumeration
clears the scope, and a failed upsert skips reconciliation so a broken
batch cannot delete points the source still reports.
- availabilityPollProvider.FixedInstanceInterval (#1582): the configured
poll interval reaches the scheduler descriptor exactly (with the
10s..1h clamp), 0 for disabled or unknown targets, so the adaptive
scheduler cannot collapse the availability detection window again.
Same millisecond-tick flake as the boundary test fixed in 63c2118a1,
one test further down the file: the assertion recomputed now() - 5_000
after the write, so any tick between the two calls failed the exact
oldestDataTimestamp comparison in CI.
The 'Prove Patrol lifecycle and permission boundaries' step includes
./internal/api, whose frontend_embed.go declares
'//go:embed all:frontend-modern/dist'. CI checkouts never contain that
gitignored build output, so the package failed to compile and the step
died before a single lifecycle test ran (last four runs all red this
way). These proofs exercise Go logic only, so a one-file stub dist
satisfies the embed without paying for a frontend build.
Pre-commit runs from a linked git worktree export an absolute GIT_DIR.
The release-control test helpers and the test-patched branch of
git_env() only removed GIT_INDEX_FILE, so their scratch-repo commands
targeted the REAL repository: 'git init' in a tempdir re-initialized it
with core.bare=true (breaking git status/commit for every checkout and
worktree) and the subsequent scratch 'git add' failed the hook.
Scrub GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE, and GIT_COMMON_DIR in:
- git_env() of format_staged_go.py, governance_stage_guard.py, and
subsystem_contracts.py (test-patched branch only; production hook
behavior unchanged)
- the scratch-repo git() helpers in format_staged_go_test.py,
governance_stage_guard_test.py, readiness_assertion_guard_test.py,
subsystem_contracts_test.py
Matches the pattern contract_audit_test.py and status_audit_test.py
already used. Verified the full hook test battery passes with
GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE pointed at a canary repo, which
stays un-corrupted.
Re-pin the six /infrastructure-era specs to the vSphere platform page:
37 keeps its full facets/audit re-pin but is fixme'd because table-row
drawers disable remote history, leaving VMware hosts no reachable
change/action history surface (tracked); 38 and 42 move mention and
chat-turn coverage onto live mock state and the session transport (the
retired /api/ai/chat SSE stub is gone, so 42 proves the rendered
exchange instead of the wire payload); 39 and 41 pin the guest drawer's
read-only vCenter context and admin-action exclusions; 40 pins the
Datastores tab without backup semantics. 22 and 36 pass unchanged.
Verified via the managed local backend with PULSE_MOCK_MODE=true (8
passed, 1 tracked fixme).
Contract-Neutral: E2E quarantine-list shrink is test selection only; no deployment contract delta, mirrors bypass on 725837dc3
Re-pin 21 to the consolidated Connected-systems table (the address
column is gone; rows carry source, surfaces, state, and freshness) and
rewrite 26 against live mock websocket state with the stubable
alerts-config activation bypass, since REST resource stubs get
overwritten by state frames. 27, 28, and 31 were environmental-only
failures and pass unchanged against a mock-mode backend. Verified via
the managed local backend with PULSE_MOCK_MODE=true.
Contract-Neutral: E2E quarantine-list shrink is test selection only; no deployment contract delta, mirrors bypass on 725837dc3
Core E2E has failed on every push since 2026-06-29. Fifty of the 81 spec
files still assert the pre-platform-first IA (locators, URLs, subheader
copy the June rebuild replaced), and with maxFailures aborting shards at
20 the healthy specs produced no verdict at all. The quarantine list in
playwright.config.ts excludes the rotted files from all three projects so
the remaining 38 files (323 tests, including the smoke and journey
coverage) return to being a live per-push gate.
This is a stopgap, named as such in the config comment. Each quarantined
spec leaves the list by being re-pinned to the current UI, never by
deletion; the recovery effort is tracked separately.
Contract-Neutral: E2E spec quarantine is test selection only; no deployment contract delta, and the contract file is another agent's in-progress work
Canonical Governance failed on nearly every push today (6 of the last 8
commits) because the local pre-commit hook and the CI workflow disagreed
about the canonical completion guard in two ways.
Frontend-only commits skipped the guard entirely: the hook gated ALL
governance checks behind governance-path detection as a perf
optimization, but subsystem contracts name canonical frontend files, so
those commits landed locally and failed the same guard in CI. The hook
now always runs the (cheap) completion guard; only the multi-minute Go
test and audit battery stays path-gated.
Contract-neutral bypasses did not travel: PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT
authorized the commit in the local shell and vanished, so CI re-ran the
guard without it and went red. A new prepare-commit-msg hook records the
reason as a Contract-Neutral trailer in the commit message, and the
workflow now evaluates the guard per commit (each commit's file list
against its own parent), honoring the trailer exactly as the commit-time
hook honored the env var. Per-commit evaluation also stops a compliant
commit from being blamed for range-mates.
The new hook is registered as a worktree-sensitive governance file in
governance_stage_guard.py with a matching unit test assert.
Build and Test has been red since 2026-07-11 because these tests still
pinned the pre-review-flow behavior:
- apiErrorStatus guardrail expected 3 triggerResourceCommand returns in
monitoring.ts; the governed container update flow moved two callers out.
- ResourceDetailDrawer docker test drove the retired click-twice confirm;
the drawer now plans the action and opens ActionReviewDialog, so the
test pins surface attribution and the dialog handoff instead (the
dialog's own suite covers approve/execute).
- AIIntelligence patrol tests mocked the old autonomy settings shape and
expected Autopilot to switch modes directly; settings now carry
requested/effective levels plus autopilot_acknowledgement, and the
Autopilot button opens the acknowledgement dialog without saving.
- infrastructureSummaryCache boundary test measured age with real timers,
so any millisecond tick between write and read flipped the strict >
verdict; the clock is now frozen for the boundary assertion.
Users hit by unwanted container image update alerts (#1545) had no UI
lever short of disabling all container alerts; updateAlertDelayHours
was backend-only. Add a Container update alerts card to the Docker
thresholds tab with an on/off toggle (off saves -1) and an hours delay
input. The save path normalizes the value so 0 (which the backend
treats as unset and resets to 24) is never emitted, and the snapshot
builder carries the field through unrelated saves.
Also fix the docker-specific-controls gate: it required a legacy
'docker-host' typed resource, which hid the ignored-prefixes and swarm
service cards (and would have hidden this one) for unified-agent Docker
hosts that only appear in the runtime list via their app-containers.
The gate now also accepts any non-TrueNAS container runtime.
The open-work-queue redesign removed findingsBadgePresentation from
PatrolIntelligenceWorkspace; work-group badge tones now resolve in
patrolControlPresentation and render as tone={group.tone} on MetadataBadge.
Repin the boundary assertion to that canonical location.
The lifecycle buttons already read resource.actionReadiness and render
disabled with the refusal reason before any click. The update button did
not, so a user with a stale or disconnected agent could click Update,
confirm, and only then get the refusal from POST /api/actions/plan
(GitHub #1564). Wire the update button to the same per-resource
readiness data: extract the readiness-refusal resolver shared with the
lifecycle buttons into utils/actionReadiness.ts, thread actionReadiness
through UpdateButtonProps at both render sites (Docker containers table
and the workloads GuestRow via useWorkloads), and render the button
disabled with the refusal reason as its tooltip. The post-click
details.reason path from b3058d47e stays as the backstop for
websocket-hydrated rows, which carry no readiness data until the REST
revalidation lands.
A refused update plan showed only 'Action execution is unavailable'; the
actionable explanation (agent disconnected, agent too old, stale inventory)
arrives in the API error's details.reason and was dropped. Prefer it in the
failure badge and tooltip. Seen in #1564 where the toast gave the reporter
nothing to act on and Actions showed no record (refusal is pre-audit).
'This agent does not support durable typed-operation receipts' told users
nothing they could act on. Say the agent is on an older version and how to
resolve it. Applies to docker container, host update and host storage
cleanup executors.
The DisableAllDockerContainers and DisableAllDockerServices config sweeps
matched only legacy docker-container-/docker-service- alert ID prefixes.
Canonical stateful alerts (image-update, state, health, replica-gap) are
stored under <resourceID>::<specID> state IDs, so existing alerts survived
the toggle and lingered indefinitely, re-notifying past cooldown.
Match on the docker: resource ID scheme as well as the legacy prefixes.
The active-turn status helpers had 27 uncovered branches its existing suites did
not reach. This covers the tool-label fallback, the retryAfter and startedAt
absent-field fall-throughs, and the skip arms for undefined stream and pending-tool
entries, taking covered branches from 276 to 316.
New test-only file with a distinct branchcov0717 suffix. No source changed.
Verified with vitest and type-check, measured to raise branch coverage over the
pre-existing suite.
aiFindingPresentation had grown a set of badge, tone, label and Patrol
classification helpers its existing suites never called, so this covers 25
previously-uncovered functions plus their fallback and count-edge arms.
workloadsSummaryCache goes from 28 to 76 covered branches across cache
hit/miss/expiry paths, infrastructureSummaryCache picks up its remaining getter
and branch arms, and resourceBadgePresentation covers a further block of edge
branches its two branchcov suites left open.
Four new test-only files with distinct branchcov0717 suffixes alongside the
existing suites. No source changed. Verified with vitest and type-check, each
measured to raise branch or function coverage over the pre-existing suite.
These presentation helpers had partial coverage and had grown functions their
existing tests never reached. alertWebhookPresentation goes from 14 to 23 covered
functions, organizationSettingsPresentation from 34 to 42, alertIncidentPresentation
from 37 to 39 with its remaining branches, and ssoProviderPresentation closes its
last four uncovered branches. The new cases exercise the fallback, boundary,
per-role and error-precedence arms the primary tests omit.
Four new test-only files with distinct branchcov0717 suffixes alongside the
existing suites. No source changed. Verified with vitest and type-check, and each
measured to raise branch or function coverage over the pre-existing suite.
The docker container lifecycle codec in agentexec had a contract test that never
exercised the codec functions at runtime, leaving decode, bind, validate and
identity helpers at zero coverage. This adds a table-driven test taking each to
near or full coverage including every validation error arm. In aicontracts,
CloneActionReference, IsNilAlertPayload and DefaultEngineConfig were uncovered,
so this covers the nil and populated arms and asserts CloneActionReference
produces an independent deep copy.
Both are new test-only files. No source changed. Verified with go test, gofmt
and go vet, with each target function confirmed to move off zero coverage.
containerUpdateBadgeModel gained update icon and button helpers since its prior
branchcov test, so this covers the three previously-uncovered functions and
their state arms (idle, confirming, updating, success, error and the default).
aiProviderPresentation route-label formatting had fourteen uncovered branches,
now exercised through the four exported entry points. dockerIdentityConflict
conflict collection reaches full branch coverage including the hostnames
fallback and the name fallback chain.
All three are new test-only files under existing __tests__ directories. No source
changed. Each was confirmed with vitest and type-check, and measured to raise
branch or function coverage over the pre-existing suite.
Two more newly added *_w0716_coverage_test.go files from the same
branch-coverage wave, touching no source and no existing test.
- internal/models deep-copy clone independence for Docker, Kubernetes,
ZFS and PBS clones, each mutating the original and asserting the clone
stays unchanged
- internal/ai/qualification lab shell-quoting and resource-name
rendering plus report replay predicates and markdown renderers
Cover previously-unexecuted branches in modules that gained behavior
since the last wave. All six are newly added *_w0716_coverage_test.go
files that modify no source and no existing test.
- internal/ai/chat investigation-budget injection plus the agentic
tool-event and string helpers
- internal/ai/qualification manifest Validate error arms and the Patrol
docker-predicate and finding-prerequisite validators
- internal/unifiedresources adapters transforms and ContractResourceType
- internal/config AIConfig nil-receiver default branches
- pkg/aicontracts orchestrator error methods and investigation config
Cloned VMs that keep the same /etc/machine-id collapse into a single
Docker host in Pulse, with each clone's report silently overwriting the
other (#1584). The server now tracks per-identity hostname and machine
ID observations and flags a conflict when a value flaps back to one
already seen inside a 15-minute window, a signature a one-time rename
never produces. The conflict rides the DockerHost model through unified
resources, and the Docker page shows a warning naming the flapping
hostnames with the machine-id remedy. The warning self-clears once one
clone stops reporting for the window.
A vSphere 6.x target fails the connect test at the Automation session
step with a generic "HTTP 500" message, because /api on those releases
routes to the JSON-RPC servlet. When /api/session fails with a non-auth
status, probe the legacy /rest CIS session API with the same
credentials; if that login works the target predates the JSON APIs
Pulse uses, so the test now reports the unsupported-version warning
naming the vCenter 8.0U1 floor instead of pointing at credentials. The
probe deletes the session it creates. Also reword the frontend guidance
for that category in plain terms.
Related to #1585.
Update checks against registries without a hardcoded token endpoint
(lscr.io and other spec-compliant registries) failed with a blanket
"Check failed" because the manifest HEAD went out anonymously and the
401 was terminal. Parse the Bearer challenge on the 401, fetch a pull
token from the advertised realm, and retry once. Token endpoints that
answer with access_token instead of token are now accepted too.
Fixes#1583.