Skip to content

Track rolling deploys per node, report releases and deploy provenance, and expire failed payloads - #3502

Merged
kriszyp merged 42 commits into
mainfrom
claude/deploy-641-followups-1d6af4
Oct 26, 2026
Merged

kriszyp merged 42 commits into
mainfrom
claude/deploy-641-followups-1d6af4

Conversation

@dawsontoth

@dawsontoth dawsontoth commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

A badger shows woodland friends parcels reaching treehouses

⊙ Problem

Deployment tracking (#641) left gaps. A rolling deploy's record, and harper deploy restart=rolling, reported success before other nodes took the release. Nothing reported deploy or restart times, a node's kept releases, or a deploy's origin. Upgraded installs missed newer hdb_deployment attributes. A truncated payload could report success. Failed payloads were kept forever. An internal event leaked into deploy history.

🤔 Does all of this belong in one PR?

Asked for as one PR, one commit per issue, so each reads and reverts on its own. The commits share components/deploymentRecorder.ts and components/DESIGN.md, which is why they were not split.

💡 Solution

  • A rolling deploy's record stays rolling_out until every node took the release, then ends success or partial naming the nodes that did not. harper deploy waits on that record.
  • get_components gives each application deployment_id, deployed_at and restarted_at; new list_releases lists each node's releases.
  • Deployment rows record source, attested by OIDC or declared by the CLI.
  • A deploy warns before going live when a declared jsResource/graphqlSchema entry point is missing.
  • Failed payloads expire; upgrades register new system-table attributes.

🤔 Should a missing entry point fail the deploy (#1719)?

It warns, so #1719 stays open. Failing broke a tested workflow: redeploying to delete an application's only resource file, whose config still names it (redeploy-restart-flag-deletion). Making it fatal, ending that workflow, is one line.

⚖️ Alternatives

🔧 Changes

Left for later: #1719's fatal check (above); an uncertified restart=rolling still records success before its peers restart, and the CLI does not wait on it; a payload's install fingerprint still has no source (#3061); a failed payload whose node left the cluster is never expired; a release kept before 5.4 reports staged. Docs need a companion PR in HarperFast/documentation.

🤔 Should the rollout's state live on the deployment row?

Yes: get_deployment and list_deployments already read the row, and it replicates. The cost is write ordering, handled by the job's wait and spacing and a node-scoped lock on the row. Separate per-peer records would avoid contention but give readers two places to look.

🤔 Should harper deploy restart=rolling wait by default?

Yes: #3058 asks that CI not pass before every node took the release. A CI step that exited at the origin's swap now waits up to ten minutes per node and can fail; wait=false keeps the old exit.

🤔 Should restarted_at count a load that failed?

Yes: it says when this node's workers last loaded the application; status says whether that worked. Renaming it later would break callers.

🤔 Should list_deployments { sha } match declared sources?

Yes: each row's source.via says whether a CI exchange attested it. Matching only attested sources would hide every deploy from a laptop.

Per issue: what changed, with links

#3062, the recorder's internal event. finish() no longer emits _recorder_finished; the registry of active deploys holds a promise it settles after the terminal write. The registry entry is removed only after that write. The get_deployment tail awaits it, or its client's disconnect, and re-reads the final row.

#2659, attributes on upgraded installs. registerSystemSchemaAttributes adds every attribute systemSchema.json declares that an existing system table lacks, indexed like a fresh install. The 5.4.0 directive runs it (registered). mount_hdb and the 5.1, 5.2 and 5.3 directives now pass table() a copy of the schema's attribute list, since table() kept the bundled array as the live list. The rule is in dataLayer/DESIGN.md; systemSchema.json gains restart_job_id and source.

#564, deploy and restart times. getComponents reads the live marker (liveDeploymentIdOf) and finds the record that made it live by key and by the activated_from index, not a scan of the history; a catalog that cannot search that index yet keeps the record found by key. A tree with no marker takes the newest row of each released status, so no run of failed attempts hides it. restarted_at comes from loadedAt, set when a thread finishes loading, kept across reports, aggregated across threads and sub-components (types).

#3060, peer outcomes on the record. The origin finishes a rolling deploy as rolling_out with restart_job_id. The activation job records each peer and ends the record success or partial. The origin's own write keeps what the job already stored, and a boot ends records whose job was interrupted (at boot). ⚠️ Look hardest: the job waits for the origin's rolling_out write before its first; the last peer's outcome, and any whose own write failed, rides the final write and every write waits a second after the previous one, so writes to the row never land as a burst (#1170). Each read-merge-write, the origin's and the job's, holds the row's node-scoped lock, since a conflict retry replays a put without rerunning its merge (LMDB has none). The final write retries, then fails the job with every outcome in its record, which the boot pass reads back. partial is terminal. The job loads the cluster's node list itself: a job worker thread starts with an empty server.nodes, so until this branch a certified rolling deploy activated on no peer of a real cluster, on main too. Only core's replication stand-in means no peers, and a job that fails before it can finish the record ends it partial (abandonRollout).

#3058, the CLI waits. awaitRollout polls get_deployment with backoff, prints each node's outcome, ends only on an ending status, since a lagging node still shows the origin's earlier one, stops at once on a refusal and times out at ten minutes per node. followRollout reuses the deploy's connection and token, ends each read at the wait's deadline however slowly its reply trickles, and the CLI exits non-zero; wait/wait_timeout are kept off the wire and checked first. A read the wait's own end cut short is not reported as a failed read. The CI role grants get_deployment.

#3059, releases per node. list_releases (replication settled at once) reports each component's live id and kept releases with state, time and size, over every component with a tree or a build; replicated: true adds each node's answer. A displaced release is marked kept in its .complete content. Registered in hdbTerms, serverUtilities (import), operation_authorization (import) and the MCP schema and description.

#3061, where a deploy came from. The OIDC exchange signs the policy and run claims into the token (createOperationToken), validation lifts it onto the principal (claim), and impersonation carries it. deploymentSource picks attested over declared and forces a declared one to say so; deployComponent records it on the row. The CLI declares the Actions run and the checkout commit (wired). list_deployments filters by sha.

#1719, entry points. missingEntryPointsWarning checks each declared pattern, reading negations as exclusions, and its glob walk, with the loader's only: filters, stops at the first match. It runs in the deploy's validateCandidate, before the swap, and the response message carries the warning.

#2681, failed payloads. sweepFailedPayloads ranks every node's failed rows per project but drops only its own, applies the bounds (config names), runs after a failure is recorded and once at boot.

#3096, an upload whose pipe loses its destination. watchForAbandonment re-checks a part held after the response after its own unpipe() or when it loses a data listener, armed from the response hook.

Design notes. components/DESIGN.md gains a section on the deployment record (each item above) and one on entry points; also server/DESIGN.md, security/DESIGN.md and the index.

Docs. HarperFast/documentation#751 documents these changes for 5.4.

✅ Verification

Three-node scenarios and gates
  • A Linux container, with this branch's CLI. wait_timeout, wait=false, an origin killed mid-rollout (settled partial at its restart), the entry-point warning, list_releases, per-node get_components, the sha filter and failed-payload expiry all behave as documented.
  • At 5c2d664: affected suites pass on dist, LMDB and type-stripped source.
  • After the activation fix: the bin, components and serverHelpers unit suites pass (3,100; OptionsWatcher fails on base here too), and canary-rollout passes 11 of 11.
  • Earlier gates on this Mac: test:unit:main 7707 passing; its 3 failures pass alone or fail on base too. integrationTests/deploy, components and upgrade ran in batches: only shutdown-drain-e2e failed, as on base. Type check and lint:required pass.
Unit tests by issue

Fixes #3062
Fixes #2659
Fixes #564
Fixes #3060
Fixes #3058
Fixes #3059
Fixes #3061
Fixes #2681
Fixes #3096
Refs #1719

🤖 Generated by Claude Code (Claude Opus 5.5); posted via @dawsontoth.

Release target: main — new operations, fields and CLI behavior for v5.4
Related PRs: #372 overlaps (merged into this branch; its status-registry rework now carries loadedAt across a level change), #3360 overlaps (both add a 5.4.0 directive and edit operation_authorization.ts; whichever lands second merges the directives), #3498 overlaps (merged into this branch; it restarts each peer in turn for an uncertified rollout, this activates each peer for the certified one), #3339 overlaps (merged into this branch; both edit deployComponent and bin/restart.ts), #3131 overlaps (both add tests to unitTests/components/deploymentRecorder.test.js; whichever lands second merges them), #3523 overlaps (merged into this branch; both edit the root DESIGN.md index), 21 others independent
Complexity: complicated

Deferred findings

Review-Coverage: authored=claude; ran=gemini,codex,cursor-composer; adjudicated=domain; blocked=cursor-grok(no-receipt),cursor-muse(no-receipt); declined=cursor-kimi,muse; rounds=10; full=4 @ 11f0a2b

Review-Attention: deep ~60m (critical: operationDescriptions.ts, operations.ts +7; sensitive: tokenExchange.ts, credentialProvenance.ts +2; decisions: rollout-row-lock-fallback, final-write-fails-job, rollout-wait-default-on, partial-status, cleanup-owner-identity, restarted-at-naming, do-less-alternative; raised: degraded review) @ 11f0a2b

dawsontoth and others added 22 commits October 23, 2026 11:59
…in-band sentinel event

DeploymentRecorder.finish() emitted _recorder_finished on the deploy's progress emitter before
unsubscribing, so the sentinel landed in event_log, a streamed deploy_component and the
get_deployment tail. The registry now carries a promise finish() settles after the terminal write;
the tail awaits it (or its client's disconnect) and re-reads the final row.

Fixes #3062

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pgraded installs

An upgraded install keeps the catalog each system table was created with, so attributes added to
systemSchema.json later (activated_from, install_fingerprint on hdb_deployment) never reached it.
The 5.4.0 directive runs registerSystemSchemaAttributes, which adds whatever the schema declares
and the catalog lacks, indexed as a fresh install indexes it; each later release's directive can
list it again. mount_hdb and the directives now hand table() a copy of each schema attribute list,
since table() kept the bundled schema's array as the live attribute list.

Fixes #2659

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every get_components entry now carries deployment_id (the id the live tree's provenance marker
names), deployed_at (completed_at of the hdb_deployment record that made that release live) and
restarted_at (the latest time a thread on this node finished loading the application, kept on
ComponentStatus and aggregated across threads). hdb_deployment stays the only record of deploys.

Fixes #564

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oyment record

A certified rolling deploy's record was finished as success before its activation job ran, so
get_deployment and list_deployments reported success for a release a peer then rejected. The
origin now finishes the record as rolling_out with the job's id in restart_job_id; the job adds
each peer's activation (activated, certification, message, error) to its peer_results entry and
ends the record success, or partial naming the nodes that did not take the release. The job waits
for the origin's rolling_out write before its first, and carries the last peer's outcome in its
final write so the two never land as a tight burst. get_components treats rolling_out and partial
records as released.

Fixes #3060

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e release

When a rolling deploy answers rolling_out, the CLI follows its deployment record with
get_deployment on the same connection and credential, printing each node's outcome as it lands,
and exits non-zero on partial, failed or a timeout (ten minutes per node by default,
wait_timeout= to override), naming the nodes that did not take it. wait=false keeps the old exit.
The record replicates, so the wait works behind a load balancer where a node-local get_job poll
does not; the GitHub Actions CI role now grants get_deployment.

Fixes #3058

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
list_releases { project?, replicated? } reports, per component on this node, the deployment_id
the live tree's provenance marker names and every dormant build deploy_component deployment_id
can activate, each with whether it was staged or kept (replaced here), when, and its size on disk.
With replicated: true it returns every node's answer. An activation that keeps the release it
displaced now writes 'kept' into that record's .complete marker, which nothing that settles or
retains a build reads.

Fixes #3059

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… policy

The OIDC exchange now signs the policy id and the run's verified claims (repository, sha, ref,
workflow_ref, run_id, run_attempt, actor, environment) into the operation token it mints, and
deploy_component records them on the deployment row as source: { via: 'oidc', ... }. A deploy
that does not come through OIDC records what the CLI declares (the GitHub Actions run, and for a
directory deploy the checkout's commit and a dirty flag) as via: 'declared'; an attested source
always wins. get_deployment and list_deployments return source, and list_deployments filters by
sha.

Fixes #3061

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before the swap, deploy_component checks the built tree for every jsResource and graphqlSchema
files pattern the component's own config file declares, and fails in the prepare phase naming each
one that matched nothing, so a truncated or partial payload no longer reports success while the
release it replaced keeps serving. The deploy_component unit tests for #3061 now read the per-run
components root deploy_component actually uses.

Refs #1719

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A failed deploy keeps its payload tarball for retry and debugging, so a retried rollout used to
accumulate one copy per attempt forever. sweepFailedPayloads now drops, per project, every failed
payload beyond the newest deployment_payloadRetention_failedMaxCount (default 2) and every one
older than deployment_payloadRetention_failedMaxAge (default 7d), never the newest; 0 turns a
bound off. It writes the row with payload_blob null, as delete_deployment_payload does, so peers
drop their copies, and only for rows this node wrote. It runs after a deploy failure is recorded
and once per process at boot.

Fixes #2681

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n after the response

A part piped after the response that loses its destination, to a destination error or unpipe(),
is left paused but not destroyed, so nothing re-entered the discard and a client that kept sending
stalled until the request timeout. While a handed-out part is held after the response, the parser
now re-checks it whenever it loses a data or readable listener, which Node's pipe cleanup removes
in both cases.

Fixes #3096

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t CLI test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A config that names a file its release no longer ships is a legal, resource-less component, and
redeploying to delete an application's only resource file is a tested workflow
(redeploy-restart-flag-deletion.test.ts), which the check failed. It now names each missing
jsResource/graphqlSchema pattern in a warning event and in the response message, and the deploy
proceeds.

Refs #1719

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lank settings

- get_components selects only the four fields it reads from hdb_deployment rather than copying
  every released row's event_log.
- The activation job keeps at least a second between its writes to the row, counting the origin's
  own rolling_out write, so a peer that fails at once cannot produce the burst #1170 describes.
- A recorder's tail that arrives during the terminal write waits for it, and forwards every event
  after its replay.
- A blank or null failedMaxCount/failedMaxAge reads as unset, not as 0.
- A negated entry-point pattern is read as an exclusion.
- harper deploy validates wait_timeout before sending, and the rollout bound grows once a read
  says how many nodes there are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…releases replication

- harper deploy keeps waiting until the record ends; a node whose copy of the row lags shows the
  origin's earlier status, which is no ending.
- list_releases settles the replication promise at once, so a rejection during the local read is
  never unhandled, and raises it after.
- deploymentSource keeps its internal helpers unexported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…load bound

- get_components finds the record of each live release by key and by the activated_from index
  instead of scanning every released row on every poll.
- Failed payloads are ranked across every node's rows, so the bound is per project, not per
  origin; each node still drops only the rows it wrote.
- The activation job waits up to ten minutes for the origin's rolling_out write, since an origin
  write landing after the job's would erase the job's outcomes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… comment history

- A component without a provenance marker finds its newest released record by walking its
  project's rows newest first and stopping at the first, instead of reading its whole history.
  Checked against the real deployment table on both engines.
- The entry-point check skips a non-string pattern rather than throwing, and reads a leading /
  on a negation as the component root, as on a positive pattern.
- New comments drop issue-number provenance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… each rollout poll

- When the origin writes a rolling deploy's rolling_out row, it keeps the peer activations and
  final status the activation job already stored, so the hand-off no longer depends on the job's
  wait outlasting the origin.
- Each get_deployment poll is bounded by what is left of the CLI's wait.
- get_components falls back to a failed record that names the live release, for a deploy that
  swapped here and then failed on a peer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…loyment record notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ge units

- At boot, after interrupted jobs are settled, each rolling_out record this node wrote whose
  activation job is no longer running is ended: success only when the job completed and every peer
  took the release, otherwise partial naming the unconfirmed peers.
- The activation job waits for the origin's write once per row, so a write that never landed
  delays only the first activation.
- failedMaxAge accepts only convertToMS's units; another suffix used to read as seconds.
- A refused rollout poll names both remedies, since an expired token also answers 403.
- The multipart watch reacts only to a lost data listener; a reader looping on
  once('readable') holds none between reads.
- A declared source's dirty flag counts only changes inside the deployed directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd the markerless walk

- The activation job's final write ends a row still short of the origin's own write, which the
  origin's late write then keeps; only a row the origin ended some other way keeps its status.
- A component without a provenance marker reads at most 100 of its project's rows, newest first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…off across a failed write

- failedMaxAge is trimmed before both its check and its conversion; ' 7d ' read as seven
  seconds, since convertToMS takes the last character as the unit. Y is accepted too.
- The activation job's wait for the origin's write is over once it has run, whether the job's
  next write landed or not.
- An incomplete boot pass of the rollout settlement or the failed-payload sweep is not
  remembered, so the next root reload tries again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a rollout tracking mechanism for rolling deployments, introduces the list_releases operation to report live, staged, and kept releases on disk, and enhances get_components with deployment and worker load times. It also adds failed payload retention to expire old failed deployment tarballs and records deployment sources (attested via OIDC or declared by the client). The review feedback suggests parallelizing independent I/O operations in keptReleasesOf and releasesOnThisNode using Promise.all to improve performance. Additionally, it recommends adding defensive nullish checks and optional chaining to prevent potential TypeError crashes, and resolving TypeScript compilation errors related to handling caught errors of unknown type in deployRollout.ts.

Comment thread components/Application.ts Resolved
Comment thread components/releaseOperations.ts Resolved
Comment thread components/deploymentRecorder.ts Resolved
Comment thread components/operations.ts Resolved
Comment thread components/deploymentOperations.ts Resolved
Comment thread bin/deployRollout.ts Resolved
Comment thread bin/deployRollout.ts Resolved
…o reads

Tighten this branch's components/DESIGN.md notes so the file is back
under its 1,000-line budget (check:design-docs). keepRolloutProgress
now skips a stored peer entry with no node, and get_components'
debug log tolerates a non-Error rejection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dawsontoth and others added 2 commits October 23, 2026 15:19
…d drop a deadline-cut read from the timeout message

A certified restart=rolling deploy activated on no peer on a real
cluster: its restart_service job runs in a job worker thread, whose
server.nodes is empty until something subscribes to node updates, and
monitorNodeCAs (which does) ran only after the peers had been chosen
from that empty list. Every peer was reported as no longer in the
cluster. The job now subscribes before reading the list; on Harper core
alone, whose stub throws, there are no peers. A 3-node harper-pro
cluster run of the simulation found it.

The CLI's rollout wait also stopped reporting "last read failed" for
the final read that the end of the wait itself cut short.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ded, not only core's stub

The catch around monitorNodeCAs swallowed every failure, so a harper-pro
scan of hdb_nodes that threw would leave server.nodes empty and an
activation with no node list would record success having visited no
peer. Core's replication stand-ins now throw with code
REPLICATION_NOT_IMPLEMENTED, and only that means "no peers".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…step fits its time limit

The Node 22 "Unit tests: main" step ran past its 8-minute limit
(8:12; main's own runs take 7:20 to 7:48). Each test of the
rollout-record suite waited out the job's one-second spacing between
row writes, 13 seconds in all. The spacing is now rolloutTiming, which
that suite shortens to 50 ms; the spacing test itself still waits 400 ms
and fails without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #3339 (start and stop one isolated application), which this
branch's PR lists as overlapping. Its two new DESIGN.md index entries
left the root index at 251 lines, over its 250 budget (main's own lint
fails on it), and this branch's notes on top took components/DESIGN.md
to 1035 of 1000. Related index entries now share a bullet, this branch's
deployment-record notes are tighter, and the dangling-symlink note drops
its history, so both budgets hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Takes #3523's DESIGN.md index, which shares closely related entries on
one line with a `·` separator, and adds this branch's two index changes
in that form: the deployment-record entry beside the install-fingerprint
one, and the entry-point warning in the dangling-symlink description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dawsontoth and others added 4 commits October 24, 2026 20:43
Brings in #562's type-strip mode, which turned components/operations.js
and server/loadRootComponents.js into .ts with static imports. This
branch's changes to them now use static imports too (deploymentSource,
declaredEntryPoints, payloadRetention, deploymentOfLiveRelease,
liveDeploymentIdOf, and the two boot sweeps), and get_components'
record lookup returns undefined from its catch so it typechecks.
restart.ts keeps this branch's node-list load with main's .ts import.
components/DESIGN.md is trimmed back under its line budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…runs can load it

upgrade/systemSchemaAttributes.ts imported json/systemSchema.json
directly. Since #562, Harper and its native unit runs load .ts sources
as ESM, where a JSON import needs `with { type: 'json' }`, so every
type-strip boot and native unit run failed with
ERR_IMPORT_ATTRIBUTE_MISSING. It now takes the schema from the CommonJS
wrapper main's directives use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…de reports no other way

Native Unit Test on Node 22.18.0 and 24.0.0 failed the harper#3096
cases where a part piped after the response loses its destination. On
those versions a pipe's cleanup removes the part's `data` listener
without emitting `removeListener`, and a part already paused by
backpressure emits no `pause`, so nothing prompted the abandonment
check. Every lost pipe ends in the part's own unpipe() (the route's, or
the cleanup's after its destination errors or closes), so the watch now
also looks after that call, and restores the method once the part ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kriszyp kriszyp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Reviewed with Codex

Comment thread components/deploymentRecorder.ts Resolved
Comment thread components/deploymentRecorder.ts Resolved
Comment thread components/deploymentOperations.ts Resolved
Comment thread components/deploymentOperations.ts Resolved
Comment thread components/declaredEntryPoints.ts Resolved
Comment thread bin/cliOperations.ts Resolved
dawsontoth and others added 5 commits October 24, 2026 23:34
…tcomes a lost write drops

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and find a markerless one past unreleased deploys

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t idles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dawsontoth and others added 2 commits October 26, 2026 08:55
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kriszyp
kriszyp merged commit abf67cd into main Oct 26, 2026
55 of 60 checks passed
@kriszyp
kriszyp deleted the claude/deploy-641-followups-1d6af4 branch October 26, 2026 15:40
kriszyp pushed a commit to HarperFast/documentation that referenced this pull request Oct 28, 2026
…_releases

Documents HarperFast/harper#3502: a certified rolling deploy's record
follows its job (rolling_out, then success or partial, with each node's
activation), harper deploy restart=rolling waits on it (wait=false,
wait_timeout=), the CI role and guide gain get_deployment, list_releases,
get_components' deployment_id/deployed_at/restarted_at, the record's
source and list_deployments sha, failed-payload retention, and the
warning for a declared entry point that matched nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dawsontoth added a commit to HarperFast/documentation that referenced this pull request Oct 28, 2026
…_releases

Documents HarperFast/harper#3502: a certified rolling deploy's record
follows its job (rolling_out, then success or partial, with each node's
activation), harper deploy restart=rolling waits on it (wait=false,
wait_timeout=), the CI role and guide gain get_deployment, list_releases,
get_components' deployment_id/deployed_at/restarted_at, the record's
source and list_deployments sha, failed-payload retention, and the
warning for a declared entry point that matched nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment