Skip to content

docs(sandbox): replace the degrade-open enumeration with a rule that cannot drift - #6399

Merged
bolichen97 merged 1 commit into
kirodotdev:mainfrom
rnoack1:docs/sandbox-degrade-open-enumeration
Aug 28, 2026
Merged

docs(sandbox): replace the degrade-open enumeration with a rule that cannot drift#6399
bolichen97 merged 1 commit into
kirodotdev:mainfrom
rnoack1:docs/sandbox-degrade-open-enumeration

Conversation

@rnoack1

@rnoack1 rnoack1 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Problem / Motivation

_mount_or_die's docstring in src/kiro_crew/sandbox.py explains why every mount in the
namespace launcher refuses rather than degrades, then draws a boundary around the decisions
nearby that do degrade open. On main that boundary reads:

The degrade-open decisions nearby are deliberately narrower -- the tmpfs source-dir fallback
and the hardlink scan's budget ceiling -- and neither concerns the hiding itself.

There is a third degrade-open decision in the same launcher: the EXPOSE_FILES pre-read.
Its own inline comment already places it in that class, in those words:

An expose source that cannot be READ degrades to "not exposed" with a stderr warning, the same
way the Step 7 hardlink scan degrades open.

So the file currently contains a two-member enumeration and, ~110 lines below it, a third member
that self-identifies as belonging to it.

Neither author could have caught this. The docstring landed in 2bfa78f8b376
("refuse to exec when a credential-hiding mount fails", #6074). The EXPOSE_FILES degrade
landed in f65bd17ad4 ("let an unreadable cc expose source degrade, not abort", #5992), later
the same day. Each merged without the other in view.

Why it matters

This docstring is the one place that states the launcher's fail-closed/fail-open policy, and it
is written to be read by the next person deciding whether some new failure path may degrade. An
enumeration that is short by one teaches that the exception list is closed when it is not, and
the closing clause — "neither concerns the hiding itself" — is the part a reader would lean on
to justify a new degrade. Left as is, it is a plausible-looking licence to degrade something
that should refuse.

Documentation only. No behaviour changes, so there is nothing to regress at runtime; the cost of
leaving it is entirely in what the next reader concludes.

What changed (motivation → approach → change)

The obvious repair is to make the list three items long. That is not sufficient, and working out
why is most of this change.

I read all three sites before editing, and they do not degrade in the same relation to the
hiding:

  • tmpfs source-dir fallback (sandbox.py L1661-1679). When no cross-fs tmpfs is available,
    _tmpfs_src stays None and the empty source inode comes from the default tempdir. The bind
    still runs through _mount_or_die (L1731, L1757) either way, so the hiding is established
    regardless; what is given up is cross-fs hardening against a teardown propagation race.
  • hardlink scan budget ceiling (L2019-2022, warning at L2088-2093). Truncating the walk
    weakens detection of an alias that circumvents the hiding. Step 7 runs after the hiding
    loops, so the mounts themselves are already in place.
  • EXPOSE_FILES pre-read (L1682-1717). On OSError the loop warns and does not populate
    expose_data; the restore loop is gated if src_path in expose_data, so the file is never
    written back into the empty mount. It therefore fails toward more hiding.

That last one is why adding a bullet alone would not have been enough — and, on the same logic,
why a three-item list is not enough either. EXPOSE_FILES is the deliberate exception to the
hiding, so the old closing clause ("neither concerns the hiding itself") was false of it. But a
list closed at three goes stale at the fourth degrade exactly as the list closed at two went stale
at the third, and the launcher already has more: the depth-5 walk truncation (sandbox.py
L2079-2083) and three silent except OSError: pass skips in the scan (L2057, L2065, L2095) each
abandon detection while every mount holds.

So the shipped change removes the count rather than incrementing it. The docstring now carries
a rule instead of a roster: a failed hiding mount is the one failure _mount_or_die exists to
prevent, nothing else in the launcher is one, and each of those others argues its case at its own
site. The EXPOSE_FILES pre-read is named there as an example, explicitly not as a roster, so a
fourth degrade-open decision cannot make the paragraph false. The rule is read narrowly on
purpose: it says none is a failed hiding mount, NOT the stronger claim that no credential can end
up reachable — a budget- or depth-truncated hardlink scan can leave an alias unchecked. A closing
norm keeps the boundary the original clause was reaching for: a degrade elsewhere is never license
to degrade a mount.

Exactly ONE site comment is edited — the hardlink budget comment at sandbox.py L2020 — which
gains that truncation cost explicitly, since it was the one consequence no site stated. The tmpfs
fallback and EXPOSE_FILES sites already carry their own rationale, so nothing was added there;
growing the diff to make a sentence true would be the wrong trade.

One file, src/kiro_crew/sandbox.py, prose only. No mount, pre-read, raise, sys.exit, or
control flow is touched. The docstring sits inside the f'''...''' launcher template, so the
edit deliberately introduces no { or }.

Tests

No test changes. Nothing pins the edited prose:

  • test/test_sandbox_mount_checked.py slices the helper region from def _mount_or_die( to
    REAL_UID = , which contains this docstring, but its _LANDMARKS are structural code
    markers only (deliberately so — see the comment above them) and its text assertions are on
    the four _mount_or_die(...) call forms. Its script.count("_mount_or_die(") == 5 invariant
    still holds.
  • A repo-wide search for the removed phrases (deliberately narrower,
    neither concerns the hiding itself) finds no test referencing them.

Existing coverage run:

  • All 17 test/test_sandbox*.py suites: 523 passed, 1 skipped.
  • Full backend suite (pytest -q -n auto --dist loadgroup): see Manual verification.

Manual verification

Because the docstring lives inside an f-string template, the real risk is a quoting or brace
error that would break launcher generation rather than a test assertion. Verified directly by
building the script from the edited module:

  • _build_launcher_script("strict") returns 32506 chars.
  • New prose present in the generated launcher: 1 occurrence.
  • Old prose absent from the generated launcher: 0 occurrences.
  • _mount_or_die( in the generated launcher: 5 (1 definition + 4 call sites).
  • The docstring still opens immediately after the def line in the emitted source.
  • A fabricated control token returns 0, confirming the probe can report absence.

Related Issues

Follow-up to #6074 (which added the enumeration) and #5992 (which added the third member).

Checklist

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable)
  • No secrets, credentials, or internal references in the diff

@rnoack1
rnoack1 requested a review from a team as a code owner August 27, 2026 21:13
@rnoack1
rnoack1 requested a review from Zedmor August 27, 2026 21:13
@github-actions github-actions Bot added fork Pull request from a fork (external contributor) readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 27, 2026
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from d134049 to bb65118 Compare August 27, 2026 22:08
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review (fork) — ✅ no blocking findings

Reviewed b76c5dbf26f3ee5e3197aeafc8adc2dd6d089f7e via the fork AI-review pipeline; updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] b76c5db

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5, fork) — ✅ PASS

Design-level review of b76c5dbf26f3ee5e3197aeafc8adc2dd6d089f7e via the fork AI-review pipeline — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All description claims check out against the base: the two-member enumeration exists at sandbox.py:1571-1574, the third degrade-open site (EXPOSE_FILES pre-read) self-identifies at line 1687, and the patch is prose-only with no braces introduced into the f-string template. The fix removes the drift mechanism (the count) rather than patching the symptom (adding a third bullet), which is the root-cause repair for a stale-enumeration problem.

Design-Verdict: PASS

Replacing the roster with a rule removes the drift mechanism itself; the fix targets the root cause, is prose-only, and fully reversible.

[DESIGN-REVIEWED] b76c5db

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review (fork) — ✅ no blocking findings

Reviewed b76c5dbf26f3ee5e3197aeafc8adc2dd6d089f7e via the fork AI-review pipeline; updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] b76c5db

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5, fork) — ✅ PASS

Premise-level review of b76c5dbf26f3ee5e3197aeafc8adc2dd6d089f7e via the fork AI-review pipeline — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Verification complete. The base tree confirms every load-bearing claim: the two-item enumeration at sandbox.py:1571-1574, the EXPOSE_FILES pre-read comment at 1686-1687 self-identifying as a third degrade-open member, the budget-ceiling comment at 2022-2025, plus the depth-5 truncation (2070) and three silent except OSError: pass skips (2046, 2054, 2084) that make any closed list unstable. The diff is prose-only, matches the description, and adds no {/} inside the f-string template.

First-Principles-Verdict: PASS

The stale count is the cause of the doc defect, and the change deletes the count itself rather than incrementing it — a cause-level fix with zero new surface.

What this change ships

Intent: keep the launcher's one fail-closed/fail-open policy statement true so the next reader cannot cite it to justify a new degrade — a FIX (documentation defect).

  1. The docstring's two-item degrade-open list becomes a rule, with EXPOSE_FILES named as an example — justified; the list is verifiably short by one on base (sandbox.py:1686 self-identifies as the third member), and four further degrade sites (depth limit, three silent OSError skips) mean any closed count re-stales.
  2. The rule is explicitly scoped narrowly — no claim that no credential stays reachable — justified; the truncated scan (sandbox.py:2074-2085) makes the stronger claim false.
  3. A closing norm: a degrade elsewhere never licenses degrading a mount — justified; it preserves the boundary the deleted clause carried.
  4. The hardlink budget comment gains its truncation cost — rides along, but declared, and required by item 1's own rule that each degrade argues its case at its own site.

Zero option for item 1 leaves a false closed enumeration in the one place the policy is stated; a bare three-item list was considered and rejected for the counted reason above. No new config, symbol, or schema — nothing to consumer-count.

[FIRST-PRINCIPLES-REVIEWED] b76c5db

@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from bb65118 to 7b4b19f Compare August 28, 2026 00:05
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 28, 2026
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from 7b4b19f to 4348488 Compare August 28, 2026 01:01
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 28, 2026
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from 4348488 to bc4ea71 Compare August 28, 2026 01:17
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention labels Aug 28, 2026
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from bc4ea71 to f2e8c3c Compare August 28, 2026 01:55
@github-actions github-actions Bot removed the readiness: action required A blocking check or review needs attention label Aug 28, 2026
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 28, 2026
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from 5c5d88c to afe9465 Compare August 28, 2026 04:10
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention labels Aug 28, 2026
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from afe9465 to 709a93b Compare August 28, 2026 04:56
@rnoack1 rnoack1 changed the title docs(sandbox): name the third degrade-open decision in _mount_or_die docs(sandbox): replace the degrade-open enumeration with a rule that cannot drift Aug 28, 2026
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 28, 2026
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from 709a93b to 12a2eae Compare August 28, 2026 06:05
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention labels Aug 28, 2026
…cannot drift

The docstring closed its exception list at two, which a third member already
falsified; a rule plus one named example cannot go stale at the fourth.
@rnoack1
rnoack1 force-pushed the docs/sandbox-degrade-open-enumeration branch from 12a2eae to b76c5db Compare August 28, 2026 06:47
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 28, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) August 28, 2026 22:16
@bolichen97
bolichen97 merged commit d19057f into kirodotdev:main Aug 28, 2026
69 checks passed
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 28, 2026
@rnoack1
rnoack1 deleted the docs/sandbox-degrade-open-enumeration branch August 28, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fork Pull request from a fork (external contributor)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants