Skip to content

Name requests in the Python ownership list, and test that the list stays complete - #105

Merged
max-sixty merged 4 commits into
mainfrom
docs/name-the-requests-owner
Aug 29, 2026
Merged

Name requests in the Python ownership list, and test that the list stays complete#105
max-sixty merged 4 commits into
mainfrom
docs/name-the-requests-owner

Conversation

@leaf-agent

@leaf-agent leaf-agent commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Add requests beside decisions in the Python ownership map. A derived test now compares the shipped Python modules with the instruction scopes that route them, while keeping package members separate from same-named top-level owners.

Testing: uv run pytest tests/test_interact_layer.py -q -n0 (133 passed). Removing the requests entry makes the new test fail on requests.py.

This was written by Codex on behalf of max-sixty

@leaf-agent leaf-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The doc half checks out: asks.py's own docstring is "Declaration-driven page and thread ask projections", and requests.py is "Durable one-shot requests and their terminal host receipts" — both entries now match the module they route to, and asks keeps the request-ask projections it actually owns (request_phases, _is_request) without claiming the lifecycle.

One finding on the test, inline: the naming lookup is flat, so the guarantee in its docstring holds for a new top-level module but not for a new member of registry/, served_state/, render_gate/, or validation/ — which is where the doc keeps a second, driftable list.

Unrelated to the change, but worth knowing before someone chases it: this is the first test that reads a CLAUDE.md, and a tend PR checkout restores every CLAUDE.md at any depth from the base branch before the session starts. In a bot PR session the test therefore reads the base doc against the merged module tree — on this branch it fails with unnamed in scripts/CLAUDE.md: ['requests.py'] even though the PR is what fixes that. Run against the PR's own doc (git show HEAD:plugins/leaf/skills/leaf/scripts/CLAUDE.md), tests/test_interact_layer.py is 126 passed, 6 skipped, matching the description.

Comment thread tests/test_interact_layer.py Outdated
max-sixty pushed a commit that referenced this pull request Aug 29, 2026
…n the suite (#106)

Tend sessions start with an empty Playwright cache, so the suite that is
most of `tests/` cannot run from one — `setup:` installs browsers under
the runner's home, which the sandbox does not inherit. This adds a
`sandbox_setup:` block that seeds the same headless shell `ci.yaml` uses
into the sandbox's own home, and asserts it by launching it. Verified in
a real tend sandbox: cache empty at start, install 4.4s, launch 0.8s,
and `uv run pytest --run-nightly -n0 tests/test_render_reactions.py -k
stacks_at_a_narrow_edge` then passes 2 tests in 4.26s where before it
could not start.

<details><summary>What was observed, and why this shape</summary>

### The evidence

Last night's `tend-nightly` session
([33238853704](https://github.com/max-sixty/leaf/actions/runs/33238853704))
reached this partway through the suite:

```
ERROR tests/test_render_conversations.py::test_the_line_offers_the_list_its_own_keys_rather_than_the_way_deeper_in
  playwright._impl._errors.Error: BrowserType.launch: Executable doesn't exist at
  /home/tend-sandbox/.cache/ms-playwright/chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell
```

It did not install the browser; it stopped verifying locally and said so
in its summary — "The browser suites also did not run locally — this
sandbox has no Playwright browsers installed — so CI was their first
pass on both PRs". Both PRs it opened (#104, #105) are green, so nothing
wrong went out. What went out unverified is the point: on a repo where
nearly every test drives a real browser, the session shipped with the
repo's own gate unrun.

Three `tend-ci-fix` sessions the same night did install it themselves,
and two of them lost a turn to the same wrong flag first:

| Run | Command | Result |
| --- | --- | --- |
|
[33229417627](https://github.com/max-sixty/leaf/actions/runs/33229417627)
| `playwright install --with-deps chromium --only-shell` | `sudo: a
password is required` |
|
[33229417627](https://github.com/max-sixty/leaf/actions/runs/33229417627)
| `playwright install chromium --only-shell` | ok, 3s |
|
[33230715465](https://github.com/max-sixty/leaf/actions/runs/33230715465)
| `playwright install --with-deps chromium --only-shell` | `sudo: a
password is required` |
|
[33230715465](https://github.com/max-sixty/leaf/actions/runs/33230715465)
| `playwright install chromium --only-shell` | ok, 4s |
|
[33231657192](https://github.com/max-sixty/leaf/actions/runs/33231657192)
| `playwright install chromium --only-shell` | ok, 5s |

`--with-deps` switches to root, and the sandbox user has no sudo — the
trap `running-in-ci`'s Privileges bullet names, and its prescribed
remedy is exactly this: propose the setup entry rather than work around
its absence.

### Why `sandbox_setup:` rather than `setup:`

`setup:` runs as the runner. Playwright downloads to
`$HOME/.cache/ms-playwright`, and per tend's config reference the
sandbox inherits shared system and toolcache paths but never the
runner's home. `sandbox_setup:` runs as the sandbox user with the
workspace as cwd, which puts the browser where `sync_playwright()` will
look for it. This is the neighbour of the `uv`-on-PATH gap that
[max-sixty/tend#1071](max-sixty/tend#1071) fixed
upstream for 0.1.20 — same boundary, but a home-scoped download rather
than a PATH entry, so the repo owns it rather than tend.

### Why no `--with-deps`

The runner image already carries the system libraries: the three
sessions above ran the browser suite successfully after a plain
`--only-shell` install, and so did the verification here. Adding a
second `setup:` step to run `--with-deps` as the runner would be
machinery for a need that has not appeared.

### Cost

~5.3s per session that boots an agent (4.4s install, 0.8s launch,
measured in this sandbox). `sandbox_setup` runs inside the
`max-sixty/tend/claude` step, so the `tend-notifications` and
`tend-review` runs that short-circuit before booting pay nothing — and
`tend-ci-fix`'s gate-skips never reach it either.

### The regenerated workflows

`.config/tend.yaml` is the source; `sandbox_setup` is an action input,
so it has to reach each generated workflow. Regenerated with `uvx
tend@0.1.20 init` — the version already pinned in every workflow header
— so the diff is three added lines per file and no other drift, which
also confirms the committed files match what 0.1.20 generates.

### Gate assessment

- **Confidence**: High. Structural — the sandbox home starts without the
cache on every run, so it recurs every time. Four sessions hit it in one
20-hour window: one abandoned the gate, three installed it themselves,
two of those after a failed escalation.
- **Magnitude**: targeted fix. One existing knob in one place, plus the
mechanical regeneration it requires.
- **Cost**: mostly wasted compute — a turn per session and a download
per session. The nightly case is the one that isn't purely waste: a repo
whose safety net is a browser suite had that net unavailable to the
session doing the surveying. No wrong outward action occurred, so the
remedy is held to the waste-class bar, which one config knob meets.

</details>

Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
@max-sixty
max-sixty merged commit ba464f8 into main Aug 29, 2026
4 checks passed
@max-sixty
max-sixty deleted the docs/name-the-requests-owner branch August 29, 2026 22:08
max-sixty pushed a commit that referenced this pull request Sep 10, 2026
…ule (#543)

`scripts/CLAUDE.md` tells a session that a clean `git status` after
`scripts/vendor.py <bundle>` is the check that a bundle still matches
the script, and that the check holds only where every fetched input is
pinned. It then sorts the bundles into those two classes — and two of
the seven are in neither, so a session rebuilding `floating-ui` or
`mcp-app` has no answer for whether a diff it sees is drift to fix or an
upstream patch to take.

Each belongs in a class the paragraph already describes:

- `floating-ui` reproduces. `@floating-ui/dom`, `@floating-ui/core`, and
`@floating-ui/utils` are all in `PINS`, and that closure is complete:
`dom` depends on `core` and `utils`, `core` on `utils`, `utils` on
nothing. Rebuilding it left `git status` clean.
- `mcp-app` fetches an input `PINS` does not name.
`@modelcontextprotocol/ext-apps@1.7.5` declares `@standard-schema/spec
^1.1.0`, which npm's resolver picks, so the bundle sits with `plot` and
`pierre`. It reproduced when I rebuilt it today — which is exactly the
reading the paragraph exists to keep a session from relying on.

This is drift rather than a deliberate omission. The paragraph dates
from `c7b37062` (2026-08-30); `mcp-app` landed in #188 on 2026-09-01 and
`floating-ui` in #537 last night, and #533 — the most recent commit to
touch the paragraph — added `jsdiff` to it, so it is meant to be kept
current.

The test follows the shape this repo already uses for instruction lists
(#93, #105, #331, #496): it reads the bundle names out of `vendor.py`'s
`BUILDS` and `COPIES` rather than restating them, and asserts each is
named in that one paragraph. On `main` it fails with `unplaced in
scripts/CLAUDE.md: ['floating-ui', 'mcp-app']`.

What it deliberately does not check is *which* class a bundle is placed
in — deciding that means resolving each package's dependency graph,
which is a network call this suite should not make. A name in the wrong
class still needs a reader to catch; a name that is absent no longer
does.

<details><summary>Verification</summary>

- `uv run pytest tests/test_interact_layer.py` — 167 passed, 6 skipped.
- The new case alone, with `scripts/CLAUDE.md` reverted to `main`'s
text: `AssertionError: unplaced in scripts/CLAUDE.md: ['floating-ui',
'mcp-app']`.
- `scripts/vendor.py floating-ui` and `scripts/vendor.py mcp-app` each
left `git status` clean.
- `npm view @floating-ui/dom@1.8.0 dependencies` → `{
'@floating-ui/core': '^1.8.0', '@floating-ui/utils': '^0.2.12' }`;
`@floating-ui/core@1.8.0` → `{ '@floating-ui/utils': '^0.2.12' }`;
`@floating-ui/utils@0.2.12` → none;
`@modelcontextprotocol/ext-apps@1.7.5` → `{ '@standard-schema/spec':
'^1.1.0' }`.

</details>

Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
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.

2 participants