Skip to content

Reject canonical escapes in private storage policies - #3834

Merged
trissim merged 1 commit into
mainfrom
work/pi-toad-errors-private-canonical
Nov 1, 2026
Merged

trissim merged 1 commit into
mainfrom
work/pi-toad-errors-private-canonical

Conversation

@trissim

@trissim trissim commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

2 files, +112/-11. Merged at e26a1897b1c8eff5fa76f06da96e764e10b02179; independent Core isolation controls passed 14/14 in 1.60s (rc0). Installed in the shared main-only update; combined frontend/runtime and affected live acceptance remain open. Fix the private-policy counterexample where root/checkouts points outside the verification root and both the stored rule and its projection canonicalize to that same escaping path.

The storage policy now owns both the private directory mapping and the pure containment query. No frontend path roster, second Comms, new store, environment setting, fallback or policy migration.

Before → after

Before:

  • StorageRule.within supplied four path overrides separately from their field declarations.
  • checkout_base.__post_init__ resolved a symlink, so equality of two rules could agree outside the intended root.
  • Preparation compared policies without establishing their physical containment.
  • A future child's default scratch path could follow a pre-existing link outside its configured scratch base.

After:

  • ScopedDirectory(ValueDirective[Path]) extends the existing field-directive mechanism. Each scoped field declares its own private relative location. FieldCodec.field_plans supplies discovery; adding another scoped field needs no separate roster edit.
  • Its shared directory behavior checks the physical target against the physical root before returning the path. Outward links refuse; inward links remain admitted.
  • StorageRule.within derives its replacements from those declarations. The four-location keyword table is deleted.
  • StorageRule.require_isolated(root) -> None is the side-effect-free observation contract. Existing preparation consumes it and preserves existing private limits and stored bytes on refusal.
  • initial_roots uses the same directory behavior, so a future default allocation cannot follow an escaping child link under its scratch base.

The existing FieldCodec wire fields and defaults are unchanged. No policy file is rewritten to manufacture isolation. /proc remains an unscoped read source; storage limits and global HOME-dependent defaults keep their existing owners.

Performance retains the paired #1393 consumer change: consume the actual already-observing service's registry.storage_rule, then call require_isolated, without opening Comms or writing during observation. Candidate02 does not contain this API and is not retrofitted or qualified by this source checkpoint.

Source evidence

Baseline e87abcc3f3cb5cc2df4407e363698a30ea643902; allocated owned checkout reused, clean tracked baseline, pushed branch verified remote=HEAD. Open-PR file check found no overlapping storage-rule/test edit at allocation.

Retained NRA query over current Core/owned Toad src/tests/tools plus the actual physical consumer tools: 1,898 file retrieval corpus, 75 selected modules, 961 original ClassDefs, 929 matching canonical projections, 32 OPEN, 2,024 source sites, zero parse omissions. It reuses the existing applied-map projections only on exact source-hash/line/name matches; changed, local, ambiguous and external cases stay OPEN. This is lexical ownership evidence, not runtime binding or global equivalence proof. Original-field declarations, assignments/checks/imports and allocation/preparation consumers are retained in storage-canonical-source-map.json.

The first source query hit its90s INT bound in repeated ast.get_source_segment line splitting. Its failure is retained. The corrected query reused NRA's traversal and indexed source lines, completed0; the raw second log's CANONICAL_MATCHED938 includes nine retained OPEN rows, so the counts above are derived from actual row statuses, not that label.

This is an authored owner repair, not an NRA-admitted transformation claim or an expansion of the separately stopped -inf proof work.

Verification so far

Source/import/codec command completed0, using existing read-only dependencies and PYTHONPATH pointing only at this allocated Core source:

  • both changed modules compile;
  • current and extended StorageRule codec roundtrips;
  • declaration-derived private projection, preserved limits and /proc;
  • actual ScopedDirectory → ValueDirective → FieldDirective → Generic → object MRO;
  • a new scoped subclass field participates without another roster;
  • the named path remained unmaterialized: no Comms/frontend/model/root/test launch.

Receipt: storage-canonical-source-codec.{log,rc}. Targeted NRA syntax reproof of the two changed modules is recorded separately; it is not behavioral equivalence.

Eleven new filesystem behavior cases passed within the 14-case selection: the three declared child-directory roles cover pre-write outward refusal, stored/reopened-policy refusal without byte changes, and inward-link preservation; plus future-child link refusal and new-field declaration derivation. Existing durable-policy/private-limit and foreign-policy no-rewrite controls remain, with the existing actual CheckRoot/reopen/future-child/PiRedeal publication control.

Completed bounded Core selection, run serially by pi-commander under the existing test lease on exact head 0c6633f85f408b15adcb65976f1fc7f70928975f:

  • tests/test_storage_rules.py::TestIsolatedDirectories
  • tests/test_storage_rules.py::test_future_child_cannot_follow_an_escaping_scratch_link
  • tests/test_storage_rules.py::test_private_projection_uses_new_field_declarations_without_another_roster
  • existing test_isolated_policy_is_durable_and_preserves_existing_private_limits
  • existing test_isolated_preparation_refuses_an_existing_foreign_policy_without_rewriting
  • tests/test_check_root.py::test_check_roots_contain_new_child_allocations_and_actual_rule_publication

Evidence read back: /home/ts/.cache/agent-scratch/pi-commander/heartbeat-publication/isolation-3834-controls-1.log and .rc: exact source/import path and HEAD printed, 14 passed, 2 warnings in 1.60s, rc0. Pi-commander reports the serial, leased selection above (9 directory cases + 5 individual cases); the retained log independently confirms the imported storage-rule path, exact HEAD and completed result. No reviewer rerun. Forge merge readback retains the exact tested head and merge commit.

This establishes the independent storage owner and actual CheckRoot/reopen/default-child/PiRedeal control, not the combined #3825/#3834/#1393 runtime consumer or physical journey. Actual paired frontend qualification, fresh combined-runtime admission and affected native/CLI/channel acceptance remain open. The independent main-only shared installation is recorded below. No full suite, local NRA gate, candidate/provider/physical launch or installer was run for this PR. Failed roots and Candidate02 remain unchanged.

Shared installation (not full live acceptance)

The shared installer completed the main-only update to e26a1897b1c8eff5fa76f06da96e764e10b02179: 93 phases in 70.0s, rc0, no pending carries, reconcile-stores changed 0, 792 scanned files identical, 2,881 thread records read back. Its retained result is /home/ts/.cache/agent-scratch/pi-commander/heartbeat-publication/main-only-live-install-1.{log,rc}. Protected old Toads were not signalled.

Independent readback confirmed current resolves to /home/ts/.local/share/agent-comms/runtimes/core-e26a1897b-lock-b7cd09e875fefab3; its build.json records the exact Core commit and Toad ec224a6c4d8bb510502a46a75886726f0969707c (also confirmed in Toad's installed direct_url.json). The installed storage_rules.py:313–394 contains ScopedDirectory, declaration-derived within, pure require_isolated and future-child containment. The install source tree had already retired; no attempt was made to reuse or recreate it.

The initial readback above established installed source, not behavior. A subsequent bounded run used that exact installed interpreter with -I -B and the production StorageRule API on fresh owned filesystem paths: rc0, four cases completed. An outward checkout link refused before policy creation; a stored/reopened policy refused a new outward link without changing its SHA256; an inward link prepared and reopened successfully; a future-child scratch link refused. The external marker hash and its directory contents stayed unchanged. Receipt: /home/ts/.cache/agent-scratch/pi-toad-errors/installed-isolation-e26-01.{log,rc}. Run paths and the one-shot were retired after exit and result retention.

That is actual installed storage-API behavior, not an end-to-end CheckRoot/frontend/native/CLI/channel journey or expanded Toad#1389 acceptance. No pytest, Comms root, model, native, physical or candidate launch was performed; the existing 14-case control remains separate source-head evidence. The #3825/#1393 combined qualification remains open.

Separately, #3831 integration exposed an enclosing-Git versus registered-scratch write-projection failure in the existing homed regression. Pi-toad-errors owns its source-only follow-up with pi-commander retaining reruns. The containment API pass does not clear that different failure.

Assumed: the selected filesystem is stable through each checked operation. These are point-in-time canonical-containment checks, not a hostile-filesystem atomic sandbox or an attestation of arbitrary manually edited registry rows. Original sessions, failed/UNKNOWN roots, external policies and peer/live trees were not rewritten.

root/checkouts → outside; stored == projected. Two witnesses, one getaway car. Containment now checks the road, not their matching alibis.

Copilot AI balanced review requested due to automatic review settings November 1, 2026 09:09

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

debt-src at 0442fd0, every finding:

+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-qmsb12bm --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/agent-comms
= 16.285s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-qmsb12bm --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/agent-comms (16.285s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-4t8bo091 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/formbridge
= 0.486s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-4t8bo091 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/formbridge (0.486s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-k_kozpi6 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/metaclass-registry
= 0.391s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-k_kozpi6 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/metaclass-registry (0.391s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-hftxz0gf --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/textual
= 0.854s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-hftxz0gf --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/textual (0.854s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-5t_yhgk3 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/toad
= 0.567s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-5t_yhgk3 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/toad (0.567s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-kgxcv62c --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/ui-theme
= 0.376s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-kgxcv62c --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/ui-theme (0.376s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-godnwm5t --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/agent-comms
= 9.002s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-godnwm5t --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/agent-comms (9.002s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-tbexc5a3 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/formbridge
= 0.407s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-tbexc5a3 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/formbridge (0.407s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-xzem_txy --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/metaclass-registry
= 0.388s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-xzem_txy --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/metaclass-registry (0.388s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-eba92yst --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/textual
= 0.903s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-eba92yst --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/textual (0.903s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-ns2q2roi --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/toad
= 0.566s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-ns2q2roi --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/toad (0.566s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-_5e2tttj --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/ui-theme
= 0.379s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-_5e2tttj --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/ui-theme (0.379s elapsed)
grew TypeErrors:src/agent_comms/storage_rules.py +1
found TypeErrors:src/agent_comms/storage_rules.py at src/agent_comms/storage_rules.py:364 (Argument 2 to "replace" of "Self" has incompatible type "**dict[str, Path]"; expected "float"  [arg-type])

@trissim
trissim merged commit e26a189 into main Nov 1, 2026
8 of 10 checks passed
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