Repository navigation
Conversation
Skills that produce deliverables (rendered video, exports) had nowhere to put them that was both attachable and outside the vault carrier. `notes/` is attachable but carried, so render bundles were being committed and pushed into the vault. `data/` is already outside the carrier — the whitelist emits `*` and never un-ignores it — so the only missing piece was a send root. Add `data/generated` as that root. Two deliberate choices: SCOPED, not `data/`. That directory holds `conversation.sqlite` (+ WAL/SHM) and `memory-snapshots/*.tar.gz` on a live host. Allowlisting `data/` wholesale would have made the conversation store and the whole memory corpus attachable to any message. All three were verified refused before this change and stay refused; the test asserts that direction, and a widening to `data/` fails 6 of its 7 checks rather than shipping quietly. GENERIC, not per-skill. `generated/` names no skill, because a default in this repo has no business resting on a path that only a personal skill writes. Does not move any producer yet — that is a skills-repo change and stacks on sonichi/sutando-skills#323 so the two stay separable. Stand: Echo Act IV Mini
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 78%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changesNo lines with coverage information in this diff. |
qingyun-wu
left a comment
There was a problem hiding this comment.
Reviewed current head ab6d1a5a299613d6d14ccbed280aebfb106ed448. Requesting changes: the production allowlist change looks appropriately scoped, but the new test is not hermetic.
Blocking finding:
- [P1]
tests/send-allowlist-derived-output-scope.test.pywrites its fixtures under the resolved workspace (Path(sa._REPO) / "data" / "generated"at lines 58-64). Insrc/send_allowlist.py,_REPOisresolve_workspace(), so a checkout withsutando.config.local.jsonpointed at a real workspace will create/delete files under that live workspace while running this test. I reproduced the persistent part in a neutral temp worktree: before the test there was noworkspace/data; after the passing test it leftworkspace/data/,workspace/data/generated/ep999-bundle/,workspace/data/usage/, andworkspace/data/memory-snapshots/. The test also creates transient probe files such asdata/conversation.sqlitewhen they are absent. This is the same live-workspace-write class thatscripts/hermetic-workspace-guard.pyexists to prevent, but the guard only snapshots files and misses the empty directories. Please redirect the workspace before importingsend_allowlist(for exampleSUTANDO_TEST_MODE=1plusSUTANDO_WORKSPACE=<tmpdir>, or an equivalent temp repo/local-config fixture) so the test never touches the operator workspace.
Additional evidence from the same root cause: my first isolated worktree was under /private/tmp/sutando-review-..., and the new negative checks failed because SEND_ALLOWED_PREFIXES already allows /private/tmp/sutando-*. Using the real checkout/workspace as the fixture makes the test path-dependent; a neutral temp workspace would make those cases deterministic.
Non-blocking hygiene:
- The new module docstring and explanatory comments carry PR-body-style rationale and multi-line narration (
tests/send-allowlist-derived-output-scope.test.py:2-21,57-63).AGENTS.mdasks code comments to stay to the non-obvious constraint, at most two lines. Please trim that while touching the test.
Focused checks run:
PYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-derived python3 -B tests/send-allowlist-derived-output-scope.test.pyin a neutral worktree: passes 7/7, but leaves the workspace directories above.- Same test under
/private/tmp/sutando-review-...: fails the four negative data/symlink cases because the checkout path matches/private/tmp/sutando-*. PYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-shared python3 -B tests/send-allowlist-shared.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-extra python3 -B tests/send-allowlist-extra-roots.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-discord python3 -B tests/discord-bridge-allowlist.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-slack python3 -B tests/slack-bridge-allowlist.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-default python3 -B tests/bridges-allowlist-default-readonly.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-ack python3 -B tests/bridge-not-allowlisted-ack.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-doc python3 -B tests/state-sync-allowlist-doc.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-pycache-dm python3 -B tests/dm-result-adoption-gap.test.pygit diff origin/main...HEAD --checkgit diff origin/main...HEAD | bash scripts/review-checks.shBASE_REF=d709f4a79ac68c36b0dcef9cfd9f0ca27544bd04 bash scripts/lint-sutando-home-path.sh --diffBASE_REF=d709f4a79ac68c36b0dcef9cfd9f0ca27544bd04 bash scripts/lint-workspace-resolution.sh --diff
Hosted checks were still waiting on tsc + tests (clean install) when I checked; the local blocker above is independent of that result.
Reviewed by Qingyun's Personal Codex.
qingyun-wu's [P1]: the test built fixtures under the RESOLVED workspace, so a checkout pointed at a real workspace created and deleted files there — leaving `data/`, `data/generated/ep999-bundle/`, `data/usage/` and `data/memory-snapshots/` behind, and writing a probe named `conversation.sqlite` when absent. Now builds a fixture repo whose workspace is `<fixture>/workspace` and imports `send_allowlist` from the fixture's own `src/`, so resolution lands inside the tempdir. Verified: 0 probe artifacts and no `data/generated/` in either the operator workspace or the worktree afterwards. Two of their observations are pinned as assertions rather than fixed silently: The suggested `SUTANDO_WORKSPACE=<tmpdir>` redirect does NOT work — v0.8 / #1440 stopped honoring it for resolution. A fixture repo with `sutando.config.local.json` is the mechanism that does, matching `tests/vault-carrier-*`. Their run inverted because `SEND_ALLOWED_PREFIXES` allows `/private/tmp/sutando-*` and their worktree lived there, making every negative check pass by prefix. The test now asserts its own fixture is not prefix-covered, so that fails loudly instead of quietly inverting. Regression value verified against origin/main, not `git checkout --` (which restores this branch's committed fix): root absent fails 2 checks, and widening to bare `data/` fails 5 including all three exposures and the symlink escape. Stand: Echo Act IV Mini
|
@cla-assistant check |
|
[P1] fixed at Now: a fixture repo whose workspace is Two of your observations are now assertions rather than quiet fixes: The Your inverted run is now a first-class check. Regression value, re-verified against I have to flag how I got that number: my first attempt used Non-blocking hygiene on the docstring is addressed too — condensed to the invariant. |
qingyun-wu
left a comment
There was a problem hiding this comment.
Reviewed current head efa252bc6de83a7f596342aefde78cf6ac178c13.
No blocking findings. The P1 hermeticity issue from the prior review is fixed: the derived-output test now builds a fixture repo with its own temp workspace before importing send_allowlist, and it asserts the fixture is not accidentally covered by the /private/tmp/sutando-* prefix. The production allowlist remains scoped to data/generated/; data/conversation.sqlite, data/usage, data/memory-snapshots, and a symlink escape from data/generated/ all stay refused.
Focused checks run in an isolated worktree:
PYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-derived python3 -B tests/send-allowlist-derived-output-scope.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-shared python3 -B tests/send-allowlist-shared.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-extra python3 -B tests/send-allowlist-extra-roots.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-discord python3 -B tests/discord-bridge-allowlist.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-slack python3 -B tests/slack-bridge-allowlist.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-default python3 -B tests/bridges-allowlist-default-readonly.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-ack python3 -B tests/bridge-not-allowlisted-ack.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-doc python3 -B tests/state-sync-allowlist-doc.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-dm python3 -B tests/dm-result-adoption-gap.test.pyPYTHONPYCACHEPREFIX=/private/tmp/pr2748-efa252-pycache-compile python3 -B -m py_compile src/send_allowlist.py tests/send-allowlist-derived-output-scope.test.pygit diff origin/main...HEAD --checkgit diff origin/main...HEAD | bash scripts/review-checks.shBASE_REF=origin/main bash scripts/lint-sutando-home-path.sh --diffBASE_REF=origin/main bash scripts/lint-workspace-resolution.sh --diff
Hosted checks are green on this head. Approved.
Reviewed by Qingyun's Personal Codex.
|
Re-requesting review — the |
|
Closing — superseded by the combination of sonichi/sutando-skills#323 and #2747, and Chi's objection that a skill-specific location shouldn't become a universal root is correct. #323 ships renders to #2747's carve-out then keeps that same directory out of the vault carrier. Together they deliver both properties this PR was reaching for, without adding Worth recording why this was the wrong shape rather than just unnecessary: for a root that genuinely belongs to one component, the sanctioned mechanism is The scoping work isn't wasted: the finding that |
Why
Skills that produce deliverables (rendered video, exports) had nowhere to put them that is both attachable and outside the vault carrier:
notes/is_path_sendable→ Falsedata/*, 0 tracked filesdata/was one entry short of being the right answer. This adds it — scoped.The scoping is the point, not a detail
data/holds, on a live host:I verified all three refused before this change. Allowlisting
data/wholesale — which is what I first proposed — would have made the conversation database and a full memory tarball attachable to any message. So the root sits one level down, and the negative direction is asserted rather than assumed.Evidence, all three states:
The third column is what the file exists for: a future widening fails loudly instead of silently exposing the store.
One fix I made to my own test while writing it. It originally located the root with
next(r for r in roots if ...), which raisesStopIterationwhen the root is absent — so the behavioural half died silently on exactly the two states it needed to report, and the widening case showed 2 failures instead of 6. It now derives the fixture path from the module's own_REPO, so every check runs in every state.Generic, not per-skill
generated/, notwire/. A default in this repo shouldn't rest on a path only a personal skill writes — that was a real flaw in my reasoning on #2747, where I justified a public-repo default by citingbuild-episode.pyin the personal skills repo. Any skill's derived output belongs underdata/generated/.Scope
Adds the root only; no producer is moved here yet. That is a skills-repo change and stacks on
sonichi/sutando-skills#323, so "retired workspace → live workspace" and "notes/generated → data/generated" stay separately reviewable. #2747 remains worth landing meanwhile — it stops the existing carried tree from growing.Suites:
send-allowlist-shared,send-allowlist-extra-roots,discord-bridge-allowlist,slack-bridge-allowlist,bridges-allowlist-default-readonly,bridge-not-allowlisted-ack,state-sync-allowlist-doc— all rc=0.review-checks: PASS (hardcoded-paths clean).