Skip to content

feat(deploy): Slice A — hdb_deployment system table + audit record per deploy#655

Open
kriszyp wants to merge 3 commits into
mainfrom
feat/deployment-tracking-slice-a
Open

feat(deploy): Slice A — hdb_deployment system table + audit record per deploy#655
kriszyp wants to merge 3 commits into
mainfrom
feat/deployment-tracking-slice-a

Conversation

@kriszyp
Copy link
Copy Markdown
Member

@kriszyp kriszyp commented May 21, 2026

Summary

Slice A of #641. Every deploy_component call now writes a row to a new system.hdb_deployment table capturing what was deployed, when, by whom, and whether it succeeded — plus the payload tarball itself as a Blob attribute. The deployment_id is returned in the deploy response and is the join key Studio/CLI will use to subscribe to live progress in Slice B.

This is the foundation layer for the broader deployment tracking, replicated payload delivery, and rollback design. Slices B and C will extend the same table with live ProgressEmitter integration (replacing #531), peer-side reads from the replicated blob (replacing #536/harper-pro#146), and rollback semantics.

Changes

  • json/systemSchema.json: hdb_deployment table with deployment_id (UUID hash) plus the full lifecycle attributes
  • utility/hdbTerms.ts: table-name constant and four new operation enums
  • upgrade/directives/5-2-0.ts: provisions the table on existing installs (fresh installs get it via mount_hdb's systemSchema iteration)
  • components/deploymentRecorder.ts: lifecycle wrapper used by deployComponent — creates the pending row, ingests payload into the Blob attribute with sha256 + size, commits the terminal status
  • components/deploymentOperations.ts: list_deployments (filters: project, status, since, until, limit, offset) and get_deployment. Payload bytes are stripped from both — only payload_blob_present: boolean is exposed
  • components/operations.js: deployComponent wraps prepareApplication in a try/catch driven by the recorder; payload is re-sourced from the persisted blob so extraction reads exactly what was recorded
  • server/serverHelpers/serverUtilities.ts: registers the two new ops
  • Brittle deepStrictEqual deploy-response assertions in 4 existing tests updated to allow the new deployment_id field

Scope deliberately deferred

  • Streaming ingest: ingestPayload currently buffers the upload before persisting (race-free; multi-GB optimization comes in Slice B alongside the ProgressEmitter subscriber that also benefits from chunk-level progress events)
  • Peer-side reads: Slice B reshapes peer execution to read the payload from the replicated hdb_deployment row's blob, replacing the staging temp file (feat(deploy): stage streamed payloads to a temp file for replication #536) and the direct-HTTPS relay (harper-pro#146)
  • Rollback / reclamation: Slice C — deploy_component {rollback_from} arg + onStorageReclamation hook for per-node blob pruning

Test plan

Refs #641

🤖 Generated with Claude Code

Slice A of #641. Every deploy_component call now writes a row to a new
system.hdb_deployment table capturing the project, package identifier,
sha256 of the payload tarball, payload size, status (pending → success
or failed), error info, and the upload payload itself as a Blob
attribute. The deployment_id is returned in the deploy response and is
the join key Studio/CLI will use to subscribe to live progress in
Slice B.

Includes:
- json/systemSchema.json: hdb_deployment table definition (deployment_id
  hash, with attributes mirroring the lifecycle)
- utility/hdbTerms.ts: SYSTEM_TABLE_NAMES.DEPLOYMENT_TABLE_NAME +
  LIST_DEPLOYMENTS / GET_DEPLOYMENT / GET_DEPLOYMENT_PAYLOAD /
  DELETE_DEPLOYMENT_PAYLOAD operation enums
- upgrade/directives/5-2-0.ts: provisions the table on existing installs
  (fresh installs get it via mount_hdb's systemSchema iteration)
- components/deploymentRecorder.ts: lifecycle wrapper used by
  deployComponent — creates the row up front, ingests the payload into
  a Blob attribute with sha256 + size, then commits success or failure
- components/deploymentOperations.ts: handlers for list_deployments
  (with project/status/since/until/limit/offset filters) and
  get_deployment; payload bytes are stripped from these responses
- components/operations.js: deployComponent now wraps prepareApplication
  in a try/catch driven by the recorder; payload is re-sourced from
  the persisted blob so extraction reads exactly what was recorded
- server/serverHelpers/serverUtilities.ts: registers the two new ops
- integrationTests/deploy/deploy-tracking.test.ts: end-to-end coverage
  for the happy path, list filtering, and failure recording

Updates the brittle deepStrictEqual deploy-response assertions in 4
existing tests to allow the new deployment_id field. Slice A scope is
deliberately single-node; Slice B will replace the in-memory buffer in
ingestPayload with a streaming variant and add peer-side reads from
the replicated blob.

Refs #641

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kriszyp kriszyp requested review from a team as code owners May 21, 2026 00:37
Comment thread server/serverHelpers/serverUtilities.ts
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 21, 2026

Reviewed; no blockers found. The prior finding (missing requiredPermissions entries for handleListDeployments / handleGetDeployment) is resolved in commit bacdd541f.

@kriszyp kriszyp marked this pull request as draft May 21, 2026 00:45
- Skip recording on replicated executions: peer nodes receiving deploy_component via
  replicateOperation already have req._deploymentId set by origin, so they no longer
  spin up a fresh recorder + UUID + row. Prevents N duplicate rows per N-node cluster.
- Cap payload at 200 MiB while Slice A buffers in memory. Throws a clear ClientError
  pointing users at the package-identifier path or Slice B's streaming variant.
- Register list_deployments and get_deployment in utility/operation_authorization.ts.
  Pattern matches get_components: requires_su=true with the operation enum as the
  named exception so a role can be granted it without SU rights (per the design's
  permission model).
- Add "audit": true to hdb_deployment in systemSchema.json so fresh installs match
  the audit setting the 5-2-0 upgrade directive applies.
- Drop two now-unused imports (Transform from recorder, existsSync from test).
- Auto-format pass via npm run format:write.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kriszyp kriszyp requested review from Ethan-Arrowood, cb1kenobi, dawsontoth and heskew and removed request for a team and cb1kenobi May 21, 2026 00:52
…ap pattern

Two non-obvious findings from #641 Slice A that future agents should know:

1. createBlob(readable) + table.put() doesn't synchronously drain the source.
   The blob's saveBlob runs concurrently; calling hash.digest() after the put
   resolves can race a still-flushing Transform and throw ERR_CRYPTO_HASH_FINALIZED.
2. Adding a new system table requires three changes: systemSchema.json (fresh
   installs), SYSTEM_TABLE_NAMES (constant), and an upgrade directive registered
   in directivesController.ts. The directive shape was undocumented after the
   .ts refactor cleared old directives.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kriszyp kriszyp marked this pull request as ready for review May 21, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant