Skip to content

feat: dl --ls says which workspaces run an older image - #674

Open
blooop wants to merge 7 commits into
mainfrom
feat/stale-image-ls
Open

blooop wants to merge 7 commits into
mainfrom
feat/stale-image-ls

Conversation

@blooop

@blooop blooop commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Part of #673 — A workspace keeps its old image after a pull, and a recreate ends its agent. This PR is part 1 of 3: it finds the stale workspaces. Part 2 makes dl <ws> recreate keep the Claude session. Part 3 adds dl --refresh-stale.

What this changes

  • A new module, flows/stale_images.rs, finds each workspace whose container runs an older image than its image reference now names. This PR calls such a workspace stale.
  • dl --ls prints one line on stderr under the table for each stale workspace. The table does not change:
    kinisi-main-abcd runs an older image than ghcr.io/o/img:latest now names. Run `dl kinisi-main-abcd recreate` to move it.
    
  • dl --ls --json adds a staleImage key at the end of a stale row. The value is the image reference. The key is absent on every other row, so a machine with no stale workspace prints the same document as before.
  • docker::inspect_formatted runs docker inspect --type <kind> --format <format> <names...>. It keeps stdout when docker exits 1, because docker prints a line for each name that it found and exits 1 if one name is missing.
  • docs/agents-using-dl.md and CHANGELOG.md describe the new key and the new note.

Where this fits

devpod writes workspace_result.json after each completed up. The check reads three fields from it and then asks docker:

for each workspace with a workspace_result.json:
  container  = ContainerDetails.Id
  created    = ContainerDetails.Config.Image     # the image the container was made from
  reference  = MergedConfig.image or created     # the image the devcontainer declared
running = image of container                     # docker inspect --type container (one call)
current = image that reference names now         # docker inspect --type image (one call per reference)

stale when running.id != current.id and:
  created == reference                           # the container was made from the reference
  or (running.layers does not start with current.layers   # devpod built a derived image
      and current.created > running.created)               # and the reference is newer

The time check stops a false alarm. A prebuilt derived image from a registry can sit on a newer base than this machine's copy of the reference. Without the time check, the note would stay after each recreate.

If any read fails, the workspace reads as current. The reasons are: no result file, a container that docker no longer has, a reference that this machine never pulled, or no docker.

How I checked it

Before: no fixture wrote Config.Image, so the --ls tests never ran the check. After:

  • dl/tests/read_side.rs puts a fake docker on PATH and runs the real binary. dl --ls prints the note on stderr, and the table is the same. The --ls --json row ends with staleImage.
  • stale_images.rs has 12 unit tests on FakeRunner. They cover the direct case, the derived case, the prebuild on a newer base, a missing container, a missing reference, no docker, and two workspaces that share one reference (one inspect of the reference).
  • Each new rule has a test that fails when the rule is removed. I removed the time check, and a_prebuilt_image_on_a_newer_base_than_the_local_reference_is_current failed.

Commands that I ran in the devcontainer:

  • cargo test --workspace: all pass.
  • cargo clippy --locked --all-targets -- -D warnings and cargo fmt --check: clean.
  • pytest test/ against the debug build: 845 passed.
  • prek run --from-ref origin/main --to-ref HEAD: passed.

I did not run:

  • A real docker. This devcontainer has no docker daemon.
  • scripts/public-api-snapshots.sh. cargo-public-api and the nightly toolchain are not in the container. I copied the diff from the public-api CI job into public-api.rest.txt.

Merge danger

Door: two-way. The JSON key is additive and absent by default. A revert removes the note and the key.

Blast radius: small. If the rule is wrong, dl --ls prints a wrong note, or misses one. dl --ls now also makes 2 + R docker inspect calls (R is the count of distinct references), each with a 10s limit. A docker daemon that hangs makes dl --ls slower.

Review notes

  • A compose workspace can read as current when it is stale. For a compose devcontainer, MergedConfig.image is empty. If devpod built a derived tag for the service, that tag does not move on a pull, so the check sees no change. The issue measured kinisi_ros. Someone must read one real kinisi workspace_result.json on the host to confirm which case it is.
  • A rebuild of the declared image that changes only its configuration (ENV or LABEL) keeps every layer. A derived container then reads as current. This is a miss, not a false alarm.

Stack

  1. #674 — feat: dl --ls says which workspaces run an older image ← this PR
  2. #675 — feat: dl recreate starts the agents it ends again
  3. #676 — feat: dl --refresh-stale recreates the stale workspaces that are idle

🤖 Generated with Claude Code

blooop added 6 commits October 7, 2026 14:30
…erence

A pull moves an image reference and leaves every running container on
the image it was created from. Nothing said so, so long-lived workspaces
drifted days behind (#673).

The check reads devpod's workspace_result.json for the container id, the
image it was created from and the image the devcontainer declared. It
asks docker for the container's image and the reference's current image.
Where devpod derived an image on top of the declared one, the container
is stale when the declared image's layers are no longer the start of the
running image's layers. Every doubt reads as current.

`dl --ls` prints one line per stale workspace under the table.
`dl --ls --json` appends `staleImage` to the row, absent otherwise, so a
machine with nothing stale prints the same document as before.

Claude-Session: https://claude.ai/code/session_01Ro7QyyAtHmrh9oxyyK6h9m
…own container

A reference whose layers all appear in the running image but not as its
prefix now reads as stale, and two workspaces behind one reference are
each judged by their own container, with the reference inspected once.
The clean row now carries a stale image before the mark, so a mark that
skips rows absent from the answer leaves it standing and the test fails.
The layer rule said "different", not "older". A prebuilt derived image
pulled from a registry can sit on a newer base than this machine's copy
of the declared reference. Its layers then differ, the note said stale,
and a recreate pulled the same prebuild again, so the note never cleared.

The derived case now also asks that the declared image was created
after the running one. docker's Created is cut to whole seconds so two
of them compare in time order as strings.

Claude-Session: https://claude.ai/code/session_01Ro7QyyAtHmrh9oxyyK6h9m
@sourcery-ai

sourcery-ai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

This PR detects workspaces whose running containers lag behind their current image references by combining devpod's workspace_result.json data with bounded, batched Docker inspections, then surfaces actionable recreate guidance on stderr and an optional staleImage field in --ls JSON while preserving the existing table and clean-output behavior.

Sequence diagram for stale workspace detection during dl --ls

sequenceDiagram
    participant CLI as dl --ls
    participant Listing as Listing flow
    participant Devpod as workspace_result.json
    participant Docker as Docker

    CLI->>Listing: stale_images(runner, devpod_home, workspace_ids)
    Listing->>Devpod: Read ContainerDetails and MergedConfig.image
    Listing->>Docker: inspect_formatted(container, container ids)
    Docker-->>Listing: Running image ids
    Listing->>Docker: inspect_formatted(image, running image ids)
    Docker-->>Listing: Running image metadata
    loop Each distinct image reference
        Listing->>Docker: inspect_formatted(image, reference)
        Docker-->>Listing: Current image metadata
    end
    Listing-->>CLI: StaleImages
    alt Table output
        CLI->>CLI: stale_image_notes(workspace, reference)
        CLI-->>CLI: Write recreate guidance to stderr
    else JSON output
        CLI->>Listing: mark_stale_images(rows, stale)
        CLI-->>CLI: Append staleImage only on stale rows
    end
Loading

Flow diagram for stale image classification

flowchart TD
    A[Read workspace_result.json] --> B{Container and reference available?}
    B -- No --> C[Treat as current]
    B -- Yes --> D[Inspect running container image]
    D --> E{Docker read succeeded?}
    E -- No --> C
    E -- Yes --> F[Inspect current reference image]
    F --> G{Reference read succeeded?}
    G -- No --> C
    G -- Yes --> H{Running image id equals current id?}
    H -- Yes --> C
    H -- No --> I{Container created from reference?}
    I -- Yes --> J[Mark stale]
    I -- No --> K{Reference layers are not running-image prefix and reference is newer?}
    K -- Yes --> J
    K -- No --> C
Loading

File-Level Changes

Change Details Files
Added stale-image detection based on devpod metadata and Docker image/container inspection.
  • Read container ID, creation image, and declared image reference from workspace results.
  • Batch container inspection and deduplicate reference inspections.
  • Detect direct-reference drift by image ID and derived-image drift by layer prefix plus creation time.
  • Treat missing metadata, Docker failures, and uninspectable images as current.
  • Added unit coverage for direct, derived, prebuilt, shared-reference, and failure scenarios.
rust/devlaunch-core/src/flows/stale_images.rs
rust/devlaunch-core/src/clients/docker.rs
rust/devlaunch-core/src/flows/mod.rs
Integrated stale-workspace results into both list output formats without changing the existing table schema.
  • Run stale checks while rendering table and emit actionable notes to stderr in workspace order.
  • Append staleImage only to stale JSON rows, preserving prior output when no rows are stale.
  • Expose workspace IDs and populate stale metadata after listing rows are built.
  • Added renderer, listing, and end-to-end read-side tests.
rust/devlaunch-core/src/flows/listing.rs
rust/dl/src/commands.rs
rust/dl/src/render.rs
rust/dl/tests/read_side.rs
Documented the new stale-image diagnostics and JSON contract.
  • Documented staleImage as optional and clarified that absence can mean current or unreadable.
  • Added unreleased changelog entry describing detection behavior and conservative failure handling.
docs/agents-using-dl.md
CHANGELOG.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.44%. Comparing base (0bc4713) to head (e851e75).

Additional details and impacted files
Flag Coverage Δ
python 42.98% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
shipped code (rust) 94.65% <100.00%> (+0.04%) ⬆️
harness and tooling (python) 42.98% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sourcery-ai sourcery-ai Bot 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.

Hey - I've found 7 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="rust/devlaunch-core/src/flows/mod.rs" line_range="46" />
<code_context>
 // it is one decision and no socket.
 pub mod session_manager;
 // binary surface — not part of the frozen wf API (#251 §7)
+pub mod stale_images;
+// binary surface — not part of the frozen wf API (#251 §7)
 pub mod workspace_clone;
</code_context>
<issue_to_address>
**Public API checks fail**

When the public-API CI job runs against this change, the new public `stale_images` API adds generated items that are absent from the checked-in snapshots, so the job fails and blocks the PR.

Regenerate and commit the updated public-API snapshots.

Also at `rust/devlaunch-core/src/flows/stale_images.rs:146`, `rust/devlaunch-core/src/flows/listing.rs:1013`.
</issue_to_address>

### Comment 2
<location path="rust/devlaunch-core/src/flows/stale_images.rs" line_range="195" />
<code_context>
+    if created.created_from == created.reference {
+        return true;
+    }
+    !running.layers.starts_with(&current.layers) && current.created > running.created
+}
+
</code_context>
<issue_to_address>
**Changed bases go undetected**

When a derived image’s declared base has changed but its `Created` timestamp is equal to or earlier than the derived image’s timestamp, or the timestamps come from skewed clocks, `is_stale` relies on `current.created > running.created` to detect a changed base, so it misses stale workspaces and can also recommend recreating a workspace when that will not clear the warning.

Determine whether the declared base changed without relying on creation-time ordering.

Also at `rust/devlaunch-core/src/flows/stale_images.rs:262`.
</issue_to_address>

### Comment 3
<location path="rust/devlaunch-core/src/flows/stale_images.rs" line_range="192-193" />
<code_context>
+    if running.id == current.id {
+        return false;
+    }
+    if created.created_from == created.reference {
+        return true;
+    }
+    !running.layers.starts_with(&current.layers) && current.created > running.created
</code_context>
<issue_to_address>
**Rollbacks trigger false warnings**

When a direct image reference is retagged to an image older than the image the container runs, `is_stale` returns `true` for every differing image ID when `created_from == reference`, so `dl --ls` recommends recreating the workspace onto the older image.

Check whether the referenced image is newer before marking a direct-reference workspace stale.
</issue_to_address>

### Comment 4
<location path="rust/devlaunch-core/src/flows/stale_images.rs" line_range="262" />
<code_context>
+                id.to_owned(),
+                Image {
+                    id: id.to_owned(),
+                    created: created.get(..19)?.to_owned(),
+                    layers,
+                },
</code_context>
<issue_to_address>
**Same-second builds look current**

When a derived image and its newer declared reference have different layers but are created within the same second, `images` truncates creation timestamps to whole seconds, so `is_stale` sees equal times and omits the stale workspace.

Preserve sufficient timestamp precision when parsing Docker creation times.

Also at `rust/devlaunch-core/src/flows/stale_images.rs:195`, `rust/devlaunch-core/src/flows/stale_images.rs:263`.
</issue_to_address>

### Comment 5
<location path="rust/devlaunch-core/src/flows/stale_images.rs" line_range="201" />
<code_context>
+/// What devpod's result file says about this workspace's container, if there is
+/// exactly one result file and it names a container and an image.
+fn created(devpod_home: Option<&DevpodHome>, workspace_id: &str) -> Option<Created> {
+    let path = sole_workspace_result(devpod_home, workspace_id)?;
+    let document: serde_json::Value = serde_json::from_slice(&std::fs::read(path).ok()?).ok()?;
+    let details = &document["ContainerDetails"];
</code_context>
<issue_to_address>
**Duplicate IDs appear current**

When the listing contains the same workspace ID in multiple devpod contexts, `created` looks up results by workspace ID alone, and `sole_workspace_result` returns no result for an ID found in multiple contexts. The workspaces are treated as current and receive no stale warning or `staleImage` field.

Look up each workspace result using its context as well as its ID.

Also at `rust/dl/src/commands.rs:348-349`, `rust/dl/src/commands.rs:414`, `rust/devlaunch-core/src/flows/stale_images.rs:146-153`.
</issue_to_address>

### Comment 6
<location path="rust/devlaunch-core/src/flows/stale_images.rs" line_range="207" />
<code_context>
+    let created_from = text(&details["Config"]["Image"])?;
+    Some(Created {
+        container: text(&details["Id"])?,
+        reference: text(&document["MergedConfig"]["image"]).unwrap_or_else(|| created_from.clone()),
+        created_from,
+    })
</code_context>
<issue_to_address>
**Compose workspaces hide stale bases**

When A compose workspace has no `MergedConfig.image` and its service image is pulled to a newer version, `created` falls back to `created_from` when `MergedConfig.image` is absent, so a compose workspace’s devpod-generated image tag becomes both the created image and the reference. Pulling a newer image for the compose service does not move that tag, and `dl --ls` reports no stale workspace.

Resolve the compose service’s declared image reference for the workspace, or explicitly represent the check as unsupported instead of treating the generated tag as the service reference.
</issue_to_address>

### Comment 7
<location path="rust/devlaunch-core/src/flows/stale_images.rs" line_range="162-166" />
<code_context>
+    let running_images = images(runner, unique(running.values()));
+    let references: BTreeMap<String, Image> = unique(created.iter().map(|(_, c)| &c.reference))
+        .into_iter()
+        .filter_map(|reference| {
+            let image = images(runner, [reference.clone()]).into_values().next()?;
+            Some((reference, image))
+        })
+        .collect();
+
+    let by_workspace = created
</code_context>
<issue_to_address>
**Many references delay listings**

When many listed workspaces have distinct references and Docker is slow or unresponsive, the reference loop calls `images` sequentially, and each inspection has a 10-second timeout, so `dl --ls` can wait up to 10 seconds per reference before returning the table and notes.

Inspect the distinct references in a batched or parallel operation.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread rust/devlaunch-core/src/flows/mod.rs
if created.created_from == created.reference {
return true;
}
!running.layers.starts_with(&current.layers) && current.created > running.created

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High · Changed bases go undetected

When a derived image’s declared base has changed but its Created timestamp is equal to or earlier than the derived image’s timestamp, or the timestamps come from skewed clocks, is_stale relies on current.created > running.created to detect a changed base, so it misses stale workspaces and can also recommend recreating a workspace when that will not clear the warning.

Determine whether the declared base changed without relying on creation-time ordering.

Also at rust/devlaunch-core/src/flows/stale_images.rs:262.

Prompt for AI agents
In `rust/devlaunch-core/src/flows/stale_images.rs` at line 195:

**Changed bases go undetected**

When a derived image’s declared base has changed but its `Created` timestamp is equal to or earlier than the derived image’s timestamp, or the timestamps come from skewed clocks, `is_stale` relies on `current.created > running.created` to detect a changed base, so it misses stale workspaces and can also recommend recreating a workspace when that will not clear the warning.

Determine whether the declared base changed without relying on creation-time ordering.

Also at `rust/devlaunch-core/src/flows/stale_images.rs:262`.

Comment on lines +192 to +193
if created.created_from == created.reference {
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Rollbacks trigger false warnings

When a direct image reference is retagged to an image older than the image the container runs, is_stale returns true for every differing image ID when created_from == reference, so dl --ls recommends recreating the workspace onto the older image.

Check whether the referenced image is newer before marking a direct-reference workspace stale.

Prompt for AI agents
In `rust/devlaunch-core/src/flows/stale_images.rs` at lines 192-193:

**Rollbacks trigger false warnings**

When a direct image reference is retagged to an image older than the image the container runs, `is_stale` returns `true` for every differing image ID when `created_from == reference`, so `dl --ls` recommends recreating the workspace onto the older image.

Check whether the referenced image is newer before marking a direct-reference workspace stale.

id.to_owned(),
Image {
id: id.to_owned(),
created: created.get(..19)?.to_owned(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Same-second builds look current

When a derived image and its newer declared reference have different layers but are created within the same second, images truncates creation timestamps to whole seconds, so is_stale sees equal times and omits the stale workspace.

Preserve sufficient timestamp precision when parsing Docker creation times.

Also at rust/devlaunch-core/src/flows/stale_images.rs:195, rust/devlaunch-core/src/flows/stale_images.rs:263.

Prompt for AI agents
In `rust/devlaunch-core/src/flows/stale_images.rs` at line 262:

**Same-second builds look current**

When a derived image and its newer declared reference have different layers but are created within the same second, `images` truncates creation timestamps to whole seconds, so `is_stale` sees equal times and omits the stale workspace.

Preserve sufficient timestamp precision when parsing Docker creation times.

Also at `rust/devlaunch-core/src/flows/stale_images.rs:195`, `rust/devlaunch-core/src/flows/stale_images.rs:263`.

/// What devpod's result file says about this workspace's container, if there is
/// exactly one result file and it names a container and an image.
fn created(devpod_home: Option<&DevpodHome>, workspace_id: &str) -> Option<Created> {
let path = sole_workspace_result(devpod_home, workspace_id)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Duplicate IDs appear current

When the listing contains the same workspace ID in multiple devpod contexts, created looks up results by workspace ID alone, and sole_workspace_result returns no result for an ID found in multiple contexts. The workspaces are treated as current and receive no stale warning or staleImage field.

Look up each workspace result using its context as well as its ID.

Also at rust/dl/src/commands.rs:348-349, rust/dl/src/commands.rs:414, rust/devlaunch-core/src/flows/stale_images.rs:146-153.

Prompt for AI agents
In `rust/devlaunch-core/src/flows/stale_images.rs` at line 201:

**Duplicate IDs appear current**

When the listing contains the same workspace ID in multiple devpod contexts, `created` looks up results by workspace ID alone, and `sole_workspace_result` returns no result for an ID found in multiple contexts. The workspaces are treated as current and receive no stale warning or `staleImage` field.

Look up each workspace result using its context as well as its ID.

Also at `rust/dl/src/commands.rs:348-349`, `rust/dl/src/commands.rs:414`, `rust/devlaunch-core/src/flows/stale_images.rs:146-153`.

let created_from = text(&details["Config"]["Image"])?;
Some(Created {
container: text(&details["Id"])?,
reference: text(&document["MergedConfig"]["image"]).unwrap_or_else(|| created_from.clone()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Compose workspaces hide stale bases

When A compose workspace has no MergedConfig.image and its service image is pulled to a newer version, created falls back to created_from when MergedConfig.image is absent, so a compose workspace’s devpod-generated image tag becomes both the created image and the reference. Pulling a newer image for the compose service does not move that tag, and dl --ls reports no stale workspace.

Resolve the compose service’s declared image reference for the workspace, or explicitly represent the check as unsupported instead of treating the generated tag as the service reference.

Prompt for AI agents
In `rust/devlaunch-core/src/flows/stale_images.rs` at line 207:

**Compose workspaces hide stale bases**

When A compose workspace has no `MergedConfig.image` and its service image is pulled to a newer version, `created` falls back to `created_from` when `MergedConfig.image` is absent, so a compose workspace’s devpod-generated image tag becomes both the created image and the reference. Pulling a newer image for the compose service does not move that tag, and `dl --ls` reports no stale workspace.

Resolve the compose service’s declared image reference for the workspace, or explicitly represent the check as unsupported instead of treating the generated tag as the service reference.

Comment on lines +162 to +166
.filter_map(|reference| {
let image = images(runner, [reference.clone()]).into_values().next()?;
Some((reference, image))
})
.collect();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Many references delay listings

When many listed workspaces have distinct references and Docker is slow or unresponsive, the reference loop calls images sequentially, and each inspection has a 10-second timeout, so dl --ls can wait up to 10 seconds per reference before returning the table and notes.

Inspect the distinct references in a batched or parallel operation.

Prompt for AI agents
In `rust/devlaunch-core/src/flows/stale_images.rs` at lines 162-166:

**Many references delay listings**

When many listed workspaces have distinct references and Docker is slow or unresponsive, the reference loop calls `images` sequentially, and each inspection has a 10-second timeout, so `dl --ls` can wait up to 10 seconds per reference before returning the table and notes.

Inspect the distinct references in a batched or parallel operation.

Copied from the public-api CI job's diff. Only public-api.rest.txt moves;
nothing in the promised api.txt changed.

Claude-Session: https://claude.ai/code/session_01Ro7QyyAtHmrh9oxyyK6h9m

This branch has not been deployed

No deployments
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.

1 participant