Skip to content

feat(store)!: name every derived id <base>__<suffix>, bare or @local - #26

Merged
earakely-scale merged 13 commits into
mainfrom
edgararakelyan/derived-ids-keep-namespace
Oct 5, 2026
Merged

earakely-scale merged 13 commits into
mainfrom
edgararakelyan/derived-ids-keep-namespace

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

The rule

def derive_id(base: str, suffix: str) -> str:
    return f"{base}__{suffix}"

Every id core names after an entity or a run is derive_id(base, suffix), for a bare base and an @local one alike, so the base's namespace carries over and each site has one spelling. Only one naming rule branches on namespace: a universe-compatibility id belongs to the env, unless only the universe is @local.

task_id = derive_id(self.id, f"validate-v{self.version}")
# "tickets" (v3)           -> "tickets__validate-v3"
# "@local/~/shop/envs/web" -> "@local/~/shop/envs/web__validate-v3"

Documents keep the whole id. Object keys and image repositories take it through key_segment, and filenames through fs_safe. An @local id becomes the bounded local/<slug>-<12 hex>. In an object key or image repository, a bare id over 200 bytes becomes <slug>-<12 hex>. Any other bare id passes through unchanged.

What changes for existing deployments

New entities get new names. Stored entities keep theirs: stores are append-only, so an old id keeps resolving to what was written under it.

What Before After
env validation task (MCP server, website, multi) validate-<env>-v<n> <env>__validate-v<n>
universe-compatibility task validate-universe-compat-<env>-v<ev>-<u>-v<uv> <env>__validate-universe-compat-v<ev>-<u>-v<uv>
its roundtrip artifacts (-export1…, -export2-…, -fau) validate-<env>-v<ev>-<u>-v<uv>-… <env>__validate-v<ev>-<u>-v<uv>-…
A2A validation task, install-image universe validate-a2a-<agent>-v<n>, validate-install-image-<agent>-v<n>-<ts> <agent>__validate-a2a-v<n>, <agent>__validate-install-image-v<n>-<ts>
create_cli task, CLI artifact (build_mcp_cli's default) create-cli-<env>-v<n>, cli-<env> <env>__create-cli-v<n>, <env>__cli
env snapshot image artifact env-snapshot-<env> <env>__env-snapshot
env snapshot image name env-snapshot-<env>-<universe> unchanged, unless it is over 200 characters or not a valid Docker name; then env-snapshot-<env slug>-<12 hex>
snapshot_env with no snapshot_id snapshot-<env>-<last 16 of the instance id> <instance id>__snapshot-<step id>
a prompt step's capture series <step>-workspace-<tail>, snapshot-<env>-<tail> <instance id>__<step>-workspace, <instance id>__snapshot-<step>
unit-test verifier stdout / stderr verifier-stdout-<step>-<instance id>-<ts>-<hex> <instance id>__verifier-stdout-<step>-<ts>-<hex>
collect_artifacts universe <universe or instance id><suffix>, spaces to -, / to _, cut to 100 chars kept whole, as written
its per-file artifacts <universe>-<path with / as _>, cut to 200 <universe>__<sha256(path)[:16]>
task run-batch --seeds universe the seed's id or name <task id>__v<n>-<seed id or name>
skill over a loaded file universe (agents see this name) <universe>-files <slug of its last segment>-<12 hex>-files
run-scoped ids above in a run with no instance id a random token adhoc-<12 hex>__… (collect still falls back to unknown<suffix>)

Unchanged: the put commands' image ids (mcp-server-<id>, a2a-agent-<id>, …), per-service ids <snapshot>-<service> and …-file, the -fau / -export… suffixes, the A2A fixture keys of a bare agent, and step ids.

  • Bug fixes. Runs whose ids shared their first 100 characters no longer collect into one universe. snapshot_env and a capture series on the same env in one run no longer write versions of one universe. In task run-batch, the same seed name in two tasks, or in two versions of a task, no longer collects into one universe.
  • Longer ids. Run-scoped ids now hold the instance id and the step id. The snapshot's temp file is named after the service only, and file-artifact-universe get-many writes a bare universe over 200 bytes into its key segment's directory.
  • Split histories. New ids start at version 1, so a history splits at the release (an env's CLI, an env snapshot). An env whose metadata caches a CLI keeps cli-<env> until create_cli(force=True) builds <env>__cli.
  • Literal references. A stored task that names an old id literally keeps loading what was written under it. Read ids from the recorded references (env_snapshotted_universes, collected_artifacts, the env's cli_artifact, the compatibility record) rather than rebuilding them.
  • Link spellings. Two spellings are a contract for clients that link to a validation task: <env>__validate-v<n> and <env>__validate-universe-compat-v<ev>-<universe>-v<uv> (an @local universe goes through fs_safe).

@local entities and runs

  • collect_artifacts refuses a bare universe, or an @local one that isn't a valid id, before any sandbox work. For a task that collects, task run-batch checks each seed's @local universe, including the step's universe_id_suffix, before any run starts, and names the seed row.
  • snapshot_env, a capture series and the unit-test verifier refuse a step id that can't sit inside an @local id before they export or run anything.
  • A2A validation: the install-image universe and the fixtures go to the agent's own object store, under key_segment keys. For an @local agent, validation stops at the existing "requires a signable object store" error and writes nothing to the configured store.
  • Env snapshots: the tarball goes to the env's own object store under a key_segment key; the snapshot checks that the store can sign the upload before the sandbox runs anything; an env and a universe in different namespaces are refused up front.

Rollout

  1. Before the release, update any client that rebuilds a validation task id to link to it: look up the new spelling first, then the old one. Env versions validated earlier keep their old task ids, so the fallback stays.
  2. Merge and release this PR.
  3. Bump dependents' pins. A dependent that reads ids from recorded references needs no other change. A run retried across the upgrade writes its per-run artifacts under the new names, and its context records those.
  4. Check one env validation and its link, and one run that snapshots and collects.

Re-running a stored compatibility task

A universe-compatibility task created before this release pins its judge's load_artifact step to the old file-universe id, validate-<env>-v<ev>-<u>-v<uv>-fau, while the roundtrip step derives the id when it runs. Re-run on this release, the task writes <env>__validate-v<ev>-<u>-v<uv>-fau, and the judge loads the latest version of the old universe, so it grades the previous run's files. Re-create the task with validate_universe_compatibility rather than re-running a stored one.

Removed

  • refuse_local_derivation and its six calls: the stop-gaps that refused these cases until now.
  • The put commands' validation skip for @local ids, so env multi put --validate now validates an @local env. The a2a-agent, mcp-server and website puts still stop earlier, at their <prefix>-<id> image id, until the put commands' image ids keep the namespace too.
  • collect_artifacts' id sanitizer and its 100-char cap.
  • The capture series' 16-char discriminator.
  • create_cli's own CLI id: build_mcp_cli's default names it. The bundle resolver keeps that default's last segment, so a CLI built for another bundle's env is still named under this task.

Testing

  • Unit tier (make unit-test): 5,856 passed, 13 skipped.
  • Every changed site's goldens are flipped to the new spellings. tst/unit/store/derived_ids_test.py pins a bare and an @local row per site, including the two link spellings, and the Docker validity of the snapshot image name.
  • The @local fence and acceptance tests assert that nothing reaches the configured store (sentinel stores).
  • Long ids run on the real local filesystem store: a 297-byte seed universe collects, a 272-byte snapshot id snapshots, and file-artifact-universe get-many writes a 300-byte universe.
  • Integration: tst/integration/cli/run_bundle_test.py gains a bundle task that ends with collect_artifacts; it needs GNU stat, so it skips on macOS and runs in CI. CI's integration-local and integration-local-slow are green.

🤖 Generated with Claude Code

RetriggerConfidence Score: 0/5

The PR does not appear safe to merge: public staging uploads and broken agent transfer paths need attention.

Summary

The PR gives derived entity and run IDs a shared base__suffix format, so stored tasks and artifacts keep a clear link to the entity or run that produced them. It also updates local-ID handling and storage names to support these IDs, including long ones.

  • Renames validation tasks, compatibility artifacts, CLI artifacts, snapshots, and collected outputs.
  • Keeps run and seed outputs distinct, and encodes IDs when they become storage keys or image names.
  • Allows more operations on @local entities while keeping their writes in the matching local store.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[AgentEnv snapshot call] --> G[Local store grant]
  G --> R[Remote agent cannot reach host grant]
  S[SDK staging route] --> E[Agent endpoint]
  A -. no staging transfer .-> S
Loading

Reviews (12) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..."

earakely-scale and others added 2 commits September 30, 2026 10:31
An id a step or a validator names after something else now keeps that thing's namespace:
store.ids.derived_id(base, suffix, legacy=...) gives derive_id(base, suffix) for an @Local base, and
otherwise the legacy spelling, passed verbatim at every call site, so bare ids are byte-identical.

- Ids a run creates are named after the run when its instance id is @Local: collect_artifacts' universe
  and per-file ids, snapshot_env's derived snapshot id, snapshot_series' workspace and env captures, and
  the unit-test verifier's stdout and stderr artifacts. collect_artifacts refuses a bare universe before
  any sandbox work; mkstemp prefixes go through fs_safe.
- Ids named after an entity use its namespace: the MCP server, website, multi and A2A validation tasks,
  create_cli, the A2A install image and fixtures (key_segment keys, the entity's own object store), the
  universe compatibility task and roundtrip ids (one validation_id helper), env snapshots and the
  add_skills name for an @Local universe.
- The temporary refusals that stood in for this go, and so does the put commands' validation skip for
  @Local ids. An env snapshot checks that the store can sign its upload before the sandbox runs anything,
  and refuses an env and universe in different namespaces.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d-ids-keep-namespace

# Conflicts:
#	README.md
@earakely-scale
earakely-scale requested review from a team and polakamtejas as code owners September 30, 2026 20:36
Comment thread src/agent_env/task_step/task_steps/collect_artifacts.py
…work

- task run-batch checks each seed's @Local universe id before any run starts, and names the seed row;
  collect_artifacts validates an @Local universe before reading the sandbox, so a bad universe_id_suffix
  fails early too.
- Test data no longer uses an internal codename; the prefix-key test uses the shared signing fake.
- AGENTS.md lists the gnu_stat capability; the ids.py comment and derived_id docstring say what holds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale
earakely-scale requested a review from a team as a code owner September 30, 2026 21:37
Comment thread src/agent_env/cli/task/run.py Outdated
earakely-scale and others added 2 commits September 30, 2026 11:45
run-batch's up-front check stopped a whole batch over a seed name that a task without a
collect_artifacts step never uses as a universe id. It now runs only when the task collects.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The fake VM collect_artifacts reads and the env-snapshot reattach setup were copied between three test
files; they live once in tst/unit/store/fakes.py now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale earakely-scale changed the title feat: ids derived from an @local entity or run keep the @local namespace feat(bundle): keep the @local namespace in ids derived from an @local entity or run Sep 30, 2026
@earakely-scale

Copy link
Copy Markdown
Collaborator Author

@greptileai review

- collect_artifacts names a per-file artifact with an explicit branch, so a bare universe runs exactly the
  expression it always has and only an @Local one hashes the name.
- validation_id lists its three cases (an @Local env, an @Local universe, neither) in order.
- run-batch's seed rule is explained once, in _seed_universe_id's docstring, and its check skips a seed with
  no universe before validating.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale
earakely-scale marked this pull request as draft October 1, 2026 18:59
earakely-scale and others added 3 commits October 1, 2026 12:23
Every id named after another now uses derive_id(base, suffix), so a bare base and an @Local base are
spelled the same way and the core keeps no legacy spellings: derived_id goes, and so do the legacy
expressions, collect_artifacts' sanitizer and its 100-char cap, and the snapshot series' discriminator.

- Ids a run creates derive from the run's instance id, or an adhoc-<12 hex> base when there is none; the
  snapshot export's temp file is named after the environment only.
- An env's CLI artifact is <env>__cli, named by build_mcp_cli's default alone; the bundle resolver keeps
  a defaulted output under the task.
- add_skills names a file universe's skill <slug>-<12 hex>-files for every universe.
- The compatibility id is owned by its @Local side when only one side is @Local, else by the env.

Prod writes these names for new entities; stored ones keep theirs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e bad ones before work

- The env snapshot image name falls back to an env slug and a hash when the full name is over
  200 characters or not a valid Docker name, so a long or mixed-case universe no longer fails
  `docker build`.
- `key_segment` gives a bare id over 200 bytes a bounded `<slug>-<12 hex>` segment, so long run
  and seed ids fit the local filesystem store's 255-byte path component. Ids stay whole.
- The verifier and workspace capture keys go through `key_segment`; `file-artifact-universe
  get-many` names its directories the same way.
- `task run-batch` checks each seed's universe with the collect step's suffix, and
  `snapshot_env`, a capture series and the unit-test verifier refuse a step id an @Local id
  can't hold before they export or run anything.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale earakely-scale changed the title feat(bundle): keep the @local namespace in ids derived from an @local entity or run feat(store)!: name every derived id <base>__<suffix>, bare or @local Oct 2, 2026
@earakely-scale

Copy link
Copy Markdown
Collaborator Author

@greptileai review

Comment thread src/agent_env/cli/artifact/file_artifact_universe.py Outdated
Bounding every directory name and flattening its `/` put universes `crm/eu` and `crm-eu` in one
directory, where same-named files overwrote each other. A directory is named after the universe
again; only a bare id too long for one filename takes `key_segment`'s bounded name.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale

Copy link
Copy Markdown
Collaborator Author

@greptileai review

Comment thread src/agent_env/cli/artifact/file_artifact_universe.py
@earakely-scale
earakely-scale marked this pull request as ready for review October 5, 2026 16:03
@earakely-scale

Copy link
Copy Markdown
Collaborator Author

@greptileai review

…d-ids-keep-namespace

# Conflicts:
#	tst/unit/config/test_fixture_prefix_keys.py
#	tst/unit/task_step/test_agent_state_capture.py
@earakely-scale
earakely-scale merged commit 8acf73f into main Oct 5, 2026
19 of 26 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/derived-ids-keep-namespace branch October 5, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant