Project Candidate Managed Publication v1

September 10, 2026 · View on GitHub

Status: implemented bounded profile; HOSTED GREEN under the v0.4.0 release baseline. Historical local, authoring-time, ignored, device/simulator, or separately provisioned evidence below retains its narrower scope; public promotion, registry publication and broader product completion remain separately gated.

Audience: compiler maintainers and hosts explicitly publishing approved Project candidates.

This bridge publishes an independently replayed candidate through the existing managed Workspace ACTIVE authority. It does not commit canonical Git sources, rewrite original .spx files, publish a Project manifest, or make raw path readers observe a transaction. The broader canonical-source commit goal remains open.

Separate host API

The functions are exported from semaprax::project:

pub fn prepare_candidate_publication(
    candidate: &ProjectCandidate,
    approved_candidate_digest: &str,
    workspace_root: &Path,
    project_manifest: &Path,
    expected_workspace_revision: &str,
) -> Result<ProjectCandidatePublication, Vec<Diagnostic>>;

pub fn apply_candidate_publication(
    candidate: &ProjectCandidate,
    approved_candidate_digest: &str,
    workspace_root: &Path,
    project_manifest: &Path,
    expected_workspace_revision: &str,
    submitted_publication: &[u8],
) -> Result<String, Vec<Diagnostic>>;

The opaque preparation exposes to_json(), publication_digest(), proposal(), workspace_change_evidence(), and candidate_workspace_revision(). Preparation is read-only and creates no proposal file, cache, staging object, or generation. It does not initialize a workspace. An existing independently initialized Semantic Workspace is required. Publication is a separate explicit host call; there is no transport, CLI, automatic capsule import, or candidate method that grants filesystem authority.

The root must be the exact absolute UTF-8 path of the independently authenticated Project root, and the manifest argument must name its semaprax.toml. Existing Project and Workspace path/handle authentication rejects aliases and drift. The caller supplies both expected digests independently of the proof. Calling an argument “approved” is a host contract, not a signature, consent detector, or authorization service.

Binding and replay

The host opens and verifies the entire raw Project manifest and source set. Its Project revision and canonical manifest must equal the candidate's original base, and the candidate may not change that manifest. The bridge then acquires the existing permanent shared lock for preparation or exclusive lock for apply. Both modes authenticate ACTIVE before candidate-history replay.

While that lock remains held, the bridge compares the whole managed source inventory with the Project base: ordered paths, exact bytes, graph schemas, source revisions, and source digests. The supplied Workspace revision must match both ACTIVE and the candidate's original base Workspace revision. Candidate history is independently replayed from the freshly authenticated Project snapshot, including the ordinary Project, ownership, cleanup, and admitted target checks. Its full canonical evidence and final digest must agree with the candidate and the caller's expected approval digest.

The compiler derives the ordinary replacements-only Change-v1 proposal. The existing Change builder reconstructs its complete base/candidate workspace analysis and evidence under the same lock. The candidate workspace revision must exactly match the freshly replayed Project result. Apply compares the submitted outer proof with the complete freshly reconstructed canonical bytes, then invokes the ordinary Change-v1 typed and exact-byte evidence replay before any candidate generation or staging object is created.

There are no replacement sources or HIR loaded from an untrusted capsule as compiler state. Submitted proof bytes are bounded and compared directly with the independently rendered result. Changed sources are compiler output from candidate replay, not host-supplied publication edits.

Proof and receipts

The proof schema is semaprax.project-candidate-publication.v1. It contains:

  • compiler package and version;
  • exact absolute Workspace root and Project manifest path;
  • original/final Project and Workspace revisions;
  • exact canonical Project manifest;
  • approved candidate digest and complete canonical candidate evidence, including its full ordered change history;
  • exact canonical Change-v1 proposal and evidence;
  • limits and explicit nonclaims.

The proof is deliberately host-specific. Moving it to another otherwise identical root requires new preparation. Strings embedding canonical child artifacts preserve all their original bytes. Object keys are recursively sorted lexically, arrays retain their defined order, and output is compact UTF-8 JSON with exactly one terminal LF. The proof digest is:

sha256("semaprax.project-candidate-publication.artifact.v1\0" ||
       little_endian_u64(proof_byte_length) || exact_proof_bytes_including_LF)

That digest is exposed by a getter and receipt, not embedded circularly in the proof. The submitted proof is neither an approval token nor a signature.

Successful application returns semaprax.project-candidate-publication-application.v1, identifying managed_generation_published, both Workspace revisions, the candidate/proof digests, and the existing ACTIVE pivot. It explicitly reports that original source files are unchanged and no Git commit occurred. A receipt is descriptive output, not reusable publication authority.

Publication and failure boundaries

Only workspace::commit_semantic_change_authority_with_hook owns generation creation and the sole ACTIVE replacement. The bridge rechecks held Project inputs after replay, before candidate publication, at the existing final check boundaries, immediately before ACTIVE replacement, and after replacement. Workspace authentication, permanent locking, generation bounds, final checks, and post-pivot validation remain unchanged.

Invalid approval, stale base, proof tampering, or replay failure reject before candidate generation/staging. Original source files remain unchanged. A failure later in the existing staging route can leave its bounded unpublished artifacts; this bridge adds no rollback or cleanup authority. SPX-G248 explicitly means ACTIVE publication occurred before a later failure: do not assume unchanged managed state or retry blindly. The host must inspect the managed snapshot. Checks cannot make independently edited raw files and ACTIVE one atomic transaction.

Because original raw sources remain unchanged, a second publication based on that old raw Project is stale after the first pivot. Reusing the new managed state as a new host Project base requires a separate explicit workflow; the bridge does not silently synchronize Git files or rebind a retained candidate.

Bounds and evidence

The unchanged Change-v1 domain permits 2–16 genuinely changed files, each at most 1 MiB, at most 4 MiB total replacement source, the same managed path set, and supported base source Graph v10–v14 schemas. One-file changes and no-ops reject; unchanged files are never inserted as padding. Unsupported newer graph schemas reject through existing Change/Workspace diagnostics. This limitation is not hidden by the broader candidate API.

The outer proof output/submission limit is 128 MiB, including its terminal LF; this is an output bound, not a total heap bound. Existing candidate, Project, Change-v1 analysis, evidence, and managed-storage bounds still apply. New bridge diagnostics are SPX-G245 for grammar/domain restrictions, SPX-G246 for proof output capacity, SPX-G247 for stale/replay mismatch, and SPX-G248 for observed postpublication uncertainty. Existing diagnostics retain their meaning and may propagate.

Release-tested tests in tests/project_candidate_publication_v1.rs cover read-only preparation, deterministic proof, real managed publication with raw source preservation, stale repeat apply, proof/approval/root substitution, exclusive-lock ordering, raw Project drift, and single-file rejection. A unit regression injects raw-source drift after the pivot and requires the new managed revision together with SPX-G248. No local tests, compiler checks, long gates, or publication calls were executed during implementation, as requested.

Canonical Git publication, arbitrary path-set changes, one-file adaptation, newer graph-schema admission, target execution, signatures, general approval policy, and full source-commit completion remain outside this version.