## What
Every job that reads a release, signing or registry credential now
declares
`environment: release`, and `scripts/check_workflow_trust.py` enforces
it.
- 20 jobs across `create-release`, `build-release-candidate`,
`publish-docker`,
`promote-floating-tags`, `promote-private-pro-runtime`,
`recover-release-activation`, `backfill-release-assets`,
`retry-release-convergence`, `release-signing-preflight`,
`signpath-test-signing` and `canonical-private-governance` declare
`environment: release`. `qualify-browser-update-release` declares
`infra` for
its tailnet credentials. `PULSE_LICENSE_PUBLIC_KEY` is a public key and
stays
repository-wide.
- The checker reads job structure with a duplicate-key-rejecting YAML
loader
and scans both raw text and decoded values for secret expressions
(case-insensitive, across lines, quote-aware). It requires the literal
release environment for release credentials and a deployment environment
for demo/tailnet credentials (the demo workflow's runtime choice only in
its
own job). It refuses dynamic `secrets[...]` access and workflow-level
secret
env, and requires any push workflow that reads a secret to admit exactly
`branches: [main]`.
## Why
Release secrets are repository-level today, so any workflow run on any
branch,
including a pushed proposal branch before review, can read them. The
release
train is switching to post-merge `release-dispatch/*` branches that only
the
release dispatcher App can create (ruleset-enforced). A `release`
environment
admitting only `main` and `release-dispatch/*` then keeps these
credentials
away from unmerged code.
## Merge order (draft until then)
Stacked on #3297. Do not merge until:
1. a beta has published through a `release-dispatch/*` dispatch, and
2. the `release` (branches `main`, `release-dispatch/*`) and `infra`
(`main`)
environments exist with branch-typed policies, so the first post-merge
run
does not auto-create an unprotected environment.
Repository-level secret copies stay until no snapshot without these
declarations can still be dispatched or recovered.
## Validation
- Live tree passes the checker; `test_workflow_trust.py` 50 OK,
including new
cases for every rule and for bypasses found in review (lower-case names,
`}}` inside strings and YAML-escaped quotes, `release#x`, duplicate
keys,
quoted `tags`/`inherit`, a push key hidden in a description, the demo
expression outside its job).
- Eleven mutations, one per guard, each fail the tests.
- All 1301 `scripts/tests`, Go `scripts/installtests`, and
`release_promotion_policy_test.py` 74 OK (new proof test pins the
release
jobs' environment). Release-control discovery has 17 failures and 1
error
that are identical on `main` and on this branch's base.
Separate disk-temperature and SCALE CPU fixes from the still-failing CORE host CPU and memory reporting request. Preserve the rest of the stable and RC.6 notes and cover both source copies in the existing renderer suite.
Contract-Neutral: Published-note wording and its regression coverage only; no runtime or subsystem contract changes.
Change-source: pulse-maintainer
The scheduled scan at 41715cf2 retried a refused Docker Hub control-plane read four times, then attempted aliases through a second script. Stop explicit registry or attestation access refusals on the first response, retain the exact target and exit, and run aliases only after exact-container verification succeeds. Keep both registries, every digest and source-bound attestation mandatory; unrelated Helm evidence remains independent.
Contract-Neutral: Fail-closed refusal handling and diagnostic ordering only; public registry, digest, provenance and release activation requirements are unchanged.
Change-source: pulse-maintainer
Release, signing and registry secrets are repository-wide, so a workflow run
on any branch can read them, including a pushed proposal branch before any
review. The release train now dispatches on post-merge release-dispatch/*
branches that only the release dispatcher App can create, so these secrets
can move into a `release` environment that admits only main and those
branches.
Every job that reads the update signing key, Apple, SignPath or Windows
signing material, Docker Hub credentials or WORKFLOW_PAT now declares
`environment: release`; the browser update qualification job declares `infra`
for its tailnet credentials. The public license verification key is not a
credential and stays repository-wide.
check_workflow_trust.py enforces this. It reads job structure with a YAML
loader that rejects duplicate keys and scans expressions across lines,
comments and quoted `}}`, matching secret names case-insensitively. Release
credentials need the literal release environment, demo and tailnet
credentials need a deployment environment (the demo workflow's runtime choice
only in its own job), dynamic secrets[...] access and workflow-level secret
env are refused, and a push workflow that reads any secret must admit exactly
`branches: [main]`. A test that asserted an any-branch push workflow could
read WORKFLOW_PAT now asserts the opposite.
Activation recovery only admitted failed create-release runs dispatched on
main, but the release train dispatches on release-candidate/* snapshot
branches, and will move to post-merge release-dispatch/* branches. A train
release that failed at activation could not use the recovery path.
Recovery now also admits runs from those refs, but only after main's
release_snapshot.py proves the run's commit is the merged head of exactly one
release-candidate/<packet> release PR. The source run's own prepare job ran
that branch's code, so it is not trusted. Recovery refuses to run unless
dispatched on main, fetches full history for the ancestry check, and gains
pull-requests: read for the PR lookups.
Release secrets are repository-wide today, so any workflow on any branch can
read them. The plan is to move them into a `release` environment that admits
only `main` and post-merge dispatch refs. Today's dispatch refs cannot serve:
`release-candidate/*` also names unmerged release PR heads.
The snapshot check now also accepts `release-dispatch/<packet>`. It must name
a merged PR whose head is `release-candidate/<packet>` at the dispatched
commit, with the same source-line and ancestry checks as before. The
existing `release-candidate/*` dispatch keeps working, so the host dispatcher
can switch over later. Every snapshot cut after this lands carries the check.
This change validates identity only. Restricting who can create or move
`release-dispatch/*` refs is a separate repository ruleset, and the contract
says the namespace carries no extra trust until that ruleset is active.
Restore the existing dialog/flow verification policy unchanged and give the Recovery overview its own direct-test requirement. Pin rejection of cross-surface substitutes, cumulative proof for mixed edits and truthful forward/reverse lookup routes without changing frontend bytes or imported history.
Change-source: pulse-maintainer
Complete the same Recovery policy move in lookup coverage, including all three transfer surfaces and their exact proof list. The broader governance run exposed this stale framing expectation after the first repair; no runtime, registry or enforcement changes are required.
Change-source: pulse-maintainer
Keep the seven remaining settings shells under framing proof, and assert transfer-specific membership for Recovery, its dialogs and its flow. Confirm dedicated proofs match and generic settings architecture cannot satisfy transfer safety. This repairs PR 3249's reproducible governance-test failure without changing the guard or weakening the registry.
Change-source: pulse-maintainer
An unrelated Microsoft repository returned 403 during fresh-index admission and stopped a browser shard before any test could run. Load the hosted runner's existing Ubuntu Deb822 definition for browser packages, preserving its mirrors and signature policy. Fail when that definition is missing, unreadable or empty and keep strict index and archive hash checks.
Exercise the real apt parser, exclusion of unrelated feeds, missing definitions and incomplete index failures in private acquisition directories. Register the package helper and policy with their exact verification routes, and update the fixture helper's governance assertion to its scoped proof contract.
Fresh password login must await the mounted workspace before a caller
opens an onboarding route. Own and revoke the bootstrap regression token
without replacing the primary credential, and bootstrap update API tests
once per worker when CI provides cookies alone.
Opt the synthetic shared-IP stack into a finite development update budget
without changing production, authentication or configuration limits.
Observe gated panels through actual gate readiness. Keep the fixed 24h
growth assertion on its native header and select the retained LXC drawer
with the fixture's canonical resource identity. Integrate the published
filesystem-observation and issue-topic repairs before qualification.
Preserve source provenance in REST-loaded resources so owning-snapshot
consumers retain provider context and host placement. Keep visible control wording in accessible names and retain readable command
and navigation text in both themes. Token fixtures now await mounted browser
bootstrap before handing control to the next journey.
Measure bounded platform table items together with their inline details so
scrolling down a tall drawer cannot unmount the resource being inspected.
Retain regression coverage for bootstrap timing, drawer state and scroll
extent, responsive controls, Patrol and availability, and align navigation
assertions with current read-only capabilities and composed resource queries.
Provider fixtures retain one authoritative inventory across transport updates.
Since v6.5.0 demo mode signs a fresh visitor in with the published
demo credentials, so the login form never stays on screen for a first
visit. The browser smoke still waited two minutes for that form and
failed, which blocked every stable demo convergence once the Tailscale
step worked again. It went unnoticed because the demo update had not
got past Tailscale since v6.5.0 shipped.
The smoke now proves the visitor's real path, a fresh tab reaching the
connected workspace, when the server reports demo mode. A second
context sets the frontend's own pulse-demo-auto-login marker to keep
proving the login form and a manual demo sign-in. Against the live
demo the old script times out and the new one passes both phases. The
policy test pins the marker to localStorage.ts so a rename fails in
source checks rather than on the live demo.
The demo update and recovery workflows still joined the tailnet with
the July 2026 tag:infra OAuth client. That client was revoked on 30
September, when the private repositories moved to narrow clients, and
the public repository was missed. Since then every demo convergence
step has failed with an OAuth 401, so stable releases cannot converge
and the hourly reconcile keeps retrying v6.5.0.
Both workflows now use the TS_CI_DEPLOY_OAUTH_* binding that the
browser update qualification already expects, restricted to
tag:ci-deploy. The tailnet policy already grants that tag SSH to
pulse-relay, the demo host, so no policy change is needed. One narrow
client now serves all three workflows, and the broad tag:infra
identity is gone from this repository.
Treat a deleted convergence owner as terminal
The hourly "Reconcile release convergence debt" job has failed every run
since 13 Oct. The v6.5.0 activation marker names convergence run
37395942598 as its owner, and that run was deleted from Actions with the
rest of the 30 Sep - 8 Oct release history. The immutable marker
outlives
Actions history, so the reconciler and release-convergence.yml both
waited
forever on an owner that can no longer be read, and the permanently red
job hid real failures such as the rc.6 paid-broker miss.
GitHub only deletes a run that has completed or is older than two weeks,
which is beyond every convergence job timeout, so a definite 404 is a
terminal owner. Both the reconciler and the successor's adoption wait
now
proceed from the validated marker in that case. Any other read failure
still waits or fails.
Also remove the workflow_run retry job. Convergence runs are dispatched
with GITHUB_TOKEN, and GitHub does not deliver workflow_run for them:
the
job fired seven times ever, each for a run an operator started by hand,
and never after 29 Aug while dozens of bot-dispatched failures were
recovered by the hourly schedule. The schedule is documented as the
retry
mechanism.
v6.5.0 is served on every surface (Docker aliases at the marker digest,
Helm index entry, paid broker, demo /api/version), but no convergence
run
has proved it. The next scheduled pass dispatches a fresh v6.5.0
convergence; its demo surface will fail until the Tailscale OAuth client
behind TS_OAUTH_CLIENT_ID/TS_OAUTH_SECRET is rotated, as it returns 401.
The hourly "Reconcile release convergence debt" job has failed every run
since 13 Oct. The v6.5.0 activation marker names convergence run
37395942598 as its owner, and that run was deleted from Actions with the
rest of the 30 Sep - 8 Oct release history. The immutable marker outlives
Actions history, so the reconciler and release-convergence.yml both waited
forever on an owner that can no longer be read, and the permanently red
job hid real failures such as the rc.6 paid-broker miss.
GitHub only deletes a run that has completed or is older than two weeks,
which is beyond every convergence job timeout, so a definite 404 is a
terminal owner. Both the reconciler and the successor's adoption wait now
proceed from the validated marker in that case. Any other read failure
still waits or fails.
Also remove the workflow_run retry job. Convergence runs are dispatched
with GITHUB_TOKEN, and GitHub does not deliver workflow_run for them: the
job fired seven times ever, each for a run an operator started by hand,
and never after 29 Aug while dozens of bot-dispatched failures were
recovered by the hourly schedule. The schedule is documented as the retry
mechanism.
v6.5.0 is served on every surface (Docker aliases at the marker digest,
Helm index entry, paid broker, demo /api/version), but no convergence run
has proved it. The next scheduled pass dispatches a fresh v6.5.0
convergence; its demo surface will fail until the Tailscale OAuth client
behind TS_OAUTH_CLIENT_ID/TS_OAUTH_SECRET is rotated, as it returns 401.
pulse-pro gives a promotion run credentials only while that run is
still its current main. Any pulse-pro merge that lands while the run is
queued or validating, roughly 20 minutes, makes the refusal certain.
The convergence caller treated it like any other failure, so the
release stayed off the Pro broker until the hourly reconcile happened
to retry. That race caused five of the last six Pro promotion failures
(12, 17 and 24 October); on 24 October it held v6.6.0-rc.6 back from
Pro installs on the RC channel.
The refusal happens before credentials, so nothing was mutated. The
caller now re-dispatches on the new main when every failed job failed
only the source check and pulse-pro main has moved off the run's head,
up to five attempts in the existing one-hour budget. Every other
failure stays terminal, and only the terminal failure is annotated so
reconciliation still finds exactly one private run URL.
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.
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.
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.
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.
Preserve selected memory freshness and the VM/LXC interpretation.
Retain both native tool contracts and current API concurrency controls.
Qualify this source separately from release and installed recovery.
Contract-Neutral: Integrate already-governed parents without a new contract change. Incoming verification files already match main. Fresh integration tests and source-bound browser proof cover the combined memory and installer surfaces.
VM inventory was available only as an on-demand download. It can now be
scheduled weekly or monthly as CSV, emailed or saved on the Pulse
server. Each attempt exports all current VMs in the organization,
including newly discovered VMs, through the existing exporter.
Historical performance-report limits and windows remain separate.
The schedule uses existing delivery configuration checks, persisted
success/failure/uncertain states, bounded retries and deliberate
recovery controls. Pricing, feature gates, retention entitlements and
existing customer grants are unchanged.
Live checks also reproduced two unattended inventory defects: reporting
retained a stopped monitor after configuration reload, and canonical
snapshot reads relied on dashboard reads to publish polled VM inventory.
Reporting now receives reload replacements through an atomic manager
reference. Canonical snapshot reads reuse the existing publication
boundary, including its freshness fast path, provider/mode fences and
nonblocking warm reads.
Validation:
- 105 focused frontend tests; full type check, lint, production build
and bundle budgets.
- Race checks for reporting, inventory, reload, publication and
organization isolation; clean private runtime build.
- Current runtime browser checks on `/settings/support/reporting` and
`/alerts/notifications` at 1440×1000, 820×844 and 390×844, with actual
pixel review. The first inventory read exports 61 normally polled VMs;
real MIME CSV matches the download; a newly discovered 62nd VM enters
the next export.
- Local SMTP acceptance, 451 retry across a real restart, permanent
rejection, uncertain acknowledgement and deliberate recovery; disk
delivery, edit/reload, disable, details and delete.
- Staged status, registry, contract, completion and exact-content
browser guards pass. Regression proof is registered against the specific
runtime paths it covers.
Reuse the canonical inventory exporter and report delivery workflow so
operators can email or save a current organization-wide inventory weekly
or monthly. Keep delivery checks, bounded retries and recovery states
shared, with existing pricing and customer entitlements unchanged.
Refresh the shared inventory for unattended reads and rebind reporting
after monitor reload so schedules use the current infrastructure state.
The canonical completion guard checks each commit on its own; the
delivery commit relies on the following commit for its contracts and
proof files, so record that exact pair.
The canonical completion guard checks each commit on its own; the
classifier commit relies on the following commit for its ai-runtime
contract, so record that exact pair.
The canonical completion guard checks each commit on its own. The
admin split commit relies on the following commit for its dependent
agent-lifecycle and security-privacy contract updates and the route
inventory proof, so record that exact pair.
The hourly "Reconcile release convergence debt" job has failed every run
since 2026-10-13T03:57Z with a bare "GitHub object was not found". The
v6.5.0 activation marker names convergence run 37395942598 as its owner,
and that run (with the rest of the 30 Sep - 8 Oct release run history) was
deleted from Actions between 03:11 and 03:57Z on 13 Oct. The immutable
marker outlives the run, so the owner lookup now 404s.
Because the stable head's run sorts first and any error aborted the loop,
the failed v6.6.0-rc.2 convergence (38927798751) has never been reconciled.
Reconcile each selected channel independently and still fail the job if any
channel failed, so one channel's broken evidence cannot starve the other.
Name the deleted owner run explicitly instead of a generic 404, and do not
retry it: release-convergence.yml waits for that owner to read "completed",
so a fresh dispatch could only time out.
Send draft verification and fingerprint settings to the saved-credential
probe so Test connection reflects the edit form before saving. Preserve
saved defaults for callers without overrides and keep probe state temporary.
Keep shared footer actions visible on narrow screens so the edit and test
flow remains usable through pending, error and confirmation states.
Measure delivered extended history before changing free and paid features.
Count only successful responses containing an older metric point and retain
only bounded daily totals. Expose the rolling sum through schema v19 and
Preview with matching privacy disclosure and explicit report coverage.
Guest investigations could miss collected backup failures, and canonical
storage IDs could not retrieve metric history. Investigations now read
backup attempts through the same org-scoped recovery source as the HTTP
API and storage history through the monitor-owned source used by charts.
Responses preserve error text, timestamps, requested windows, read
failures and synthetic provenance. Dense history sampling also includes
the newest remainder observations, which the previous capped fixed-size
buckets discarded.
Regression coverage checks guest tool projection, persisted backup
attempts, resource and tenant isolation, demo-mode transitions, retained
storage history after restart, measured zero, newest observations in
dense series and API/tool coherence. Focused backend tests and both
private runtime builds passed on the validation worker. Frontend
type-check and build passed.
The separate backup inventory overview still uses legacy identifiers.
That remaining canonicalization work is recorded in the existing L6
architecture follow-up.
Browser verification exercised saved investigation summaries and
expanded tool evidence on `/patrol` at 1440×900 and 390×844, including
keyboard open/close, reopen and reload. This was a read-only view of
synthetic investigation records, not action or release qualification.
Guest investigations could miss collected backup errors and could not read
storage history through canonical resource IDs. Share recovery point reads
with the HTTP API and monitoring history with charts, preserving tenant and
resource scope, timestamps, provenance, window bounds, and read failures.
Cover manifest projection, persisted recovery tasks, demo source parity,
retained storage history after restart, and API/tool evidence coherence.
Alerts > History chose between its table and its phone cards by the
viewport: the table from 768px. With the Alerts rail open, a 1024px to
roughly 1240px window gives the table a 600px to 800px card, where its
own CSS stacks the three row actions and resource names and messages
break mid-word ("inventory-/api") or end after two lines.
The table section now measures its card with useObservedElementWidth and
shows the cards whenever the card is narrower than 800px, the width from
which the row actions share one line; before the card is measured, the
phone breakpoint decides as before. The cards take that decision as an
`active` signal instead of their own 768px media query, so an open
timeline or resource view still moves into the cards' drawer when they
take over and back to the inline expansion when the table returns. The
decision is a memo so a resize within one layout does not re-run that
move and reopen a drawer the operator closed.
Put published highlights and optional screenshot comparisons in the
post-update dialog, with grouped details available on demand.
Reuse inspected images from merged pull requests or capture fresh release
views, then publish versioned PNG sidecars. Serve screenshots through the
authenticated release endpoint to preserve the existing image policy.
The frequency chart drew every period in one blue under a legend of
coloured severity dots that described nothing in the bars, left info
alerts out of that legend, and used a floored log scale on which a
25-alert burst looked barely three times one alert.
Each period now stacks critical, warning and info in the legend's own
colours, counted with the alerts contract's closed severity vocabulary,
and bars scale by the square root of their share of the busiest period so
a burst stands clear while quiet periods keep a readable mix. The legend
counts all three severities, and the tooltip and accessible name carry
each period's breakdown. Light-theme warnings use amber-700 because
yellow on the chart surface was under 2:1, and a selected period is
ringed instead of dimming the rest below 3:1.
The chart's two test files are registered as alerts frontend proofs.
Own PR2936's stale pin and download-admission failures at b7e774d4. Refresh all current workflow consumers, including the newer pre-token issue-label job, with their exact static expectations and lifecycle/deployment contracts. Preserve Node 24, scoped lockfile caching, credential isolation and artifact identity/retention; reject old, mutable and arbitrary download pins.
Contract-Neutral: Immutable Actions pin refresh and coupled consumer/trust expectations only; workflow inputs, permissions, cache and credential controls, artifact identities and application/runtime contracts are unchanged.
Change-source: pulse-maintainer
The containing suite caught its old identity-step label assertion after the admission route changed. Require the new authenticated packet step and verify that authentication precedes OCI recovery; preserve the source-run binding and all downstream qualification checks.
Change-source: pulse-maintainer
The stable source Actions record currently returns 404 even though the published release is immutable. Do not require that transient record for post-activation chart recovery: authenticate the exact consumed activation marker and its canonical hosted source provenance with the existing packet verifier, then retain release, source-run and chart bindings. OCI provenance and public package readback remain mandatory. Add executed offline admission/refusal regressions and the corresponding deployment contract.
Change-source: pulse-maintainer
Recorded run 38566953125 at 86a244bc stops on an HTTP 404 but reports only api -H Accept because the adapter truncates its argv before the endpoint. Preserve a bounded managed-resource path in API failure diagnostics, without query filters. Keep the same requests, credential selection, error classes, retry policy and fail-closed decisions. Add exact failed-read and no-mutation regressions.
Contract-Neutral: Failure diagnostics only; no admission, identity, credential, retry, mutation or release contract delta.
Change-source: pulse-maintainer
Current exact-head CI exposes outdated PBS proof inventory, omitted bounded-download helper extraction, closed transport rejecting the production HTTPS flags, and Darwin's truncated comm column in Linux NAS fixtures. Preserve exact transport and process ownership, restoration and migration safety assertions while binding the controls to the actual producer.
Contract-Neutral: Test and offline CI-fixture alignment only; no installed runtime, trust, credentials, deadlines, source safety or public contract changes.
Change-source: pulse-maintainer
Expect the complete reviewed PBS proof list. Collect full-width real process rows in the private NAS watchdog fixture, retaining exact ownership, deadlines and failure evidence instead of truncating long Darwin paths.
Contract-Neutral: Test fixtures only; production installers, process ownership checks, retention, registry policy and runtime behaviour are unchanged.
Change-source: pulse-maintainer
The containing install-metadata gate requires the existing companion-release disclosure. Retain it alongside, not instead of, the approved retirement notice; do not relax the gate or change the served runtime. Preserve the failed first proof and verify the final notes and actual producer.
Contract-Neutral: Notes-only compatibility disclosure and its assertion; no runtime, protocol, release identity, asset, maturity, latest pointer or cadence change.
Change-source: pulse-maintainer
Use the stable-based source comparison rather than a contradictory immutable changelog introduction. Preserve the reviewed bounded monitoring outcomes and the explicit guest-safety limitation, and include the approved Mobile retirement notice in the existing beta correction. Regression assertions bind the exact notice and comparison without changing release identity, assets or cadence.
Contract-Neutral: Published-note maintenance and its regression assertions only; no runtime, protocol, release identity, assets, maturity, latest pointer or cadence change.
Change-source: pulse-maintainer