The MCP adapter shipped in slice 51 with one install option:
clone the repo and go build. This slice integrates pulse-mcp
into Pulse's existing governed release pipeline so a Pulse
release publishes a pulse-mcp binary alongside the unified agent
and the install scripts that bring it home in one command.
What ships:
- scripts/build-release.sh extended to build pulse-mcp for
the same multi-OS matrix as the unified agent, package
per-platform tarballs and zips, and copy bare binaries to
RELEASE_DIR for /releases/latest/download/ redirect
compatibility.
- .github/workflows/create-release.yml extended to upload
the bare pulse-mcp binaries plus install-mcp.sh and
install-mcp.ps1 as release assets.
- scripts/install-mcp.sh: bash one-line installer that
detects platform/arch, downloads the matching binary from
the configured release (latest by default), verifies SHA256
against the published checksums.txt, places at
~/.local/bin/pulse-mcp (or /usr/local/bin if not writable).
Honors PULSE_MCP_VERSION, PULSE_MCP_BIN_DIR, PULSE_MCP_REPO,
PULSE_MCP_NO_VERIFY env vars; declines Windows shells with
a pointer at the .ps1 sibling.
- scripts/install-mcp.ps1: PowerShell installer for Windows,
placing pulse-mcp.exe at $LOCALAPPDATA\pulse-mcp.
Documentation aligned:
- cmd/pulse-mcp/README.md gains an Install section above
Quick start with three options: one-line installer,
GitHub Release download, go install. Documents the macOS
Gatekeeper bypass since v1 is unnotarized by design.
- The Settings -> API Access agent-integrations panel now
surfaces the curl|bash command above the config snippet so
operators see "install pulse-mcp" before "configure your
MCP client."
- docs/releases/AGENT_PARADIGM.md drops the "no published
distribution path" item from "what it does not do yet" and
documents the Gatekeeper / Homebrew gaps as next-tier
follow-ups.
Trade-offs surfaced and chosen:
- Same cadence as Pulse: pulse-mcp ships per Pulse release,
not on its own track. The MCP server reads the manifest
from the Pulse it talks to, so version alignment is the
natural model.
- No Homebrew tap or core formula in v1. Maintaining a tap
is real ongoing work; foundation supports adding Homebrew
later as a layer.
- No Docker image. Stdio JSON-RPC fights Docker's stdin
/stdout pattern.
- No notarization in v1. SHA256 verification through the
installer preserves the audit trail; README documents the
Gatekeeper bypass.
Subsystem contract: deployment-installability.md gains
scripts/install-mcp.sh, scripts/install-mcp.ps1, and
cmd/pulse-mcp/ in canonical files (mid-list entries
renumbered) plus a paragraph documenting the new MCP entry
point alongside the existing installer family.
Verification artifacts:
- scripts/installtests/build_release_assets_test.go gains
TestBuildReleasePackagesPulseMcpForAllPlatforms which pins
the build/package/copy wiring and the load-bearing
install-mcp.sh helpers (platform detection, SHA256
verification, install-dir resolution).
- scripts/release_control/render_release_body_test.py gains
test_agent_paradigm_release_notes_blurb_documents_-
distribution_path which pins the AGENT_PARADIGM.md draft's
install-mcp.sh reference and the four-axis frame so a
future edit cannot regress the install story silently.
Smoke-tested install-mcp.sh locally on darwin-arm64: platform
detection, install-dir resolution, URL building, and 404 error
handling all correct. The full end-to-end install path becomes
live the moment a Pulse release ships pulse-mcp binaries; the
next RC cut will exercise it.
|
||
|---|---|---|
| .. | ||
| backfill-release-assets.yml | ||
| build-and-test.yml | ||
| canonical-governance.yml | ||
| close-needs-retest-timeout.yml | ||
| create-release.yml | ||
| deploy-demo-server.yml | ||
| eval-model-matrix.yml | ||
| helm-ci.yml | ||
| helm-pages.yml | ||
| issue-version-label-sync.yml | ||
| issue-version-retest-comment.yml | ||
| promote-floating-tags.yml | ||
| publish-docker.yml | ||
| publish-helm-chart.yml | ||
| README.md | ||
| release-dry-run.yml | ||
| repo-boundary-audit.yml | ||
| test-e2e.yml | ||
| test-updates.yml | ||
| update-demo-server.yml | ||
| validate-release-assets.yml | ||
GitHub Actions Workflows
Issue Triage Automation
Files:
issue-version-label-sync.ymlissue-version-retest-comment.yml
Issue intake is split deliberately:
issue-version-label-sync.ymlis the silent metadata path. It runs onopened,edited, andreopenedissue events so version labels,needs-version-info, andneeds-retest-on-lateststay correct when maintainers tidy issue metadata.issue-version-retest-comment.ymlis the public guidance path. It only runs onopenedandreopened, and only posts reporter-facing retest guidance when the issue is an older-version bug report from a non-maintainer.- Both workflows load the shared helper at
.github/scripts/issue-version-triage.cjsso parsing and classification logic lives in one place instead of drifting across duplicated inline scripts.
Update Demo Server
File: update-demo-server.yml
Automatically updates the governed demo target after a release is published. Stable releases update the stable public demo. Prerelease tags update the separate v6 preview demo.
Configuration Required
Create two GitHub Environments:
demo-stabledemo-preview-v6
Each environment must define the same secret names so the workflow can select the target by environment instead of hardcoding separate workflows.
Required environment secrets:
-
DEMO_SERVER_SSH_KEY
- The private SSH key for accessing the demo server
- Generate with:
cat ~/.ssh/id_ed25519(or your key file) - Should be the full private key including
-----BEGINand-----ENDlines
-
DEMO_SERVER_HOST
- The hostname or IP of the demo server
-
DEMO_SERVER_USER
- The SSH username for the demo server (e.g.
rootor a deploy user with sudo access)
- The SSH username for the demo server (e.g.
Required shared secret:
- TS_AUTHKEY
- Tailscale auth key used by the governed demo deploy/update workflows before SSH
- Allows GitHub-hosted runners to reach private demo targets such as the stable
pulse-relayTailscale host - May be stored as a repository secret or repeated in the selected environment if desired
Required environment variables:
-
DEMO_EXPECTED_HOSTNAME
- The remote
hostnamevalue the selected environment is expected to report - Stable example:
pulse-relay - Preview example:
pulse-v6-preview - This is a host-identity guard: the workflow fails closed if the SSH secret points at the wrong machine
- The remote
-
DEMO_LOCAL_BASE_URL
- Local URL used on the target host for version and mock-mode verification
- Example stable value:
http://localhost:7655 - Example preview value:
http://localhost:8665
-
DEMO_PUBLIC_HEALTH_URL
- Public health endpoint for the selected demo target
- Example stable value:
https://demo.pulserelay.pro/api/health - Example preview value:
https://v6-demo.pulserelay.pro/api/health
Optional environment variables:
-
DEMO_SERVICE_NAME
- Stable default:
pulse - Preview example:
pulse-v6-preview - When set, the server installer derives the instance-specific install dir, config dir, update helper, and update timer from this service identity.
- Stable default:
-
DEMO_AUTH_USER / DEMO_AUTH_PASS
- Demo credentials used for post-update mock verification
- Defaults to
demo/demowhen omitted
How It Works
- Trigger: Runs automatically when a GitHub release is published
- Target selection: Stable tags deploy to
demo-stable; prerelease tags deploy todemo-preview-v6 - Service identity guard: Preview runs default to
pulse-v6-previewand refuse to target the stablepulseservice identity - Governance check: Validates the selected tag is reachable from the governed release branch for that version
- Latest check: Refuses to update a target unless the published tag is the latest release for that target channel
- Network attach: Joins Tailscale before any SSH step so governed demo targets can stay on private hostnames or Tailscale IPs
- Update: SSHs to the selected demo host and runs the tag-matched root installer from that exact git tag
- Host identity check: Verifies the SSH target reports the governed expected hostname before running installer or deploy steps
- Verify: Checks that the new version is running, mock mode is active, and the public demo HTML serves the same frontend entry asset as the target service
- Browser smoke: Uses the governed Playwright helper to prove the public demo still renders the login shell in a real browser
- Cleanup: Removes SSH key from runner
Testing
To test without publishing a release:
- Go to
Actionstab in GitHub - Select
Update Demo Serverworkflow - Provide a tag and choose
stable,preview-v6, orauto
Benefits
- ✅ Stable and preview demos stay on separate governed targets
- ✅ Prereleases no longer require a stable demo overwrite or a manual skip
- ✅ Validates the real server installer path on the selected target
- ✅ Removes release-operator guesswork about which demo should move
Preview Bootstrap Note
The preview environment must be bootstrapped once on the host before the update
workflow can keep it current. The supported path is a separate service identity
such as pulse-v6-preview plus a separate public route such as
v6-demo.pulserelay.pro; do not reuse the stable pulse.service instance.
Deploy Demo Server
File: deploy-demo-server.yml
Manually deploys the current branch build to either the stable or preview demo environment without changing the governed release workflow.
- Uses the same
demo-stable/demo-preview-v6environment contract as the release-driven updater - Joins Tailscale before SSH so governed demo targets can stay on private addresses instead of requiring public runner reachability
- Requires
DEMO_EXPECTED_HOSTNAME,DEMO_LOCAL_BASE_URL, andDEMO_PUBLIC_HEALTH_URL - Supports optional
DEMO_SERVICE_NAME,DEMO_INSTALL_DIR,DEMO_TEST_PORT,DEMO_AUTH_USER, andDEMO_AUTH_PASS - Assumes the target service and install directory already exist on the host
- Defaults preview runs to
pulse-v6-previewand refuses to target the stablepulseservice identity - Verifies the SSH target reports the governed expected hostname before deploy
- Verifies that the public demo shell serves the same frontend entry asset that was built and deployed
- Uses
scripts/run_demo_public_browser_smoke.shto prove the public demo still renders the login shell in Chromium after deploy/update verification
Helm CI
File: helm-ci.yml
Runs helm lint --strict and renders the chart with common configuration combinations on every pull request that touches Helm content (and on pushes to main). This prevents regressions before they land.
- Triggered by PRs/pushes touching
deploy/helm/**, docs, or the workflow itself - Uses Helm v3.15.2
- Renders both the default deployment and an agent-enabled configuration to catch template issues
Publish Helm Chart
File: publish-helm-chart.yml
Packages the Helm chart and pushes it to the GitHub Container Registry (OCI) whenever a GitHub Release is published. Also makes the packaged .tgz available as both an Actions artifact and a release asset. The same behaviour can be triggered locally via ./scripts/package-helm-chart.sh <version> [--push].
- Triggered automatically on
release: published, or manually via workflow dispatch (requireschart_versioninput) - Chart and app versions mirror the Pulse release tag (e.g.,
v4.24.0→4.24.0) - Publishes to
oci://ghcr.io/<owner>/pulse-chart - Requires no additional secrets—uses the built-in
GITHUB_TOKENwithpackages: writepermission