mix ptc.materialize (PtcRunner v0.14.0)

Copy Markdown View Source

Turns model-authored source into {candidate.clj, descriptor.json} and reports whether it is fit to promote.

mix ptc.materialize MANIFEST --workflow --component ID --out DIR --source authored.clj
mix ptc.materialize MANIFEST --target-mission NAME --component ID --out DIR \
  --from-result results/run.json --result-pointer /value/source
mix ptc.materialize MANIFEST --workflow --component ID --out DIR --source authored.clj \
  --origin-run-id run-2026-08-03-0001 --accept-widened-effect

A model can author a working library inside a run, but a runtime defn dies at end of run: it is not in the frozen bundle, not covered by a component source hash, and absent from the mission inventory. This task closes that loop by making the authored bytes a real component candidate, which a later run can evaluate through mix ptc run --component-override-descriptor.

Promotion stays an explicit human decision. This task does not promote anything; it makes the decision cheap to reach and well evidenced.

Source acquisition

--source reads raw candidate bytes. --from-result reads a result artifact written by mix ptc run --output/--private-output and resolves one RFC 6901 JSON pointer to one string, because a result artifact is JSON, not raw Lisp. A non-string or absent target is refused rather than coerced.

--workflow targets the selected workflow occurrence. --target-mission NAME targets exactly that declared mission; supplying neither or both is invalid. The target is written into the closed override descriptor.

Publication

--out must not exist. It is created exclusively at mode 0700 with both files restricted to 0600 before content is written, because a candidate extracted from a private artifact must not be declassified by publishing it. A refused candidate leaves nothing behind.

The gate

The candidate is re-acquired through the descriptor just written — the exact path a run takes — and judged on whether it compiles, whether its prompt-visible exports declare a signature and docstring, and whether any export reaches further than the base it replaces.

Every criterion is intrinsic to the candidate or relative to its base. The gate does not check capability grants: real capability names exist only after provider acquisition, so a candidate naming a capability no provider grants passes here and fails at run-time assembly. The gate narrows the distance to that failure; it does not remove it.

A widening is refused unless --accept-widened-effect is given, which is recorded in the report and in the descriptor's provenance. An override changes which source compiles, never what compilation permits, so a widening is not a security hole — but it is a different risk profile and must not pass silently.

Provenance

--origin-run-id, --origin-prompt-hash, and --origin-authored-at record who authored the candidate. These are operator-asserted and verified by nothing: a run id is a claim about origin, not evidence of it. There is no model-id option, because a descriptor is a published artifact and the raw model selector must not be published; the authoring model is reachable through the run id.