Skip to content

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

Closed

Description

@blooop

Question

Document the one build that exists inside the devcontainer, and stop AGENTS.md
implying the host's two-install convention applies in there.

Graduated from the which-build ticket.
The decision is made; this is the writing-down.

The answer: inside, run pixi run dl and pixi run aid. There is exactly one
build in there and it is the working tree — pyproject.toml declares
devlaunch = { path = ".", editable = true } and postCreateCommand runs pixi install, so the container comes up with ./dev.sh's job already done.

The forcing constraint is devpod, not naming. dl shells out to a bare
devpod from PATH (dl.py:843,866) and refuses cleanly when it is absent
(:72). Inside the container devpod exists only at
.pixi/envs/default/bin/devpod. So a devlaunch installed anywhere else would not
merely be redundant — it would have no devpod to drive and would die on the
lookup. Corroborated independently by
the in-container prototype, whose
nested ProxyCommand names exactly that path.

./dev.sh does not work in there, and not for the reason the question
guessed: it exits at its first check because uv is absent from the container
(not in the Dockerfile, not in [tool.pixi.dependencies], not in the claude-code
feature's install). The XDG_DATA_HOME worry was moot in the other direction —
dev.sh hardcodes ${HOME}/.local/share and the container sets exactly that path.

The proposed wording is in #99's resolution comment. Take it from there — but
with the two amendments below.

Amendment 1 — drop the proposed final paragraph

#99's draft ends with a paragraph explaining that the scratch-XDG recipe is
host-only and that XDG_CONFIG_HOME is load-bearing inside the container.
Do not include it. It has been superseded by
the credentials ticket, which
established that the XDG_CONFIG_HOME half guards nothing on the host either
and is being deleted outright by
#115 rather than caveated.

Once #115 lands there is no XDG_CONFIG_HOME in the instruction to warn about, so
the paragraph would document a hazard that no longer exists. Two closed tickets
pointed opposite ways here and the later, stronger finding wins: deleting beats
caveating
, which is #115's own stated reasoning and principle 2's.

Amendment 2 — fix a stale claim while you are in this file

AGENTS.md still says --version reports no provenance. That is already false on
main: PR #85 landed (dev, editable from <tree>). The new section's own example
depends on it being true, so it cannot be left contradicting itself two paragraphs
apart.

The metal obligation this ticket carries

#99 could not run these — it was a reading ticket and other agents held the host's
Docker. Before this closes, paste into the PR:

  • pixi run dl --version from inside the container,
  • one pixi run dl <repo> from inside,
  • command -v uv || echo none, confirming the uv-absent claim on metal.

CLAUDE.md is a symlink to AGENTS.md — edit AGENTS.md.

This is a wayfinder:build ticket: go through /tdd. Most of it is prose and
does not take a unit test; say plainly in the PR which parts are covered and which
rest on the pasted transcripts, rather than inventing a test for a paragraph.

Blocked on #115 — it edits
the same section of the same file. Sequencing them avoids a conflict and makes
amendment 1 unambiguous.

Activity

  1. self-assigned this
    on Aug 8, 2026
  2. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: Claimed and re-read AGENTS.md against origin/main (0.0.16, with the credential-warning rewrite landed). Two facts change the shape of the work before a word is written.

    Amendment 1 is confirmed and is now simply the absence of a paragraph. The scratch recipe on main is one variable, and the section already carries its own explanation of why the second one was dropped — the credential loss it caused, weighed against the repos_dir hazard it would have guarded. There is nothing left to caveat for the container, so the draft's final paragraph does not go in.

    Amendment 2's stale claim is already gone. The provenance work landed its own doc edit in the same commit as the feature, so the section already tells the two builds apart by output — dev, editable from <tree> — as well as by name. Nothing to fix; the new section can lean on it without contradiction. (The stale sentence does still exist in checkouts sitting on older commits, which is likely where the ticket read it.) Reporting this rather than manufacturing an edit.

    Seams I will test at. Most of this is prose and gets no unit test. Three claims the new section makes are load-bearing and checkable from the tree, so they get one small module rather than a paragraph-shaped assertion:

    1. ./dev.sh refuses, cleanly and before it touches anything, when uv is absent — exercised by actually running the script with uv off PATH and asserting the exit status and the message. This is the claim the container advice rests on, and it is behaviour, not prose.
    2. The container really does come up with dev.sh's job done — the editable self-dependency and the create-time install are declared, not assumed.
    3. The provenance string the section quotes is the one the code actually emits, so the example cannot drift from --version.

    Everything else — that pixi run is the only working form, that no released fallback is wanted inside — rests on the metal transcripts the ticket asks for, which I will paste into the PR rather than assert in a test.

  3. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: Built the container and the metal contradicts the draft on its central point. devpod is on the bare PATH inside, at /usr/local/bin/devpod, and it is a working CLI — same version as the pinned one, answering version, provider list and list. It is root-owned and appeared at container-create time: it is the agent binary devpod injects into every workspace it makes. So the sentence "a devlaunch installed anywhere else has no devpod to drive and dies on the lookup" is false as written, and shipping it would have put a claim in AGENTS.md that one command -v refutes.

    The conclusion survives; the reason has to change, and the corrected reason is better than the original because it is about entry path rather than absence. That binary is there because devpod created this container. Start the same devcontainer through VS Code or plain devcontainer up and nothing injects it, so a devlaunch outside the project env finds a devpod on one route in and none on another — working intermittently by how you happened to open the container, which is worse to debug than never working. What pixi run buys is the devpod that is there unconditionally and is the version the tree pins, rather than whatever the host happened to inject.

    Two other things measured while in there. The pinned devpod and the injected one are the same version here only because the host runs the version the tree pins — they are independent, and nothing keeps them equal. And both binaries read the same ~/.devpod, so the provider state is shared; the choice of binary is not an isolation boundary and the section should not imply it is.

    Rewording the second paragraph before the PR. The uv claim and the dev.sh refusal both held exactly as described.

  4. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: PR #125 opened. One slice: the in-container section appended to the two-install advice, plus a small module holding its checkable claims.

    What landed differs from the draft in three ways, and only one of them was planned. Amendment 1 went in as instructed — the scratch-XDG paragraph is absent, since the recipe it caveats no longer has the variable in it. Amendment 2 turned out to be already fixed on main: the provenance work updated that bullet in the same commit that landed the feature, so the section already tells the two builds apart by output. Nothing to edit, so nothing was edited; a check pins it shut instead, because the new section quotes that string as its own example.

    The unplanned one is the devpod paragraph, rewritten because building the container refuted the draft's stated reason. The corrected version turns on how you opened the container rather than on devpod being absent, which is both true and more useful — the failure it predicts is intermittence, not a clean error.

    All three required transcripts are in the PR, plus the ./dev.sh refusal run for real in there and the command -v devpod measurement that forced the rewrite. dl blooop/dotfiles from inside went clone → workspace → ssh → exit 0 in 37s cold, three levels down.

    One thing recorded but not fixed: the nested up wrote its ssh host block into the host's ~/.ssh/config, because the devcontainer bind-mounts ~/.ssh. Known from the in-container prototype; reconfirmed, cleaned up by hand, and left for whoever owns that leak.

  5. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: CI green on #125 across all four Python versions. One self-inflicted red on the way: the new test parsed pyproject.toml with the stdlib TOML reader, which 3.10 does not have — the repo already depends on tomli for exactly that reason and the test now uses it. Caught by pylint rather than by a test failure, since the local environment is new enough to have both.

    Stage complete: branch pushed, PR open, host left as found. Nothing outstanding on this ticket.

  6. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: Reviewed on PR #125 — Approve, six non-blocking comments, nothing routed back to /tdd. The headline call was verified rather than accepted: /usr/local/bin/devpod is present, root-owned and functional in two live devpod-created containers (so #99's draft was indeed false), and it is absent from the image those containers are built from (docker run --rm --entrypoint /bin/sh <image> -c 'command -v devpod' → not in image; nothing in the Dockerfile or the claude-code feature installs it). That closes the half the PR asserted rather than measured — the entry-path argument is sound as written. Amendment 2 confirmed: fda3cb3 is an ancestor of origin/main and main's AGENTS.md carries the fixed bullet; the stale sentence survives at b304cb6, where the primary checkout sits. Amendment 1 confirmed absent. Red-before-green re-run against 85e27b1: exactly 3 of 7 red, as claimed. Comments are two prose nits (a 0.0.16 example three paragraphs below a 0.0.9 one; the -next sentence arguing that names stop distinguishing the builds, which they do not) and four test-quality fixes (a source-spelling assertion, one assertion implied by its predecessor so the host-advice half is unguarded, a warning test that passes on a section saying the opposite, and two unhelpful failure modes). Host left as found — ~/.ssh/config md5 unchanged, devpod list unchanged, no wf116/wf125 artifacts.

  7. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: review verdict approve, six non-blocking comments. Three concern test quality and I am treating them as fix-before-merge — a doc test that passes when the doc says the opposite is worse than no test, because it buys false confidence and still breaks on harmless rewording.

    The reviewer measured the half this PR asserted: it probed the devcontainer image directly and found devpod: NOT IN IMAGE (/usr/local/bin holds only code devcontainer-info git gitk pixi scalar systemctl), with nothing in the Dockerfile or the claude-code feature installing it. So the entry-path argument is sound as written — the binary really is injected post-create and really is absent on the VS Code route. Its method note is fair: under verify on metal, shipping that half as an inference was one docker run --rm short.

    One over-generalisation in the PR body, not in AGENTS.md: "mtime is the moment the container was created" does not hold — devlaunch-main-zovomobo was created 2026-08-07T22:36 with binary mtime Aug 8 08:42. devpod re-injects on later operations, so mtime is last injection. That strengthens the framing rather than weakening it.

  8. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: review round on #125 addressed in 11d9f22 — all six non-blocking comments from the approval.

    Three of the seven doc tests passed on text saying the opposite of what they claimed to check. test_the_provenance_example_matches_what_version_prints pinned the f-string's source spelling instead of running the line that composes it, and its host-advice assertion was implied by its container one — it now feeds the real _install_provenance() a PEP 610 editable record and compares what it emits against what the doc quotes, parametrized so host and container fail independently. test_agents_md_warns_off_dev_sh_inside_the_container now pins the bold prohibition rather than the words ./dev.sh and uv. _container_section() names a missing heading instead of raising ValueError, and the postCreateCommand regex accepts the argv-array form it used to misreport as absent.

    Prose: both --version examples read dl <version> (…) so they cannot drift apart at a release (they already had, 0.0.9 vs 0.0.16), and the -next sentence now says the two builds would be identical rather than that naming stops distinguishing them. The PR body's "mtime is the moment the container was created" is corrected to last injection — devpod re-injects on later operations.

    Every changed assertion was mutated red against text that should fail it (inverted ./dev.sh instruction, changed provenance message, host-only example break, renamed heading) and green where it should stay green (a tree → path rename, an array postCreateCommand); transcripts are in the PR reply. CI green on 11d9f22. Not merged, not closed.

  9. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    breadcrumb: fixes verified, merge-ready (pending the merge train's release bump). CI green at 11d9f22.

    I checked the two tightened assertions directly rather than taking the report's word:

    • test/test_agents_doc.py:91 — re.search(r"\*\*Do not run \./dev.sh`[^*]***", section). The old version asserted only that ./dev.shanduv` appeared, so a section telling you to run it would have passed. It cannot now.
    • :40-47 — _heading_index asserts with the missing heading named, instead of letting str.index raise ValueError. A renamed heading now says so rather than failing every dependent test for an unrelated-looking reason.
    • :147 — postCreateCommand matches string or argv array, so the array form stops being misreported as "declares no postCreateCommand".

    The mutation table's most convincing row is the negative control: renaming tree → path in dl.py now leaves the tests green (3 passed), where the old test broke on it. The test moved from pinning an f-string's spelling to running _install_provenance() against a real PEP 610 record — so it exercises dl.py's composition instead of its source text.

    One environmental gotcha worth carrying: local pylint reports E0611: No name '_install_provenance' because the pixi env's editable install resolves to the primary checkout, which sits at b304cb6 and predates fda3cb3 where that function landed. Not a defect in the PR — a stale-checkout shadow, and the second time today it has misled someone.

  10. blooop commented on Aug 8, 2026

    @blooop
    OwnerAuthor

    Merged in #125 and shipped in v0.0.17.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions