Contract-Neutral: Responsive API-token presentation and a fingerprint-scoped scanner false-positive exception; no security contract, permission, or API behavior changes.
Contract-Neutral: Responsive Proxmox Backups table presentation and verification only; backup ownership, data contracts, and extension boundaries are unchanged
.husky/pre-commit and .husky/pre-push are tracked 755 but
prepare-commit-msg was 644. In the main checkout that never shows, because
git runs husky's generated executable .husky/_ shim, which sources this
file regardless of its own mode.
Committing from a linked worktree is different. The gitignored .husky/_
does not exist there, so the documented workaround is
git -c core.hooksPath=.husky commit, which points git straight at this
file. Git then skips it as not executable, prints a one line hint, and
commits anyway.
That hook is what records PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT as the
Contract-Neutral trailer. Without it a bypassed worktree commit lands with
no trailer, passes every local check including Pre-commit checks passed,
and is then failed by the canonical governance workflow, which reads the
trailer back per commit. The only signal is a red CI run later.
Marking the file executable makes the tracked hook run directly under
core.hooksPath=.husky. Behaviour in the main checkout is unchanged, since
the shim path is unaffected.
The docs sync test pins frontend-modern/public/docs to the repo docs
and 85136c582 only updated the repo copy, which broke Frontend CI on
main.
Refs #1635
The row dot resolved through getGuestPowerIndicator, which only knows whether
the hypervisor says the guest is powered on. A guest that is up while the
service behind it stops answering showed a plain green Running dot, so the dot
disagreed with the Attention bucket that already accounts for the same probe.
getGuestHealthIndicator layers the probe result on top of the power state and
the row now uses it, so a running guest whose attached probe cannot reach it
reads as needing attention in both places.
The predicate that decides this moves to availabilityProbePresentation and is
now shared by the indicator and the status bucket, rather than the guard
conditions being written twice. Only attached correlations count, the failure
threshold mirrors the gate the availability poller uses before raising
availability_unreachable, and a guest that is not running keeps its power
state, since a probe says nothing useful about a guest nobody expects to
answer.
The availability check remains the sole owner of the incident, the alert and
the history. Nothing new is minted here.
Verified against the live backend across 34 guests and 36 passing attached
checks with no false positives. The degraded path is covered by unit tests
proven red without the change.
Contract-Neutral: Workloads guest-row presentation only. Reads the availability facet already projected onto the guest and mints no incident, alert, or history, so the source-owned check contract from the 2026-07-23 availability check identity record is untouched. No backend, payload, or subsystem boundary changes.
The security status endpoint truncates by authority and only a
privileged caller receives the settings-derived posture fields. The
Security Overview page scored truncated responses anyway, reading every
absent field as No and showing non-admin RBAC roles a wrongly critical
posture. Gate the posture summary and hardening actions on the
privileged detail level and explain the admin requirement instead.
The banner half of this report was already fixed for #1650.
Refs #1675
Contract-Neutral: behavioral fix: gate Security Overview posture on privileged detail level so truncated responses stop rendering false negatives, no contract delta (#1675)
Contract-Neutral: Responsive Proxmox Storage table presentation and verification only; storage ownership, data contracts, and extension boundaries are unchanged
A full monitor reload (triggered by every node settings save) starts
from empty host state, and the physical disk poll runs on the first
cycle. Evaluating disk alerts before a node's agent has re-reported
treats the agent's --disk-exclude patterns as absent, so excluded disks
fired wearout/health alerts that the next cycle resolved. Defer disk
alert evaluation for unlinked nodes during a two minute settle window
after monitor start.
Refs #1674
Contract-Neutral: behavioral fix: defer disk alert evaluation until host-agent linkage settles after reload, no contract delta (#1674)
A reporter on #1635 found that Assignment required alone does not stop
Entra emitting every security group when the app manifest still lists
SecurityGroup in groupMembershipClaims. Add the manifest check to the
group-overage warning.
Refs #1635
A Proxmox guest whose availability probe cannot reach it kept a running
bucket, so it stayed out of the Attention filter and read as healthy while
the service behind it was down. The hypervisor only knows the guest is
powered on, and resolveWorkloadStatusBucket read only resourceStatus, which
never carries the probe result.
The availability check remains the sole owner of the incident, the alert and
the history, per the availability check identity record of 2026-07-23. This
mints nothing new. It reads the facet already projected onto the guest and
stops that guest from being counted as healthy.
Only attached correlations count, since a standalone, ambiguous or unresolved
probe has not been proven to describe the guest, and the failure threshold
mirrors the gate the availability poller uses before raising
availability_unreachable, so the bucket and the alert agree. A stopped guest
keeps its own bucket because a probe says nothing useful about a guest nobody
expects to answer.
Verified against the live backend across 36 attached checks with none
failing, confirming no false positives. The failing path is covered by unit
tests proven red without the change.
Contract-Neutral: Workloads status-bucket presentation only. Reads the availability facet the guest already carries and mints no incident, alert, or history, so the source-owned check contract from the 2026-07-23 availability check identity record is untouched. No backend, payload, or subsystem boundary changes.
The cell had no test coverage. Locks the shipped behaviour so the response
time cannot be dropped by accident and the failure reason keeps replacing it
when a probe stops answering.
Both columns spent permanent width rendering the healthy state, so a fleet with
nothing wrong read as two columns of identical repeated values.
Avail printed a freshness suffix on every probed row. Freshness is Pulse
vouching for its own probe pipeline rather than a fact about the guest, and it
reads fresh on almost every row, so it now renders only when the probe data is
stale or of unknown age. Avail also becomes toggleable. It was the only
permanently pinned column in the table, which made operators who run no
availability probes pay 56px for mostly empty cells with no way to hide them.
Backup drew a bordered, filled pill with a compact age on every guest. The
shield colour already carries the verdict and the configured threshold already
decides what counts as acceptable, so the age was decoration on the rows nobody
has to act on. A fresh backup is now a bare green shield, and the age, the pill
chrome and the alerting colours are reserved for stale, critical and never. The
exact timestamp stays one hover away in the tooltip and the age stays in the
aria-label, so nothing is lost for assistive tech.
Verified against the live backend at 1440x900 and 390x844 across 34 guests.
Contract-Neutral: Workloads guest-row presentation only. GuestRowCells.tsx and guestRowModel.tsx are the contracted owners for cell presentation and column metadata in performance-and-scalability.md and this change extends them in place, keeps the shared workloadGuestPresentation helpers required by frontend-primitives.md, and moves no contract section, width rule, or payload field.
The mock:on and mock:off scripts used GNU 'sed -i' syntax, which fails on
the default macOS BSD sed. The '||' fallback then appended a fresh
PULSE_MOCK_MODE line on every invocation, so a local .env accumulated three
contradictory entries.
They also targeted the repo-root .env, which hot-dev.sh does not consult
when choosing the data directory. hot-dev.sh reads the canonical flag from
tmp/dev-config/.env, written authoritatively by toggle-mock.sh. The npm
wrappers were therefore a no-op that corrupted .env as a side effect, while
leaving the operator believing mock mode had switched.
Point them at toggle-mock.sh, which handles BSD sed, writes the canonical
file, syncs the runtime env and restarts the managed runtime. Add
mock:status and mock:edit; the hot-dev startup banner already advertised
'npm run mock:edit', which did not exist.
Extend the deployment-installability contract to cover the mock wrappers
alongside the existing repo-root dev entry rules, and add a regression test
asserting the wrappers delegate rather than rewriting the flag inline, and
that every mock command named in the hot-dev banner exists.
The three deep-dive documents describe enumerations and tuned constants that
live in code. Prose cannot notice when a fifth state or an eighth signal type
is added next to it, so the documents would have rotted silently.
These guards derive the truth from the source rather than asserting literal
strings. A test that only checks the document contains RESOLVING keeps
passing when a new state appears, which is the drift actually worth catching.
Covered, in both directions where the set is enumerable. Session states and
tool kinds against internal/ai/chat/fsm.go and the ToolCallKind String
method. Patrol signal types and the five tuned signal thresholds against
internal/ai/patrol_signals.go. The investigation attempt limit and cooldown
against internal/ai/findings.go. The look-before-asking bound and the tool
concurrency cap against internal/ai/chat/agentic.go. Identifiers named in the
structured errors section against the declared error codes and tool names.
Error codes are checked document to code only. The document names a
representative sample rather than all 45 declared codes, so requiring the
reverse would force a table that churns on every addition while catching
nothing a reader cares about. A rename or removal still fails.
Writing the guard immediately found a real mistake in the document it
guards. ErrCodeFSMBlocked is FSM_BLOCKED, not fsm_blocked as the prose
claimed, corrected here.
Each guard was verified to fail by adding a fifth session state, adding a
signal type, and changing a threshold, then confirming the failure named the
document and the value.
Contract-Neutral: test and documentation only
d00249fd7 added frontend-modern/src/pages/__tests__/Alerts.readOnly.test.tsx
without adding it to the pinned proof list in the canonical completion
guard, so the guard's expected list no longer matched what the registry
derives. canonical-governance has been red on main since that commit, and
the pre-commit hook has been refusing every commit locally for the same
reason. Six commits landed in that window, including the v6.2.0-rc.7 release
preparation.
The delta is one file added and none removed, so this restores the snapshot
rather than lowering the bar. A removal would have meant proof coverage was
dropped, which is a regression to investigate rather than a pin to update.
Contract-Neutral: governance pin restored to match the registry
AI.md, AI_AUTONOMY.md and PULSE_PRO.md linked nine times into
docs/architecture/, which .gitignore marks as not for public release. The
targets were never missing, they were deliberately unpublished, so every one
of those links was dead for anybody but the maintainer.
Write the three promised documents against the code and publish them under
docs/ where the rest of the shipped set lives.
PATROL_ARCHITECTURE.md covers a run end to end. The interesting part is that
deterministic signal detection runs after the model, not before, so unmatched
signals catch what the model failed to file. Signal types, the thresholds
those signals derive from your own alert settings, and every condition in
Finding.ShouldInvestigate are documented from internal/ai/patrol_signals.go
and internal/ai/findings.go.
ASSISTANT_SAFETY.md documents the session state machine in
internal/ai/chat/fsm.go, its four states, the tool classification it runs on,
and its invariants. No write without a validated target, no second write
before the first is verified, no final answer about an unverified change, and
no attempt count that wears the gate down.
ASSISTANT_ARCHITECTURE.md covers the agentic loop around that machine, the
three-phase pipeline, why only execution parallelises and at what cap, the
read-before-write batch that must stay ordered, the look-before-asking gate
and its bound, and the stable error codes.
Also removed two older pointers into the same private directory, in API.md
and UPGRADE_v6.md, and the two references to ENTITLEMENT_MATRIX.md in
PULSE_PRO.md. That file exists locally and is a monetization document, so
publishing it is not a documentation decision.
Unresolvable intra-doc links are now 2 of 218, both internal release-control
documents deliberately withheld from the shipped set.
Contract-Neutral: documentation only
This document is linked from Settings, API Access, Agent integrations, so
its reader is somebody trying to connect an MCP client to their own
instance. It was written as a note to the maintainer, opening by describing
what landed on a branch and offering itself as a reminder in three weeks.
Rewritten for that reader. The endpoint reference, error codes and adapter
details are unchanged in substance, since an integrator needs all of them.
What changed is who the prose is addressed to.
Three things were cut rather than reworded because they were about building
Pulse rather than using it.
The Provable claims section listed test function names, referenced numbered
work slices, and recounted that discovery used to return 401 before a fix.
The guarantees underneath were worth keeping, so they are stated as
guarantees now, that the manifest matches the implementation, that
discovery needs no token, that error codes come in two layers, and that the
surfaces are exercised together. The test names that enforce them are not
the reader's concern.
Where to read more pointed at internal subsystem contracts under
docs/release-control, which are deliberately not shipped, and at Go and TSX
source paths. It now points at Configuration, API and AI, which are all
shipped and all resolve in the viewer.
The roadmap sentence about the next work item being whatever friction first
usage surfaces became an invitation to open an issue. The Gatekeeper
warning stayed, because that one is something a reader actually hits.
Title is now Pulse agent integrations, matching the panel the reader
arrives from, rather than substrate, which is not a word they saw.
Contract-Neutral: documentation rewrite, no contract delta
The documentation index links both, and the viewer resolved them to routes
with no asset behind them, so following either reached the not-shipped
state. Both are public repository documents, so ship them alongside the
rest and let the links work.
They live at the repository root rather than under docs/, so the sync test
maps them the way it already maps SECURITY.md and TERMS.md.
CONTRIBUTING.md links as docs/AI_TRANSPARENCY.md, which is the path from
the repository root. Shipped flat into the docs root that prefix has to
collapse rather than nest into /docs/docs/, which the resolver already did
via normalizeDocPath. Pinned with a test now that it is a shipped case
rather than an incidental one.
Both were read in full before shipping. Neither carries internal governance
content or credentials beyond the documented local dev defaults that are
already public in the repository.
Unresolvable intra-doc links are now 11 of 202, down from 23. Nine are
references to docs/architecture/ files that do not exist in the repository
at all, and two are the internal release-control documents deliberately
withheld from the shipped set.
Contract-Neutral: ships existing public documentation as static assets
Documentation links opened the raw .md asset, which browsers display as
plain text, so operators got markdown source complete with ## markers,
asterisks and unclickable [label](TARGET.md) syntax. Shipping the full set
made the content right without making it readable.
Add a viewer at /docs/<path> that renders the markdown. The raw asset stays
at /docs/<path>.md, which is what the viewer fetches, so nothing that wants
the source loses it, and the extensionless route cannot collide because
every shipped file ends in .md.
Notes on the pieces that are not obvious:
- The renderer is separate from the AI chat's. That one sets breaks: true
globally, which would break every hard-wrapped paragraph in the shipped
set, and pins hrefs to http/https/mailto, which would strip the relative
links holding the documentation together. Options are passed per parse
call so neither renderer disturbs the other. DOMPurify still gates output.
- Intra-doc links are rewritten to viewer routes and navigate in-app. Links
climbing above the docs root, such as ../SECURITY.md, are clamped to it,
because those files ship flat and the link only made sense on GitHub.
- A document that is not shipped does not 404. The static handler falls back
to index.html for any unmatched non-API path including one ending in .md,
so the viewer treats an HTML content type as missing rather than rendering
the application shell as markdown.
- The route is public because the underlying markdown is already served
unauthenticated. Gating the readable form would make it less reachable
than its own source.
- Tables get their own scroll container and inline code is allowed to break,
otherwise a wide table or a long URL pushes the whole page into horizontal
scrolling on a phone. Verified by measuring scroll width at 390 pixels.
The doc URL constants now resolve to the viewer route, so the assertions
pinning the old .md paths were updated to match. That is the change being
made rather than a baseline being quietly moved.
Contract-Neutral: frontend rendering of existing static assets
The second thing a user read in the documentation portal was the v6
Execution Canonical Source section, telling them to consult
release-control internal files, run status_audit.py, registry_audit.py and
contract_audit.py, and observe pre-commit partial-staging rules for
.husky/pre-commit and canonical-governance.yml. That is agent and
maintainer execution guidance, and it means nothing to somebody who has
just installed Pulse. The docs are shipped and served raw under /docs, so
it reached self-hosted operators as written.
Move it into SOURCE_OF_TRUTH.md as an Execution Entry Point subsection,
which is where the section already pointed and which is excluded from the
shipped docs set. Nothing is lost: the three facts that lived only in the
index, contract_audit.py, the hook-sensitive partial-staging list, and the
substantive-contract-section rule, are all carried across, and every other
reference already existed under Canonical Control Files.
The operator index now runs from its welcome line straight into Getting
Started.
The in-app docs index listed 41 documents while only 9 were shipped, so
anything a self-hosted operator reached under /docs/ that was not one of
those 9 returned 404. Copy the 39 reachable documents from docs/ into
frontend-modern/public/docs so the set the index describes is actually
present offline, which is the point of shipping docs with a self-hosted
product rather than linking GitHub.
Deliberately not shipped: docs/release-control/v6/internal/
RELEASE_PROMOTION_POLICY.md and docs/releases/V6_PRERELEASE_RUNBOOK.md,
which are internal release governance rather than operator documentation.
Every shipped file was checked for GitHub blob/main links, which the sync
test forbids, and for internal-only content.
The sync test now derives its pairs from what is actually in public/docs
instead of a hand-maintained list, so a copied doc cannot silently drift
from its source and a new one cannot be added without one.
Known remaining gaps, all pre-existing: nine references to
docs/architecture/ files that do not exist in the repository, and parent
relative links such as ../SECURITY.md that resolve on GitHub but not under
the shipped /docs/ root.
Contract-Neutral: ships existing documentation as static assets