Skip to content

feat(cli): unstable [build] manifest — per-canister build record with fresh-install compatibility - #799

Open
Kamirus wants to merge 2 commits into
mainfrom
feat/unstable-build-manifest
Open

feat(cli): unstable [build] manifest — per-canister build record with fresh-install compatibility#799
Kamirus wants to merge 2 commits into
mainfrom
feat/unstable-build-manifest

Conversation

@Kamirus

@Kamirus Kamirus commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Deployment platforms and CI pipelines that drive mops build need to know, per artifact, whether the wasm can land on a fresh canister or only works as an upgrade over pre-existing state — and today they have to reconstruct that themselves from the .most. With the unstable [build] manifest = true setting, mops build writes a machine-readable record per canister, next to the artifacts it describes.

Before: mops build emits <canister>.wasm / .did / .most only.

After (with [build] manifest = true in mops.toml), additionally <outputDir>/<canister>.build.json:

{
  "version": 1,
  "canister": "fresh",
  "moc": "1.5.0",
  "outputs": {
    "wasm": { "path": "fresh.wasm", "sha256": "2b6e0f37…" },
    "did": { "path": "fresh.did", "sha256": "6458b9c3…" },
    "most": { "path": "fresh.most", "sha256": "5354d987…" }
  },
  "checks": [
    { "name": "stable-compatibility", "baseline": "empty", "passed": true }
  ]
}

Design follows the "record facts, let the consumer derive policy" convention of cargo's SBOM precursor files and Vite's build.manifest:

  • checks records the outcome of moc --stable-compatible from an empty-actor baseline to the built .most. passed: false (a migration requires pre-existing state → upgrade-only wasm) is recorded, never fails the build — unlike the gating build checks, this one is observational. A check that fails to run (moc unspawnable) errors instead of masquerading as passed: false.
  • outputs hashes the artifacts as finally written, after candid metadata embedding and the [optimize] pass.
  • A stale record can't mislead: mops build deletes any existing <canister>.build.json at the start of each canister build — even with the feature off — and writes the new one atomically (temp + rename) as the final step, so a watcher never observes a partial or outdated record.

Unstable

Enablement is config-only ([build] manifest = true): no CLI flag, no --help change. Documented solely on a new Unstable features docs page (/unstable), which states the schema and behavior may change in any release; the stable reference pages (mops.toml, mops build) deliberately don't mention it until stabilization.

What is unchanged

The empty-baseline check is not new logic: EMPTY_ACTOR_MOST and the --stable-compatible invocation existed in duplicate in check-deploy.ts and deployed.ts, and are now extracted into a shared helpers/empty-baseline.ts that all three consumers use. check-deploy behavior is identical (its suite passes unchanged). When both check-deploy and manifest are enabled, the empty-baseline check runs twice per canister — a single cheap moc invocation, deliberately left unshared.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Cursor AI review

👍 APPROVE — looks safe to merge

Category Assessment Details
Summary Adds opt-in unstable [build] manifest = true so mops build writes <canister>.build.json (hashes, moc pin, empty-baseline moc --stable-compatible outcome) and extracts the duplicated empty-actor check from check-deploy.ts / deployed.ts into empty-baseline.ts.
Code Quality Shared checkEmptyBaselineCompatibility / EMPTY_ACTOR_MOST replace the inlined copies in filterFreshDeployableArtifacts and deployedInit; writeBuildManifest is a single write path (hash → JSON → rename).
Consistency Follows existing [build] booleans (check-wasm / check-deploy) via config.build?.manifest ?? false in build() and Config.build in types.ts; ## Next changelog and /unstable docs are present; no CLI flag / stable 09-mops.toml.md mention matches the stated unstable boundary.
Security Traced writeBuildManifest SHA-256 of final artifacts, build() per-canister lock + unconditional rm of <outputDir>/<canister>.build.json, and checkEmptyBaselineCompatibility (execa + mkdtemp under .mops/). No auth, publish, or package-integrity surface.
Tests build-manifest.test.ts + snapshot cover fresh passed: true, upgrade-only passed: false (build still exits 0), and stale-record deletion when the key is removed. Extracted skip path still covered by build-check-deploy.test.ts (MOPS-CHECK-DEPLOY-SKIPPED / sibling install). Snapshot moc args match empty-baseline.ts after normalizePaths redacts the mkdtemp suffix.
Maintainability Unstable schema isolated to 11-unstable.md / MANIFEST_VERSION; atomic write+rename; helper is the single empty-baseline implementation.

Verdict

Decision: APPROVE
Risk: Low
Reason: Default-off opt-in plus a verified-equivalent extract of the existing empty-baseline moc --stable-compatible check; no registry, install/resolution, identity, or release-pipeline changes.


Generated for commit d57adf2

@automation-sa-sre automation-sa-sre left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated approval: the AI review verdict for 3c7b72a is APPROVE. See the "Cursor AI review" comment for details.

… write, spawn-failure error

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@automation-sa-sre automation-sa-sre left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated approval: the AI review verdict for d57adf2 is APPROVE. See the "Cursor AI review" comment for details.

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.

2 participants