Repository navigation
v0.5.0
Metaproc v0.5.0
A minor release, because Metaproc now requires Python 3.14.
The floor moved so that Zstandard support could come from the standard library’s
compression.zstd, and most of what else changed is about a run accounting for itself.
A run can now cap what it spends and stop a mapped step whose items keep failing.
Every run writes an operations summary of where its time went.
A resume records the launch-config changes it makes instead of refusing them, re-runs a
consumer whose collected inputs changed, keeps its fingerprints when the same revision
is planned from another checkout, and can re-enter one nested step or mapped item
through its parent. Every executable leaf of a local run, scalar or mapped, agent or
code, now draws on one adaptive controller, which backs off when a provider refuses on a
per-minute quota. Gemini steps run in a private Gemini home per launch, and failures of
every kind keep a bounded, credential-redacted cause.
No CLI command or flag was removed, no adapter default or accepted model identifier
changed, and no plugin entry point moved.
Some Python APIs were renamed, and three changes can stop something that worked on
v0.4.1: the 3.14 floor, the GCP image interpreter, and mapped agent steps on profiles
with different pool resources.
Read Compatibility before upgrading.
What’s Changed
Features
-
A run can cap its own spend.
run-process --max-spend-usd Xsets one cap on the
run’s measured list cost, shared by every scope it enters and kept for the run’s whole
life: it is recorded asmax_spend_usdinrun-config.yaml, a resume without the
flag keeps it, and.state/spend-ledger.yaml(metaproc:SpendLedger/0.1) carries the
measured spend, so a resume on a host without the earlier logs still counts them.
Three mechanisms read the cap:- A mapped step may declare
spend: {per_item_usd, source, max_attempts}. After reuse
has chosen the actionable items and before any starts, the step refuses when the
spend already measured plus items × attempts × price exceeds the cap, and names the
cap that would admit it.
Only actionable items count, so a resume is priced for the work it will run.
max_attemptsdefaults to one plus the largest resolvedretry.max_retriesamong
the agent steps an item runs. - Mapped composite and code steps stop starting items once measured spend reaches the
cap. Running items finish and the rest stay pending for a resume. for_each.breaker: {max_failure_fraction, min_finished}stops a mapped composite or
code step’s dispatch once more than that fraction of the items it finished in this
invocation failed.
A stopped step keeps
state: failed, which every reader already treats as resumable,
and gains astoppedrecord inprocess-status.yamlwith the reason, phase, and
numbers.metaproc statuslabels the runBUDGET-STOPPEDorBREAKER-STOPPEDwith a
Stopped:line per step.
The cap is cumulative, so a budget-stopped run resumed with the same cap refuses
again; a higher one continues it.
A process may declarespend_cap: {required: true}to refuse an uncapped launch, or
spend_cap: {default_usd: X}. Neither a price nor a breaker enters a step
fingerprint. Measured spend is list cost and a lower bound where a model is unpriced,
not provider billing. - A mapped step may declare
-
Every run writes an operations summary. Run finalization writes
operations-summary.md(metaproc.operations:AgentOperationsSummary/v1) beside
resource-usage-summary.md: real elapsed time, setup, per-stage shares, per-item
chain running time and barrier wait, step durations, pool concurrency, attempt
dispositions, agent transcripts, and machine resources.
A figure the run cannot establish is null with a stated reason, and a failure while
summarizing is logged without changing the run’s outcome.
metaproc operations summary RUN_DIRbuilds the document for a finished run (and
--writewrites it);metaproc operations rollup RUN_DIR...sets runs side by side
against a per-item chain-time target (--target-min-minutes,--target-max-minutes,
10 and 15 by default) and reports a run whose summary does not read asunreadable. A
run that executed as a child scope of a larger run is summarized with its nested
scopes and transcripts.
A run that never reached finalization gets its summary from the nextmetaproc status
on it. A list cost that leaves out unpriced models readsat least. -
A resume records launch-config changes instead of refusing them. v0.4.1 refused a
resume whose resolved variables differed from the onesrun-config.yamlrecorded.
A resume may now change any resolved variable (including through an edited
default:), the--step-variantset,--variant,--artifact-namespace, the
execution profiles the plan resolves,--backend, the spend cap, and the checkout’s
git_sha. Each change prints once asResume changes <field>: <old> -> <new>, all of
one resume’s changes are appended with old and new values as one
launch_config_changeevent in.logs/dispatch-config-changes.jsonl, and
run-config.yamlis rewritten to the values the resume ran with, sostatus --steps
and the operations summary describe the run as it now executes.
A resume still refuses, before recording anything, on a corruptrun-config.yaml, a
different process name, or a different run directory (the two canonical Filestore
mount roots excepted), and names the recorded value and the two ways out.
What re-runs is still decided by step fingerprints: a value substituted into a
resolved field such asenv:re-runs that step and its downstream, while a step that
binds the value throughwith:or reads it at runtime is reused until
--from <step> --forcere-does it. -
A process input can bind itself to one value for the life of a run.
on_change: new_runon aninputs:entry records the value in the scope’s
.state/input-bindings.yaml(metaproc:InputBindings/0.1) when the scope is first
entered, and every later entry compares against it before writing anything: a
run-processresume, a composite child scope afterwith:resolution,run-step,
andrun-parallel. A launch that resolves another value is refused, naming the input,
both values, and the two ways out: the recorded value or a newRUN_ID. A renamed
paramalias is not a change.
on_change: record, the default, is the behavior above: record and continue.
new_runneeds aparam-backed input. -
One nested step or mapped item can be re-run through its parent.
--only <root-step> --only-scope-step <path>enters a composite child by its exact
scope path, each composite step and mapped item key in turn (such as
depth/ACME/fetch), verifies that omitted ancestors completed, and re-runs the
selected step and its descendants; other items, pending ones included, stay untouched.
Repeat the flag for several item keys under one mapped composite, run as one bounded
batch under--max-concurrency. A selector that names a mapped item itself runs its
whole child and can complete that item.
--scope-through-targetruns the target and its prerequisites in their scope and
stops before anything downstream, which prepares a stage without starting its paid
successor. A missing, duplicate, terminal, or differently shaped selection fails rather
than widening the run.
process-status.yamlrecordsselected_scope_step(andselected_scope_stepsfor a
batch) withselected_scope_mode;metaproc statusshowsSCOPEDand a
Selected scope:line, and completion checks do not certify the full run until an
unrestricted evaluation clears the marker.
The selectors cannot be combined with--from,--force,--skip,--cloud, or
--dry-run. -
Every executable leaf of a local run shares one adaptive controller. Mapped agent
steps in a localrun-processnow submit their items to the run-owned RunPool that
scalar agent leaves already used, instead of opening a pool per step, and code leaves
(scalar and mappedmode: code) take permits from the same controller.
Memory and provider pressure therefore throttle all of them, and concurrent mapped
steps share one--max-concurrencyceiling, withfor_each.max_concurrencystill
bounding each step. Pool state and events are written once per run, to
.state/runpool-status.yamland.logs/runpool/, andpool status,rollup,
health, andeventsreport code leaves (Code leaves:,code_active=). A
run-owned pool serves several execution profiles as lanes when each resolves the same
pool ceiling, host-slot limit,estimated_process_rss_bytes, and
initial_memory_budget_fraction;pool statuslists every lane, and a profile that
differs fails before launch with an error naming each differing value.
v0.4.1 refused any second profile on the run-owned pool; see Compatibility for what
extending the pool to mapped steps means for mixed-profile runs. -
Zstandard artifacts, read everywhere and written on request. Every reader of
.jsonl.gzalso reads.jsonl.zst:metaproc tail,status,stats,trace, the
operations summary, and themetaproc.iohelpers.
metaproc gzip-text --codec zstdwrites Zstandard and records the decoded size.
Gzip stays the write default, and a run directory holding both is ordinary.
Metabrowser cannot read.zstyet; see Compatibility. -
Gemini steps run in a private Gemini home. v0.4.1 handed native settings to
gemini-cli throughGEMINI_CLI_SYSTEM_SETTINGS_PATH, and gemini-cli 0.60.0 and later
skip a system settings file that is not root-owned and closed to group and others,
with only aSecurity Warningtranscript line, so a session on such a CLI ran without
the session-retention guard, dynamic model configuration, the step’s tool
restrictions, or the subagent overrides.
A newnative_settings_scopeadapter key defaults touser: each launch gets its own
home under$TMPDIR/metaproc-gemini-*, whose.gemini/settings.jsonholds the
resolved native settings, andGEMINI_CLI_HOMEpoints at it.
native_settings_home_templatenames a prepared home to copy in when a workflow needs
login state, instructions, hooks, or extensions;workspacescope keeps the
operator’s~/.geminiand requiresworking_directory;systemis rejected.
Launches no longer share a project registry.
Three guards keep the settings Metaproc asserts in force:- A launch whose trusted working directory carries a
.gemini/settings.jsonthat sets
an asserted key to a different value is refused, naming the file and keys. - A step
envthat setsGEMINI_CLI_SYSTEM_SETTINGS_PATH,
GEMINI_CLI_SYSTEM_DEFAULTS_PATH,GEMINI_SYSTEM_MD, or (underuserscope)
GEMINI_CLI_HOMEis rejected at planning like a rejected adapter-config key,
through a new optionalvalidate_step_envadapter hook. - Authentication checks follow gemini-cli’s own order (
GOOGLE_GENAI_USE_GCA,
GOOGLE_GENAI_USE_VERTEXAI, each exactlytrue, thenGOOGLE_GEMINI_BASE_URL,
thenGEMINI_API_KEY), report a gateway asgateway, and refuse a template-less
Login with Google launch that has noGOOGLE_CLOUD_ACCESS_TOKENor
GOOGLE_APPLICATION_CREDENTIALSinstead of letting it wait for a browser login.
auth-check --livereports a Gemini config the adapter refuses as a failed
invalid adapter configurationresult.
The pinned gemini-cli moves from 0.55.1 to 0.59.0, which still needs the dynamic-model
setting to servegemini-3.6-flashas asked. - A launch whose trusted working directory carries a
-
Agent CLIs’ own session records survive slot teardown. A pooled Codex or Claude
attempt points the CLI’s config home into its credential slot, and the slot, with the
CLI’s own rollout or transcript, was removed after every attempt.
Those records are the CLI’s account of per-response usage, model, timestamps, and, for
Codex, rate-limit snapshots.
Codex rollouts and, when a step setsno_session_persistence: false, Claude project
transcripts are now copied owner-only to<session-stem>.codex-sessions/or
<session-stem>.claude-projects/under.logs/native/before the slot is removed.
Credential files and symlinks are never copied, andcompact-logsleaves the copies
alone. These are private run data: they hold full prompts, responses, and file contents
the agent read. -
Failures keep a bounded, redacted cause. A failed command step records
command exit code Nwith the tail of its stderr (or stdout when stderr is empty),
and a failed Python handler its exception type and message, each clipped to twelve
lines and 1,200 characters and ending with the attempt’slog:ortraceback:path.
Terminal styling is removed and known credentials are replaced with[redacted]
before anything is stored: framework secrets, declared Secret Manager targets,
BearerandBasicvalues, credential-shapedkey=valuepairs, URL user info, and
credential-named environment values of at least eight characters that are not booleans
or numbers. Retry classification reads the full redacted evidence, so aquotain a
step id no longer makes a transient failure permanent.
Mapped summaries count distinct causes against the current item roster, as in
2 of 3 items failed (2 x timeout after 600s), listing at most five; run status,
completion events, and fan-in outcomes keep every failed step’s cause and output
paths. Credential and input refusals before launch reach durable status without an
attempt, and a mapped code item refused for a missing input fails alone while its
siblings continue. Handlers can produce the same summary with
metaproc.runtime.diagnostics.summarize_diagnostic. -
metaproc.ionames a helper for every write contract. The curated surface adds
atomic_write_text,atomic_write_bytes,copyfile_atomic,copytree_atomic,
temp_output_file,temp_output_dir, andwrite_secret_text, which creates its
staged file at0o600before the first byte and commits it by rename.
metaproc help artifactsnames the contract of every artifact Metaproc writes. -
gemini-3.8-flashhas a list price. The pricing table records Google’s
introductory rate through 2026-12-31 ($0.75/M input, $3.75/M output, $0.075/M cached
input) as its actual price and the standard rate from 2027-01-01 ($1.50, $7.50, $0.15)
as its list price, so its invocations count towardlist_cost_usd.
Fixes
-
A provider quota refusal backs the pool off and retries the item. An agent that
hit a per-minute quota, such as gemini-cli on Vertex reporting
TPM quota exhausted. No spare capacity is currently available, failed permanently
because its message containedquota, and a scalar agent step never told the run
pool, so siblings kept failing the same way until an operator lowered the cap by hand.
A per-minute refusal (tpm quota,rpm quota,tokens per minute,
requests per minute,per_minute,PerMinute,no spare capacity,
RetryableQuotaError) now retries, at most three times and at least 60 seconds apart
on an agent step, inside the declaredmax_retries; a daily, monthly, billing, or
credit limit stays permanent.
One refusal halves the pool’s provider ceiling at once; failures from processes
admitted before that cut only hold recovery, and recovery resumes after
quota_backoff_hold_s(300 seconds) without another refusal.
Manual mode leaves the operator’s ceiling alone, andDisk quota exceededis not a
provider quota. Each response is aquota_backoffevent thatmetaproc pool events
formats. Scalar agent steps now report every classified failure to the pool, so a named
reset time pauses submissions.
And a fan-out item whose quota failure named a reset time, which v0.4.1 rescheduled
without advancing its attempt number and so relaunched as a paid session indefinitely,
now stops at its retry budget. -
A resume re-runs a consumer whose collected input changed. A
collect:consumer
was reused whenever its fingerprint matched, so after a resume finished mapped items
that had failed, the consumer and its downstream kept output computed over the partial
outcomes and the run reported success.
Each fan-in document a step is handed now has an outcome digest (upstream step,
totals, and each item’s key, state, and success) recorded in
.state/tasks/<step>/collected-inputs.yaml(metaproc:CollectedInputs/0.1). When the
rebuilt document’s digest differs, the step and its downstream are invalidated as for
a fingerprint change, with acollected input ... changed since the step last ran
line. An item that fails again with a different message is not a change.
A downstream composite or mapped step is re-entered and keeps its completed child
steps or items.status --stepsnames a changed collected input among an invalidated
step’s causes. -
A resume from another checkout of the same revision reuses completed work. Step
fingerprints hashed the absolute paths a plan resolves./and../references to,
so planning from a checkout at another path re-ran every composite, every step loading
a runbook, and everything downstream.
Paths inside the process’s checkout (the nearest directory holding.git, recorded as
ResolvedStep.checkout_root) are now hashed relative to it, wherever they appear in
the resolved step. Paths outside it, and every path of a process not in a checkout,
hash as before. See Compatibility for the one-time cost. -
A run serves several execution profiles from one pool. v0.4.1 failed the second
profile to reach the run-owned pool.
Profiles whose pool resources resolve equal now share it as lanes, as described under
Features. -
A composite step’s
execution_profile:applies to its whole subtree. The child
process was planned on the launch--variantwhatever the composite pinned.
A pinned composite now plans its child, every nested scope, and their unpinned agent
leaves on its own profile, and answers{{run.execution_profile}}with it.
Agent-step pins inside the subtree still win. -
--step-variantrefuses what it cannot override, and a resume keeps it. A step id
inside a composite child, or a composite step itself, was silently ignored or replaced
the composite’s pin; it now fails launch validation, listing the steps that can be
overridden.run-config.yamlrecords the overrides, a resume without the flag reuses
them, andstatus --stepsplans them. -
Agent leaves take a host slot when their pool admits them. A
run-processagent
leaf took its host slot before pool admission, with a default limit of 4, so leaves
queued behind adaptive capacity or a lowermax_concurrency_hintwaited 60 seconds
and launched without one.
The slot is now taken inside the pool, after admission, with a limit defaulting to the
pool’s ceiling;resources.host_max_concurrencystill replaces the default and
METAPROC_HOST_MAX_LOCAL_AGENTSstill lowers it.
Host-admission refusals recorddecisionandwaited_s, and
pool events --type host_admission_denied --summarycounts waits, bypasses, and wait
seconds. -
An agent fan-out inside a composite child records its results. Its items took a
task identity from the child process’s name while the rest of the scope used the
identity the parent derived, so an item that had just succeeded failed its own result
write withresult does not match latest attempt fields run_id. Any mapped composite
whose child fanned out an agent step hit this. -
Scalar agent timeouts follow the retry policy. A timeout now classifies, backs
off, checks credentials, and counts toward the retry limit like a retryable nonzero
exit, and attempt history keeps the timeout cause.
A timed-out process is never accepted as a successful shutdown. -
Smaller step-contract fixes. An output declared
optionalmay now be absent
(validation never read the flag).
run-processcode steps writeprocess_<attempt_id>.lograther than
process_<UTC seconds>.log, so two attempts in one second no longer share a log.
working_directorytakes effect under Claude execution profiles, as it does for
Gemini and Codex. A file handler in a checkout nested inside another imports its own
checkout’s code, nearest root first, instead of the enclosing checkout’s same-named
packages. -
Foreground agent commands clean up their whole process group.
run-step --wait,
auth-check --live, andprobe-tool-usesignalled only the command’s leader on
timeout or interrupt, so a Gemini prompt pipeline, Codex’s shell wrapper, or a tool
subprocess could outlive the command.
They now run every adapter’s command in its own process group and end the group on
timeout, interrupt, or a SIGTERM, SIGHUP, or SIGQUIT to Metaproc.
A leader that exited while a descendant still holds its output pipe is collected with
its output rather than reported as timed out.
run-step --waitwith the pi-cli log filter still waits on the leader alone. -
auth-check --assert-modelchecks which model answered. For gemini-cli the
assertion read theinitevent, which reports the configured model before any request
leaves the process, so a call the CLI rewrote to another model still matched.
It now reads the terminalstats.models, counts only models that billed tokens, and
fails when none did, and a run refuses a Gemini result by the same rule.
auth-check --live --variant <profile>probes with that profile’s own adapter config,
native_settingsincluded. -
Usage and cost figures say what they cover.
output_tokensis output-rate tokens
including reasoning; a partial Gemini stats block no longer attributes its whole total
to output (one regression fixture moves from 1,650,379 to 9,429 output tokens).
An invocation whose model has no list price added nothing tolist_cost_usd, so the
total read as complete;resources.jsonandresource-usage-summary.mdnow carry
unpriced_models, the summary reports the cost as a lower bound, and
resource-reportlists them.
Repeated Gemini tool calls that reuse an id are counted separately in resources and
traces instead of merging into the first. -
A Gemini prompt reaches the CLI through a pipe. The prompt file was redirected
into gemini-cli’s stdin as a regular file, which its timed stdin reader can queue
behind other I/O; it is now streamed through a pipe, and a missing prompt file stops
the launch before the CLI starts. -
Credential files are never readable beyond their owner, even briefly. Claude,
Codex, and Gemini credential and settings files were written and then narrowed to
0o600, and~/.pi/agent/models.json, which the GCP container bootstrap writes and
which can hold a literal provider key, was never narrowed.
All are now created at0o600and published atomically,models.jsonin a0o700
directory. Traces, RunPool invocation sidecars, and copied slot debug logs are also
published atomically, so a crash leaves the previous file instead of a short one that
reads as valid. -
metaproc tailfollows compressed and damaged logs. It now discovers.jsonl.gz
transcripts, so an attempt whose log was compressed on close stays visible; replays a
transcript compacted or compressed under it once, rebuilding usage rather than adding
to it; skips a truncated or corrupt compressed log with one warning instead of
raising; and reads a multi-member gzip once instead of on every poll.
trace,stats,pool, andauth usagewarn at a damaged compressed transcript
instead of stopping silently or raising. -
Pool control survives its own bookkeeping.
pool override --clearrestores
capacity on the next tick instead of at the next ceiling change.
A failed event-log write no longer fails the item that triggered it: the capacity
change, retry, or quota pause it records still happens, and a pause still lifts. -
SoftSchema CLI validation keeps each library record, with its skip reasons and any
execution evidence the installed SoftSchema reports.
Guidelines and content
- Agent Skill: the shipped skill’s topic catalog is regenerated for the grown help
topics (operator 6.6k → 11.5k words, design 18.5k → 22.0k) and the newarch-file-io
description. Its routing and operating guidance are unchanged, and the committed
.agents/and.claude/copies match. - Bundled data:
pricing.mdaddsgemini-3.8-flash; the default execution profiles
change only in their notes, with no resource, model, or capability edits.
Documentation
All shipped documents are metaproc help topics, so these change the installed package.
- Resume principles in one place.
metaproc help concepts§6.2 principle 7 now
states the contract this release implements: a rerun against the sameRUN_IDis
simple, resumable, transparent, idempotent, and flexible; reuse follows content; a
check on the resume path records and warns, and refuses only to protect durable state,
live work, or anon_change: new_runbinding.
metaproc help developerapplies it to extensions, and
metaproc help execution-contractsgains Inputs: Identity, Reuse, and Evidence. - Operator reference:
metaproc help operatorgains the nested-scope selectors,
Spend Cap and Breaker Stops, the launch-config changes a resume records, the
collected-input check, and the operations summary questions it answers. - Write contracts:
metaproc help arch-file-iogains Write Contracts, and
metaproc help artifactslists every new artifact, the contract each is written
under, and the credential files Metaproc writes outside the run directory. - Launch contract:
metaproc help developerstates the contract every adapter’s
foreground command shares;metaproc help arch-runpooldocuments the quota governor,
several profiles in one run, and host-slot limits per launcher. - Cloud image boundary:
metaproc help cloud-dispatchstates the image interpreter
requirement and how to cross it.
Compatibility
-
Python 3.14 or newer is required.
requires-pythonis>=3.14, so an environment
on 3.12 or 3.13 cannot install v0.5.0; v0.4.1 remains available for it.
The floor is what lets Zstandard come from the standard library’scompression.zstd
with no optional backend. -
Check the GCP Batch image before dispatching with a shipped wheel. When
metaproc gcp runships its wheel, container bootstrap force-reinstalls it into the
image’s/opt/venvwithuv pip install, which refuses a wheel whose
requires-pythonthat interpreter does not satisfy.
On an image below Python 3.14, dispatch fails at container bootstrap, not at build or
test time, and reads as a wheel-install refusal rather than a version problem, so
neither CI normake verifycatches it.
This release could not verify the published image’s interpreter (tracked as
mp-mo74). Before dispatching, do one of these:- rebuild the image on a 3.14 interpreter and re-pin
METAPROC_GCP_CONTAINER_IMAGE; - pass
--no-wheelto run the Metaproc baked into the image; - hold the dispatching side at 0.4.x until the image is rebuilt.
metaproc help cloud-dispatchdescribes this boundary. - rebuild the image on a 3.14 interpreter and re-pin
-
Metabrowser cannot read
.zsttranscripts yet. It decodes.gzbut not.zst,
so a Zstandard transcript opens as binary with no charts.
Gzip remains the write default, forgzip-textand for logs compressed on close; keep
transcripts on it wherever the browser view matters. -
Mapped agent steps on profiles with different pool resources no longer share one
local run. Because mapped agent steps now join the run-owned pool, the
profile-equality rule applies to them: a localrun-processrun whose mapped agent
steps pin profiles that resolve differentestimated_process_rss_bytes,
initial_memory_budget_fraction, pool ceiling, or host-slot limit now fails the later
step before launch. v0.4.1 gave each mapped step its own pool, so such a run worked.
The bundled profiles fall into three groups that do not match each other: the Claude
andpi-*profiles (500 MB, 0.25), the gemini-cli profiles (250 MB, 0.5), and
codex-gpt55(1,024 MB, 0.25, hint 20, host limit 20). A run mixing, for example, a
Claude fan-out with a Gemini fan-out must align those resources in its own profiles or
run the steps as separate runs.
A composite that pins a profile now runs its subtree on it, which can bring a child
step under the same rule. -
One ceiling for the whole local run. Concurrent mapped agent steps, and code
leaves, now share the run’s--max-concurrencyand adaptive capacity instead of each
mapped step getting its own, so a run that relied on per-step pools to multiply
concurrency runs narrower.
Pool state for a local run lives only at<run>/.state/runpool-status.yamland
<run>/.logs/runpool/; readers that looked under.state/steps/<step>/or
.logs/runpool/steps/<step>/for a local mapped step find nothing there.
run-paralleland thegcp-workerbackend keep their own pools. -
Fingerprints change once on upgrade. Every step whose resolved definition names a
path inside its checkout gets a new fingerprint, so a run recorded at an earlier
release re-runs those steps and their downstream on its first resume at v0.5.0, as it
would after an edit to each.
Resume such a run with the release it launched with to avoid that.
A run started before this release also has nocollected-inputs.yaml: backfilling its
failed items still reuses the consumer, so run--from <consumer> --forceonce;
--only <consumer> --forceis not enough. -
Resume behavior changed in both directions. Changed variables and other launch
config are now recorded and accepted rather than refused, andrun-config.yamlis
rewritten after a resume that changes it; tooling that treated that file as immutable
should readdispatch-config-changes.jsonlfor history.
A resume that cannot append that log stops before any step runs.
--step-variantnaming a step inside a composite, or a composite step, now fails
launch validation.metaproc statuson an inactive run with no
operations-summary.md, a v0.4.x run included, now writes one. -
Gemini launches no longer see the operator’s
~/.gemini. Under the defaultuser
scope its settings, login, hooks, instructions, extensions, and MCP servers do not
apply, so authentication must come from the launch environment (GEMINI_API_KEY, or
GOOGLE_GENAI_USE_VERTEXAI=truewithGOOGLE_CLOUD_PROJECTand
GOOGLE_CLOUD_LOCATION) or a home template.
A strayGOOGLE_GENAI_USE_GCA=truenow refuses the launch; gemini-cli still reads
.envfiles upward from the working directory, so keep keys in the shell environment
or the project.envrather than~/.gemini/.env. Set
native_settings_scope: workspaceto keep the old home.
Each private home collects that launch’s full session state and is removed at normal
exit but left inTMPDIRwhen Metaproc is killed.
A Gemini result in which the requested model billed no tokens is now refused. -
Per-minute quota refusals now spend more. They retry, up to three more full agent
sessions per item inside the declaredmax_retries, where v0.4.1 failed them at once.
A declaredmax_retries: 0and a spend cap’s worst case are unchanged. -
Handlers import only their own checkout’s code. Code that exists only in an
enclosing checkout is no longer importable from a file handler, and within one
checkout a module nearer the handler now wins over a same-named one further out. -
Renamed and removed Python APIs. These break direct importers only; no CLI surface
changed with them.metaproc.io.gz_ioismetaproc.io.artifact_io, with the codec registry in
metaproc.io.compression.gzip_siblingiscompressed_sibling, which takes a
codec.ArtifactPath.is_gzipis gone; usecodec,is_compressed, orcompression.
disk_sizeandlogical_sizekeep their meanings under every codec.- A compressed artifact that cannot be decoded raises
ArtifactDecodeError, an
OSErrorsubclass, so an existingexcept OSErrorcovers it, and also now catches
damage it used to miss. - In
metaproc.commands.gzip_text,gzip_fileiscompress_file,
SourceChangedDuringGzipisSourceChangedDuringCompression, and
GzipTextSummary.gzippediscompressed; summaries and results carrycodec. metaproc.engine.fan_in.write_outcome_manifestis removed; use
build_outcome_manifest(...).write(...).
Additive Python surface:
artifact_sidecar_path,ArtifactDecodeError, the write
helpers above,SECRET_FILE_MODE,summarize_diagnostic, and the optional adapter
protocolsStepEnvValidatorandNativeSessionLogCapable. Existing adapters and
plugins need no change. -
Changed CLI output strings.
gzip-texttext output names the codec: per-file
lines readgzip:orzstd:instead ofgzipped:, the total reads
N compressed with <codec>, the dry run sayswould compress, and the skip reason is
already-compressed.--levelnow defaults per codec; gzip’s default is still 6.
Command failures readcommand exit code N (stderr: ...; log: <path>). A Gemini
command killed by a signal now reports exit code128+Nrather than-N, because its
prompt pipe keepsshas the leader.
A script scraping any of these needs updating. -
New process-file fields, and new runtime artifacts.
inputs.<name>.on_change,
steps[].spend,for_each.breaker, and process-levelspend_capare new; v0.4.x
rejects a spec that usesspend,breaker, orspend_cap, and ignoreson_change.
Aninputs:entry now rejects an undeclared field instead of ignoring it.
New artifacts:operations-summary.mdwith its compiled schema under
.state/schemas/,.state/input-bindings.yaml,.state/spend-ledger.yaml,
.state/tasks/<step>/collected-inputs.yaml, and native session copies under
.logs/native/. Existing records gain optional fields:stopped,
selected_scope_step,selected_scope_steps, andselected_scope_modein
process-status.yaml;step_variantsandmax_spend_usdinrun-config.yaml;
unpriced_modelsin the resource documents;active_code_count,
pending_code_count, andstopped_atin pool status;decisionandwaited_son
host-admission events; a newquota_backoffpool event; andon_change,
checkout_root,spend, andbreakerin the run plan andVizModel. No existing
schema token changed. -
Nothing else public was removed. No CLI command or flag, no adapter default
(opus,gpt-5.5,gemini-3.1-pro-preview-customtools,sonnet), no accepted model
identifier, and no plugin entry point was removed or renamed. -
Still open from v0.4.0: Gemini steps ship with
context.fileFiltering.respectGitIgnore: False, so a Gemini step’s file tools can
read ignored files anywhere in the workspace,.envincluded.
The private home does not change this.
Whether the default should become opt-in is still open asmp-x1qh; treat a workspace
a Gemini step runs in as readable by that step.
Full commit history:
v0.4.1 … v0.5.0