Skip to content

Say which build to run inside the devcontainer - #125

Merged
blooop merged 3 commits into
mainfrom
wayfinder/devlaunch-116
Aug 8, 2026
Merged

blooop merged 3 commits into
mainfrom
wayfinder/devlaunch-116

Conversation

@blooop

@blooop blooop commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Closes #116

AGENTS.md's two-install section reads as universal advice. It is not: an agent
working inside this repo's devcontainer who follows it runs ./dev.sh, gets
Error: uv is not installed, and has nothing to fall back on. This appends one
section saying what is true in there — one build, it is the checkout, and it is
reached as pixi run dl / pixi run aid
— and gives three of its load-bearing
claims a test so they cannot rot quietly.

CLAUDE.md is a symlink to AGENTS.md; only AGENTS.md is edited.

What changed against #99's draft

The wording is #99's, with the ticket's two amendments applied and one paragraph
rewritten because the metal contradicted it
.

Amendment 1 — dropped, as instructed. The draft's final paragraph explained
that the scratch-XDG recipe is host-only because XDG_CONFIG_HOME is
load-bearing inside the container. #119 has since deleted XDG_CONFIG_HOME from
that recipe outright and written down the trade it makes, so there is no longer a
hazard to caveat. Including the paragraph would have documented one that no longer
exists.

Amendment 2 — the stale claim was already gone. The ticket asks for a fix to
AGENTS.md still saying --version reports no provenance. On main it does not:
fda3cb3 ("Tell an editable dl install apart from a released one", PR #85) updated
that bullet in the same commit that landed the feature, and it now reads
dl 0.0.9 (dev, editable from /path/to/checkout) — the two builds told apart by
output as well as by name. The stale sentence survives only in checkouts sitting on
older commits, which is the likeliest source of the report. No edit was needed,
so none was invented. A test pins it shut instead, since the new section's own
example depends on it.

The devpod paragraph is rewritten, and this is the substantive change. #99's
draft said:

inside the container devpod exists only in the project env, so a devlaunch
installed anywhere else has no devpod to drive and dies on the lookup

That is false on metal. command -v devpod inside the container answers
/usr/local/bin/devpod, and it is a working CLI — version, provider list and
list all respond. It is root-owned, mode 711, and its mtime is devpod's last
injection rather than the container's creation — devlaunch-main-zovomobo was
created 2026-08-07T22:36:22Z and carries an Aug 8 08:42 binary, because devpod
re-injects the agent on later operations. It is the agent binary devpod injects
into every workspace it makes, and keeps current for as long as it drives it
,
which is a tighter tie to devpod than a create-time stamp would be. A devlaunch
installed outside the project env would have found it.

The conclusion survives and the corrected reason is stronger, because it is about
the entry path rather than about absence. That binary is there because devpod
created this container
. Open the same devcontainer through VS Code or a plain
devcontainer up and nothing injects it — so a devlaunch outside the project env
drives a devpod on one route in and none on the other, which is worse to diagnose
than never working at all. What pixi run buys is a devpod that is present on both
routes and is the version the tree is pinned against, rather than whatever the host
happened to inject. Two further measurements went in as a short second paragraph:
the pinned devpod and the injected one match in version here only because this
host runs the version the tree pins
— nothing keeps them equal — and both read
the same ~/.devpod, so choosing between them is not an isolation boundary and the
section should not let a reader think it is.

Test coverage, honestly

Most of this is prose and gets no test; inventing one for a paragraph would be
theatre, and #116 says so. What test/test_agents_doc.py covers is the handful of
statements a reader would act on that the repository could silently invalidate:

Covered by a test How
./dev.sh refuses, before touching a path, when uv is absent runs the script for real with uv off PATH against a throwaway HOME, and asserts exit 1, the message, and that neither ~/.local/share/devlaunch-dev nor ~/.local/bin was created
the container arrives with ./dev.sh's job done pyproject.toml declares the editable self-dependency and devcontainer.json's postCreateCommand runs pixi install
the quoted dev, editable from … is what the code emits _install_provenance() is run against the PEP 610 record pip writes for an editable install, and the line it composes is compared with the one the document quotes — host and container checked separately
./dev.sh is forbidden inside, not merely mentioned the bold prohibition itself is pinned, so a section telling the reader to run it fails
amendment 2 stays fixed the retired phrasings are absent from the file
the section is addressed to pixi run pixi run dl and pixi run aid both named under the new heading
editing one file serves both names CLAUDE.md is still a symlink to AGENTS.md

Not covered by a test, and resting on the transcripts below: that pixi run dl
is the invocation that works inside, that dl <repo> succeeds from in there, and
that no released fallback is wanted. Those are claims about a built container, not
about this tree.

Full suite green locally: 988 passed, 22 deselected (pixi run test) as first
pushed; the review round below parametrizes one of these tests in two, so the count
is one higher from 11d9f22 on. pixi run style clean — ruff format, ruff check,
ty, pylint 10.00/10.

The metal obligation

#116 carries #99's undischarged test obligation. All three checks were run, plus
two more that were worth having.

A devcontainer was built for this. main's .devcontainer/devcontainer.json still
has the dind feature commented out, so for the dl <repo> check the container was
built from origin/feat/devcontainer-dind's version of that one file — the diff
#109 is landing. That file is not touched by this PR; it was a local swap for
the measurement only. The --version, uv and dev.sh checks need no daemon and
would have been identical on main's file.

Everything ran under a scratch DEVPOD_HOME/DEVPOD_SSH_CONFIG, from a copy of
this branch named wf116-doc, and was removed afterwards.

1. pixi run dl --version, from inside

$ whoami; pwd
vscode
/workspaces/wf116-doc
$ pixi run dl --version
dl 0.0.16 (dev, editable from /workspaces/wf116-doc)

Exactly the string the new section quotes.

2. command -v uv, from inside

$ command -v uv || echo none
none

The uv-absent claim holds on metal. And the consequence, run rather than
asserted:

$ ./dev.sh
Installing DevLaunch in development mode using uv...
  Venv location: /home/vscode/.local/share/devlaunch-dev
  Project path: /workspaces/wf116-doc

Error: uv is not installed.
Install it with: curl -LsSf https://astral.sh/uv/install.sh | sh
exit=1

Clean and loud, nothing half-installed — the refusal the section calls correct.

3. pixi run dl <repo>, from inside

Needs a daemon, and had one (docker version inside reports server 29.7.1-1
against the host's 29.5.2). The docker provider was added inside first; dl does
not add it. blooop/dotfiles was used rather than this repo, so the check measures
dl rather than a nested pixi build.

$ pixi run dl blooop/dotfiles -- 'echo INSIDE-INSIDE: $(hostname) $(pwd); ls | head -5'
info Cloning repository git@github.com:blooop/dotfiles.git to /home/vscode/.cache/devlaunch/repos/blooop/dotfiles/.bare
info Successfully cloned blooop/dotfiles
info Creating workspace clone at /home/vscode/.cache/devlaunch/repos/blooop/dotfiles/dotfiles-main-dizipeja
info creating devcontainer
info image not found, pulling image: image=mcr.microsoft.com/devcontainers/base:ubuntu
info devlaunch: installing pixi
info devlaunch: installing gh
info devlaunch: installing claude
info SSH command: devpod ssh dotfiles-main-dizipeja --command bash -lc '...' --send-env GH_TOKEN
INSIDE-INSIDE: bdda457dd3ee /workspaces/dotfiles-main-dizipeja
CLAUDE.md
README.md
claude-settings.json
container
dot_config

real    0m36.715s

Clone → id → workspace → ssh → exit 0, cold, three levels down.

4. The measurement that changed the wording

$ command -v dl || echo "dl: none"
dl: none
$ command -v devpod
/usr/local/bin/devpod
$ /usr/local/bin/devpod version
v0.26.1
$ .pixi/envs/default/bin/devpod version
v0.26.1
$ ls -l /usr/local/bin/devpod
-rwx--x--x 1 root root 117940408 Aug  8 11:38 /usr/local/bin/devpod   # = container create time
$ /usr/local/bin/devpod provider list   # answers; empty table
$ pixi run bash -c 'command -v devpod'
/workspaces/wf116-doc/.pixi/envs/default/bin/devpod

5. An isolation leak worth recording — already known from #96, reconfirmed
here. The devcontainer bind-mounts ${localEnv:HOME}/.ssh, so the nested
devpod up the check above performed wrote its host block into the host's
~/.ssh/config, with a ProxyCommand naming a path that exists only inside the
container:

ProxyCommand "/workspaces/wf116-doc/.pixi/envs/default/bin/devpod" ssh --stdio … dotfiles-main-dizipeja

Removed by hand afterwards. Not this PR's to fix; noted so the next prototype
expects it.

Host hygiene

  • Scratch DEVPOD_HOME and DEVPOD_SSH_CONFIG; the host's real workspace list was
    never in scope and devpod list on the host is unchanged.
  • ~/.ssh/config before the run: 8a4da6f37505c9cfb325ab92b8ce5497 (46 lines).
    After cleanup: 0fcb9aeff46aad62551d9d688f2021f7 (56 lines). The delta is not
    from this run — it is one block a concurrently running agent added for
    bencher-test1-pipagito, whose container is still up. The only block this run
    added (dotfiles-main-dizipeja, from the nested up) was removed by a targeted
    edit, verified by diff against a snapshot: exactly ten lines, all of them that
    block.
  • devpod delete was deliberately not used. With other agents working on this
    host, a delete that rewrites the shared ~/.ssh/config risks their blocks;
    removing the container, its two volumes and its image directly reclaims strictly
    more (devpod delete does not reclaim the dind volume) and touches nothing
    shared.
  • Created and removed: container wf116-doc, image wf116-doc-40365:devpod-…,
    volumes wf116-doc-pixi and dind-var-lib-docker-default-wf-41361, and the
    scratch devpod home. A second dind volume (…-wf-9ef1b, created 09:45, hours
    before this run) was left alone.

Review round: the three tests that passed on the wrong text

11d9f22 answers the six non-blocking comments on the approval. The theme is that
a doc test which passes when the doc says the opposite is worse than no test — it
buys confidence and still breaks on a harmless reword. Three of the seven were in
that shape.

  • test_the_provenance_example_matches_what_version_prints pinned the
    f-string's source spelling, so a treepath rename went red without ever
    running the line that composes the string, and its second assertion was a
    substring of its first, so the host advice it named was unguarded. It now feeds
    the real _install_provenance() the PEP 610 record pip writes for an editable
    install and compares what it emits against what the document quotes,
    parametrized over the host bullet and the container section so each stands alone.
  • test_agents_md_warns_off_dev_sh_inside_the_container asserted only that
    `./dev.sh` and uv appeared, so a section telling the reader to run it
    passed. It now pins the bold prohibition and the reason.
  • _container_section() raised ValueError: substring not found on a renamed
    heading, so every test using it lied about why it failed; it now names the
    missing heading. The postCreateCommand regex reported a valid argv-array
    form as "declares no postCreateCommand"; both forms are accepted.
  • Prose. Both --version examples now read dl <version> (…) rather than
    drifting apart at each release, which 0.0.9 and 0.0.16 already had. The
    -next sentence no longer claims naming stops distinguishing the builds — the
    document itself rests that convention on names that still differ; the point is
    that the two builds would be identical.
  • This body. The mtime over-generalization above is corrected: it is the last
    injection, not create time.

Each changed assertion was checked against text that should fail it — an inverted
./dev.sh instruction, a changed provenance message, a host-only example break, a
renamed heading, an argv-array postCreateCommand — and each failed for the stated
reason, with git diff clean of the mutations afterwards.

Proposed CHANGELOG entry

Not applied here — four PRs are in flight and four version bumps would collide.
For whichever release picks this up:

Changed

  • AGENTS.md says which build to run inside this repo's devcontainer, instead of
    leaving the host's two-install advice to be followed in a place it does not work.
    There is one build in there and it is the checkout — the devcontainer installs it
    editable at create time — so the answer is pixi run dl and pixi run aid, and
    ./dev.sh should not be run in there at all: it exits at its first check because
    the container has no uv, which is the right outcome rather than a gap to fill.
    The reason pixi run matters is which devpod dl finds. devpod injects its own
    agent binary onto the bare PATH of every container it creates, so a devlaunch
    installed outside the project environment finds a devpod when the container was
    opened by devpod and none when it was opened by VS Code — intermittent by how you
    got in. The project environment's devpod is present either way and is the version
    the tree is pinned against. Nothing about dl on a host changes, and the two
    builds the section already described are unaffected.

🤖 Generated with Claude Code

Summary by Sourcery

Clarify how agents should run DevLaunch inside this repo’s devcontainer and add tests that pin the key behavioural and documentation assumptions in place.

Documentation:

  • Add an in-container section to AGENTS.md explaining that the devcontainer already installs the checkout editable, that agents should use pixi run dl / pixi run aid, and why ./dev.sh and a separate host-installed devlaunch are inappropriate inside the container.

Tests:

  • Introduce test/test_agents_doc.py to verify CLAUDE.md remains a symlink to AGENTS.md, the new devcontainer guidance references pixi run dl/pixi run aid and warns against ./dev.sh, dev.sh fails cleanly when uv is absent, the devcontainer performs an editable self-install at create time, the documented provenance string matches what dl --version emits, and stale claims about --version hiding provenance do not reappear.

@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.

Sorry @blooop, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds explicit devcontainer-specific guidance to AGENTS.md about which devlaunch build to run inside this repo’s devcontainer (use the editable checkout via pixi run dl / pixi run aid, do not run ./dev.sh), and introduces tests that pin key doc claims to the actual project configuration and dev.sh behavior so they cannot silently drift.

File-Level Changes

Change Details Files
Document how to run devlaunch inside the devcontainer and why pixi run is required there instead of ./dev.sh or a host-installed build.
  • Add a new in-container section under the heading "Inside the devcontainer: one build, and it is pixi run dl" describing the devcontainer’s editable install of the checkout.
  • Explain that pixi run dl and pixi run aid are the correct entry points inside the devcontainer, with an example --version output showing editable provenance.
  • Clarify why pixi run is required: dl shells out to devpod from PATH, and the devcontainer may expose different devpod binaries depending on how it was opened; the project env’s devpod is stable and version-pinned.
  • Explain that both devpod binaries share the same ~/.devpod, so choosing between them is about consistency rather than isolation.
  • Warn explicitly against running ./dev.sh inside the devcontainer, explaining the lack of uv, the correct refusal behavior, and why a second editable install or a released build would be misleading and unnecessary.
AGENTS.md
Add tests that lock the new AGENTS.md devcontainer guidance and related behaviors to the repository’s actual configuration and scripts.
  • Introduce test/test_agents_doc.py with unit and integration tests around AGENTS.md, dev.sh, and project config.
  • Assert that CLAUDE.md is a symlink to AGENTS.md so editing AGENTS.md updates both agent docs.
  • Extract the devcontainer-specific section by heading and assert it mentions pixi run dl, pixi run aid, and warns about ./dev.sh and uv.
  • Integration-test dev.sh with uv removed from PATH and a scratch HOME, verifying it exits with status 1, emits the expected "uv is not installed" message, and does not create devlaunch directories or binaries.
  • Verify pyproject.toml declares devlaunch = { path = ".", editable = true } and devcontainer.json’s postCreateCommand includes pixi install, ensuring the devcontainer installs the checkout editable at create time.
  • Check that the provenance string quoted in AGENTS.md matches the f-string in devlaunch/dl.py and appears in both the host and in-container documentation.
  • Ensure AGENTS.md no longer contains stale claims that --version hides provenance or that only the name distinguishes builds.
test/test_agents_doc.py
AGENTS.md
CLAUDE.md
pyproject.toml
.devcontainer/devcontainer.json
devlaunch/dl.py

Assessment against linked issues

Issue Objective Addressed Explanation
#116 Update AGENTS.md to clearly document devcontainer behavior: there is a single editable build (the checkout), it is invoked via pixi run dl / pixi run aid, ./dev.sh should not be run inside, and the reasoning around devpod is correct and does not include the superseded XDG_CONFIG_HOME paragraph.
#116 Ensure AGENTS.md no longer claims that dl --version reports no provenance and that the documentation’s provenance example matches what the code actually emits, ideally guarded by tests.
#116 Fulfill the metal / wayfinder obligations: include transcripts of pixi run dl --version, pixi run dl <repo>, and command -v uv from inside the devcontainer, and explicitly state which parts of the new guidance are covered by tests versus resting on those transcripts.

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

AGENTS.md's two-install advice reads as universal, and an agent working
inside this repo's devcontainer who follows it runs ./dev.sh and gets
'Error: uv is not installed', with nothing to fall back on. Say what is
true in there instead: one build, the checkout, reached as pixi run dl
and pixi run aid.

The reason is which devpod dl finds, rather than naming. devpod injects
its own agent binary onto the bare PATH of every container it creates,
so a devlaunch outside the project env has a devpod to drive when the
container was opened by devpod and none when it was opened by VS Code --
working or not by how you got in. The project env's devpod is there on
both routes and is the version the tree is pinned against.

Three of the section's claims are load-bearing and can go stale without
anyone noticing, so they get checks: that dev.sh really does refuse
before touching a path when uv is absent, that the container really does
arrive with the checkout installed editable, and that the provenance
string the example quotes is the one --version emits.
@blooop
blooop force-pushed the wayfinder/devlaunch-116 branch from c3becd3 to 97962c8 Compare August 8, 2026 11:49
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.68%. Comparing base (85e27b1) to head (0286d7f).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #125   +/-   ##
=======================================
  Coverage   90.68%   90.68%           
=======================================
  Files          18       18           
  Lines        1987     1987           
=======================================
  Hits         1802     1802           
  Misses        185      185           

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@blooop blooop left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This was generated by AI during review.

Reviewed in fresh context, against merge-base 85e27b1. Diff is two files (AGENTS.md, test/test_agents_doc.py); .devcontainer/ is untouched. All 9 checks green.

This PR is mostly prose, so it was reviewed as prose: are the claims true, and will a reader act correctly on them? Every load-bearing claim was re-measured independently rather than taken from the PR body.

Standards

The rewritten devpod argument holds. Both halves verified on metal, independently.

Presence. Two live devpod-created containers on this host:

$ docker exec <devlaunch-main-zovomobo> sh -c 'command -v devpod; ls -l /usr/local/bin/devpod; devpod version'
/usr/local/bin/devpod
-rwx--x--x 1 root root 117940408 /usr/local/bin/devpod
v0.26.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Root-owned, mode 711, on the bare PATH, a working CLI. #99's draft ("no devpod to drive and dies on the lookup") is indeed false, and the refutation was right to be made.

Absence — the half the PR asserted rather than measured. Probed the image the devcontainer is built from:

$ docker run --rm --entrypoint /bin/sh <devlaunch devcontainer image> -c 'ls /usr/local/bin; command -v devpod'
code  devcontainer-info  git  git-*  gitk  pixi  scalar  systemctl
devpod: NOT IN IMAGE

Nothing in .devcontainer/Dockerfile (pixi only) or the claude-code feature (pixi + claude) installs devpod. So the binary is injected post-create by devpod, and an entry route that is not devpod — VS Code or devcontainer up, same image — yields nothing at that path. The entry-path argument is sound as written, and the corrected reasoning is better than what it replaced.

Two notes on method rather than conclusion. First, the PR measured only the presence half; the VS Code half — the half the whole argument turns on — shipped as an inference. Under this map's verify on metal that is the shape of the failure the map exists to prevent. It happens to be true; it was one docker run --rm away. Second, a PR-body over-generalization: "its mtime is the moment the container was created" is not a general property. devlaunch-main-zovomobo was created 2026-08-07T22:36:22Z and its binary's mtime is Aug 8 08:42 — devpod re-injects the agent on later operations, so the mtime is last injection, not create time. This does not appear in AGENTS.md and if anything strengthens the "present because devpod is driving this container" framing.

Other claims in the new section, each checked:

Claim Result
dl shells out to a bare devpod from PATH cmd = ["devpod"] + args, dl.py:843 and :866
uv is not installed in the container command -v uv → none in a live devcontainer; uv appears nowhere in pyproject.toml
pixi run aid works inside aid = "devlaunch.aid:main" in [project.scripts]; aid present in .pixi/envs/default/bin/
the pinned devpod is the version the tree is written against devpod = ">=0.26.1,<0.27"
"devpod 0.8 asks for a pty on ssh --command and 0.26 never does" ✅ matches pyproject.toml's own pin comment and CHANGELOG 0.0.13
the project env's devpod is present on both entry routes postCreateCommand runs on both

Finding 1 — the quoted version already contradicts its neighbour (comment). The new section quotes dl 0.0.16 (dev, editable from /workspaces/<checkout>) three paragraphs below the host bullet, which still quotes dl 0.0.9 (dev, editable from /path/to/checkout); README.md:~326 also says 0.0.9. Two different versions in two examples of the same string inside one section is exactly the rot this PR's tests exist to prevent, and test_the_provenance_example_matches_what_version_prints pins the phrase but not the version, so nothing catches it. Cheapest fix: drop the number (dl <version> (dev, editable from /workspaces/<checkout>)).

Finding 2 — the -next sentence argues against something the document does not claim (comment). "printing the same provenance string as pixi run dl, so the one property the two-install convention rests on — being able to tell the builds apart — would not exist." Two paragraphs up, the convention rests on "names that cannot collide", and dl-next vs pixi run dl are still different names. The true point is that the two would be the same build, so there is nothing to tell apart and nothing to gain — not that naming stopped working. Inherited verbatim from #99's draft; one sentence of repair.

The seven tests

Red-before-green verified rather than accepted: the module was run against a detached worktree at 85e27b1exactly 3 failed, 4 passed, matching the claim. The other four pin pre-existing facts the new prose now depends on, which is a legitimate reason to write them.

test_dev_sh_refuses_before_touching_anything_when_uv_is_absent is the strongest of the seven and the model for the rest: real subprocess, real script, asserting exit status, message, and absence of side effects. It is the one claim in the section that a code change could actually invalidate. Keep as is.

Three tests pin less than their names promise:

Finding 3 — test_the_provenance_example_matches_what_version_prints has one brittle assertion and one inert one (comment).

  • assert 'f"dev, editable from {tree}"' in source pins the source spelling, not the emitted string. Renaming the local treepath — a no-op refactor — turns it red while the documented output is unchanged; conversely it never exercises dl.py:133, where the string is actually composed. A behavioral assertion on _install_provenance() costs the same line.
  • assert "dev, editable from" in _container_section() followed by assert "dev, editable from" in _agents_md(): the section is a substring of the document, so the second can never fail independently. The docstring says it checks "both the section and the host advice"; it does not check the host advice at all — and that is precisely the assertion that would guard amendment 2's fix in the host bullet.

Finding 4 — test_agents_md_warns_off_dev_sh_inside_the_container cannot tell a warning from an instruction (comment). It asserts "./dev.sh" in section and "uv" in section. A section that said run ./dev.sh passes. Pin the imperative the reader must obey: **Do not run ./dev.sh**.

Finding 5 — _container_section() fails unhelpfully on the failure mode it will actually hit (nit). When the heading changes, text.index(...) raises ValueError: substring not found and three tests die on a stack trace pointing at .index (observed at 85e27b1). One assert CONTAINER_HEADING in text, "…" makes it self-explanatory.

Finding 6 — the postCreateCommand regex assumes the string form (nit). devcontainer.json also permits an array or object; adding a second step as an array makes the test report "devcontainer.json declares no postCreateCommand" — a false diagnosis of a still-correct config.

Also minor: for tool in ("dirname",): is a one-element loop (speculative generality); and the hardcoded .local/share/devlaunch-dev / .local/bin duplicate dev.sh's paths, so if dev.sh moved them those two assertions go vacuous — the exit-code and message assertions still carry the test.

test_claude_md_is_the_same_document_as_agents_md (cheap, exact, guards a real hazard), test_agents_md_sends_in_container_work_through_pixi_run (thin but pins the one actionable instruction, correctly scoped to the section) and test_agents_md_does_not_claim_version_hides_provenance (a negative pin that can only catch re-introduction of two exact phrasings — which is what the PR table honestly says it is) all earn their place.

tomllibtomli fix confirmed correct: tomllib is stdlib from 3.11 and ci (py310) is in the matrix; tomli>=2.0.0 is already a runtime dependency (devlaunch/worktree/config.py:8), so no new dependency was taken. grep -rn tomllib over the branch → zero hits.

Placement (test/test_*.py top level) and markers (unit / integration, both registered, both run in CI) match repo convention. No Fowler smell worth acting on. Ruff/pylint/ty already gate the rest.

Spec

Spec is #116, which carries #99's undischarged obligations.

The answer: inside, run pixi run dl and pixi run aid.

✅ Delivered, under its own heading, with the premise the ticket states — devlaunch = { path = ".", editable = true } plus postCreateCommand running pixi install — and both halves now tested.

Amendment 1 — drop the proposed final paragraph.Do not include it.

Confirmed absent. No XDG string anywhere in the added text; #119 landed the deletion the amendment anticipated, so there is no hazard left to caveat.

Amendment 2AGENTS.md still says --version reports no provenance.

The author is right, and the spec's premise is wrong. Verified independently: fda3cb3 ("Tell an editable dl install apart from a released one", 2026-08-07 23:12) is an ancestor of origin/main, and origin/main:AGENTS.md reads "dl-next --version names the tree it resolves to — dl 0.0.9 (dev, editable from /path/to/checkout) … told apart by output as well as by name." The stale sentence survives only at older commits — including b304cb6, an ancestor of main and the commit the primary checkout is parked on, which still reads "the package version, not its provenance … the name is the only thing distinguishing the two." Reporting the finding instead of inventing an edit was the correct call, and pinning it with a test was the right substitute.

Before this closes, paste into the PR: pixi run dl --version, one pixi run dl <repo>, command -v uv || echo none.

✅ All three present. Two re-derived here: uv absent from a live devcontainer and from pyproject.toml; f"dev, editable from {tree}" at dl.py:122. The 36.7s dl blooop/dotfiles run was not reproduced — that means building a dind container on a shared host — and is accepted on the transcript; its shape is consistent with #96's measured 25.2s cold / 7.9s warm.

The devcontainer.json separation held. git diff 85e27b1...HEAD --name-status returns only AGENTS.md and test/test_agents_doc.py; .devcontainer/ shows an empty diff, and main's file still has the dind feature commented out. Using origin/feat/devcontainer-dind's version locally for the daemon-requiring measurement leaked nothing into the diff.

say plainly in the PR which parts are covered and which rest on the pasted transcripts

✅ The coverage table plus the explicit "Not covered by a test, and resting on the transcripts below" paragraph does this better than asked.

Proposed CHANGELOG entry — accurate, and in the repo's voice. Every checkable claim in it holds: the injection is real (measured above), the uv consequence is real, "the version the tree is pinned against" matches devpod = ">=0.26.1,<0.27". Voice matches 0.0.16 and 0.0.14: one long ### Changed bullet, mechanism before consequence, and the closing "Nothing about dl on a host changes, and the two builds the section already described are unaffected" is the house formula. Scoping it as a fragment for the merge train to place, without a release lead paragraph, is correct — the lead is per-release and not this PR's to write. No defect; the absent version bump is deliberate and not counted against it.

Host hygiene — the devpod delete call was right, and I would have made it. Two independent supports in the repo's own record: #101 measured that devpod's DeleteDevContainer is a plain docker rm without -v and DeleteVolume has zero callers, so delete demonstrably leaks the volume — removing container + volumes + image by hand does reclaim strictly more, exactly as claimed; and delete rewrites the shared ~/.ssh/config, which #112 is open about. Verified the outcome: no wf116-* container, image or volume remains, and the only dind volume left is dind-var-lib-docker-default-wf-9ef1b, created 2026-08-08T09:45:59+01:00 — pre-dating the run, as stated.

Host state, before and after this review: ~/.ssh/config md5 0fcb9aeff46aad62551d9d688f2021f7, unchanged; no dotfiles-main-dizipeja or wf116 block present, so the targeted ten-line removal was complete. devpod list shows the same six workspaces before and after. The author's recorded 8a4da6f3…0fcb9aef… (46 → 56 lines) lands on the value still present, and their attribution of the +10 lines to a concurrent agent checks out: lines 1–10 are the bencher-test1-pipagito.devpod block, whose workspace is live and is not theirs.

Verdict

Approve — with six comments, none blocking.

The headline call was correct twice over: the draft's stated reason really was false, and the replacement really is sound — I verified the half the PR did not. Amendment 2's claim about main is right, and declining to invent an edit was better practice than making one. Nothing false ships.

Non-blocking, worth fixing before or after merge:

  1. The section quotes 0.0.16 three paragraphs below a bullet quoting 0.0.9 — drop the number.
  2. The -next sentence argues that names stop distinguishing the builds; they do not. The point is that the builds would be identical.
  3. test_the_provenance_example_matches_what_version_prints: pins an f-string's source spelling, and its second assertion is implied by its first, so the host-advice half is unguarded.
  4. test_agents_md_warns_off_dev_sh_inside_the_container passes on a section that says the opposite of what it means.
  5. _container_section() should assert its heading rather than raise ValueError.
  6. The postCreateCommand regex misreports the array form as "declares no postCreateCommand".

Nothing here belongs to /tdd; 1–2 are one sentence each and 3–6 are four lines of test.

Three of the seven tests passed on text that says the opposite of what
they claim to check, which is worse than not having them: they buy
confidence while still breaking on a reword.

The provenance test pinned an f-string's *source spelling*, so renaming
a local turned it red without ever running the line that composes the
string, and its second assertion was a substring of its first, leaving
the host advice it named unguarded. It now feeds the real
`_install_provenance()` the PEP 610 record pip writes for an editable
install and compares the emitted line against the one the document
quotes, parametrized over host and container so each is checked alone.

The `./dev.sh` test asserted only that `./dev.sh` and `uv` appeared, so
a section telling the reader to *run* it would have passed. It now pins
the prohibition itself.

`_container_section()` raised `ValueError: substring not found` on a
renamed heading, so every test using it lied about why it failed; it now
says which heading is missing. The `postCreateCommand` regex reported a
valid array form as "declares no postCreateCommand".

Two prose fixes: both `--version` examples say `dl <version>` rather
than drifting apart at each release, as `0.0.9` and `0.0.16` already
had; and the `-next` sentence no longer claims naming stops
distinguishing the builds when the point is that the builds would be
identical.
@blooop

blooop commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

All six addressed in 11d9f22. CI green on that SHA (ci py310–py313, prek, codecov, GitGuardian).

The common thread in 3–6 is the one the review named: a doc test that passes when the doc says the opposite is worse than no test. So each changed assertion was run against text that should fail it, rather than just re-read.


1. The quoted version (0.0.16 three paragraphs under 0.0.9). Both examples now read `dl <version> (dev, editable from …)`. Dropping the number rather than syncing it, as suggested — a pinned version is unpinned rot by construction, and the test below now composes the expected line from _install_provenance(), so the string is guarded and the version is deliberately not part of it.

README.md:326 is left alone. It is a shell transcript ($ dl --version / dl 0.0.9), not an example embedded in prose, and its neighbour is a conda badge that rots the same way; changing one line of it would not make that document version-stable and is outside this PR.

2. The -next sentence. Reworded to the point the review identified — the two would be the same build, not two builds that stopped being distinguishable:

A -next build would be a second editable install of the same tree, printing the same provenance string as pixi run dl — the same build under a second name. On the host the convention is worth its keep because the two names stand for genuinely different builds, released and working tree; in here there would be nothing to tell apart, and nothing gained by telling it.

3. test_the_provenance_example_matches_what_version_prints. Both halves of the finding were right, and both are fixed by the same change. The test now runs the real function against the PEP 610 record pip writes for pip install -e, and compares what it composes with what the document quotes:

def _provenance_for(tree: str) -> str:
    direct_url = json.dumps({"url": Path(tree).as_uri(), "dir_info": {"editable": True}})
    with mock.patch("devlaunch.dl.distribution", return_value=_FakeDist(direct_url)):
        provenance = _install_provenance()
    ...

@pytest.mark.parametrize("tree,section", DOCUMENTED_PROVENANCE_EXAMPLES)
def test_the_provenance_example_matches_what_version_prints(tree, section):
    expected = f"`dl <version> ({_provenance_for(tree)})`"
    assert expected in section(), f"AGENTS.md no longer quotes {expected} for {tree}"

Parametrized over (/path/to/checkout, _host_section) and (/workspaces/<checkout>, _container_section) — two disjoint slices of the file, so the host half is now a test that can go red on its own rather than an assertion implied by its neighbour. _host_section() is new and stops at the container heading.

Three mutations, since this test had to stop doing one thing and start doing another:

The no-op refactor the review predicted would break it — must stay green:

MUTATED dl.py: 'tree = url2pathname(...) ... f"dev, editable from {tree}"' -> '... {path}'
3 passed, 5 deselected

The emitted message changed — must go red, both examples:

MUTATED dl.py: 'return f"dev, editable from {tree}"' -> 'return f"dev, editable at {tree}"'
E   AssertionError: AGENTS.md no longer quotes `dl <version> (dev, editable at /workspaces/<checkout>)` for /workspaces/<checkout>
FAILED ...[/path/to/checkout-_host_section]
FAILED ...[/workspaces/<checkout>-_container_section]
2 failed, 1 passed

Only the host bullet broken — proves the independence the old version lacked:

MUTATED AGENTS.md: '`dl <version> (dev, editable from /path/to/checkout)`' -> '`dl <version> (dev install)`'
E   AssertionError: AGENTS.md no longer quotes `dl <version> (dev, editable from /path/to/checkout)` for /path/to/checkout
FAILED ...[/path/to/checkout-_host_section]
1 failed, 2 passed

4. test_agents_md_warns_off_dev_sh_inside_the_container. Now pins the prohibition, not the vocabulary:

assert re.search(r"\*\*Do not run `\./dev\.sh`[^*]*\*\*", section), (
    "the in-container section no longer forbids `./dev.sh` in bold"
)
assert "`uv` is not installed" in section

The uv assertion is tightened from the bare word to the reason clause, which pairs with the message the integration test asserts dev.sh actually prints. Mutated to exactly the text the review said would slip through:

MUTATED AGENTS.md: '**Do not run `./dev.sh` in the container.**' -> '**Run `./dev.sh` in the container.**'
E   AssertionError: the in-container section no longer forbids `./dev.sh` in bold
1 failed, 7 deselected

5. _container_section() raising ValueError. Replaced str.index with a _heading_index() helper shared by both section accessors:

MUTATED AGENTS.md: heading -> '### Inside the devcontainer'
E   AssertionError: AGENTS.md has no heading '### Inside the devcontainer: one build, and it is `pixi run dl`'; the section was renamed or removed
2 failed, 6 deselected

Two tests, one message each, pointing at the cause instead of at .index.

6. The postCreateCommand regex. Now (?P<value>"[^"]*"|\[[^]]*\]), with a comment saying why the file is matched rather than parsed (it is JSONC — the commented-out features). The array form the review named:

MUTATED devcontainer.json: '"postCreateCommand": "sudo chown vscode .pixi && pixi install"'
                        -> '"postCreateCommand": ["sh", "-c", "sudo chown vscode .pixi && pixi install"]'
1 passed, 7 deselected

and a genuine absence still diagnoses correctly: AssertionError: devcontainer.json declares no postCreateCommand as a string or an array. The object form is not accepted; it is a per-lifecycle map whose values are themselves strings or arrays, and matching it with a regex would be guessing at nesting. If it is ever used here, this fails loudly with a message that names what it understands.

Also taken: for tool in ("dirname",) is now a straight-line shutil.which("dirname") — the review's speculative-generality note, and it removes a loop.

Deliberately not changed: the hardcoded .local/share/devlaunch-dev / .local/bin in the dev.sh test. They duplicate dev.sh's paths and would go vacuous if it moved them, but reading the paths out of the script to assert against the script is circular, and as the review says the exit-code and message assertions carry that test either way.

PR body: the mtime over-generalization is corrected there — last injection, not create time, with devlaunch-main-zovomobo's 2026-08-07T22:36:22Z / Aug 8 08:42 as the counterexample. It does tighten the argument: the binary tracks devpod for as long as devpod is driving the container. The entry-path argument is untouched — the review's docker run --rm probe settles the half the PR had inferred, and the section already says what that probe confirms. The proposed CHANGELOG entry is unchanged; nothing in this round alters what it claims.

All mutations were applied to a backed-up copy and restored; git diff shows only AGENTS.md and test/test_agents_doc.py, with devlaunch/ and .devcontainer/ clean.

@blooop
blooop merged commit d7462ce into main Aug 8, 2026
9 checks passed
@blooop
blooop deleted the wayfinder/devlaunch-116 branch August 8, 2026 12:51
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.

Document that pixi run dl is the only build inside the devcontainer

1 participant