Skip to content

[spark-compete] fix(os): tolerate TCC-protected (unreadable) ~/Desktop in discover_repo_paths#619

Closed
banse wants to merge 1 commit into
vibeforge1111:masterfrom
banse:fix/cli-os-desktop-permissionerror
Closed

[spark-compete] fix(os): tolerate TCC-protected (unreadable) ~/Desktop in discover_repo_paths#619
banse wants to merge 1 commit into
vibeforge1111:masterfrom
banse:fix/cli-os-desktop-permissionerror

Conversation

@banse

@banse banse commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Spark Compete Packet

{
  "schema": "spark-compete-hotfix-v1",
  "event": "spark-compete-first-event",
  "submission_mode": "public_repo_pr",
  "submission_target_url": "https://github.com/vibeforge1111/spark-cli/pull/619",
  "team": {
    "name": "The Dudes",
    "members": [
      "His Dudeness",
      "El Duderino",
      "Duder"
    ],
    "llm_device_holder": "His Dudeness",
    "device_holder_github": "banse",
    "github_accounts": [
      "banse"
    ]
  },
  "target_repo": {
    "id": "vibeforge1111/spark-cli",
    "source": "https://github.com/vibeforge1111/spark-cli",
    "owner_surface": "spark-cli"
  },
  "issue": {
    "type": "bug",
    "severity": "high",
    "title": "spark os subcommands crash with PermissionError on TCC-protected macOS ~/Desktop",
    "actual_behavior": "discover_repo_paths() in src/spark_cli/system_map.py guards on desktop.exists() but then calls desktop.iterdir() with no error handling. On a default macOS account the ~/Desktop directory is TCC-protected: .exists() returns True while .iterdir() raises PermissionError ([Errno 1] Operation not permitted). The error is uncaught, so `spark os capabilities` (and compile/authority/trace/memory, which all walk the desktop for repo discovery) crash with a raw Python traceback and exit 1.",
    "expected_behavior": "An unreadable Desktop should be tolerated the same way a missing Desktop already is: skip Desktop-based repo discovery, fall back to installed-module paths, and exit 0 with normal output. No traceback.",
    "repro_steps": [
      "On a default macOS account where the terminal/agent has not been granted Files-and-Folders / Full Disk Access, ~/Desktop is TCC-protected.",
      "From an installed Spark or a spark-cli checkout run: spark os capabilities (equivalently python -m spark_cli.cli os capabilities).",
      "Observe a PermissionError traceback originating in discover_repo_paths (system_map.py, the `for child in desktop.iterdir()` line); process exits 1.",
      "The same crash affects spark os compile, spark os authority, spark os trace, and spark os memory."
    ],
    "affected_workflow": "spark os capabilities|compile|authority|trace|memory and the onboarding system-map it backs"
  },
  "evidence": {
    "safe_links_only": true,
    "before_after_proof": "BEFORE: `spark os capabilities` on the installed CLI exits 1 with a redacted, bounded traceback ending in `PermissionError: [Errno 1] Operation not permitted: '<HOME>/Desktop'` raised at system_map.py discover_repo_paths. A new regression test reproduces the same PermissionError before the fix. AFTER: the regression test passes and `spark os capabilities` exits 0 with clean output and empty stderr. Full suite delta: baseline 36 failed / 660 passed vs with-fix 36 failed / 661 passed (the +1 is the new passing test; the 36 pre-existing failures are an unrelated unittest.mock import quirk present on clean main).",
    "links": [
      "https://github.com/vibeforge1111/spark-cli/blob/master/src/spark_cli/system_map.py",
      "https://github.com/vibeforge1111/spark-cli/pull/619"
    ],
    "forbidden": [
      "pdf",
      "zip",
      "exe",
      "unknown downloads",
      "tokens",
      "raw logs",
      "private repo maps",
      "secrets",
      "wallet material"
    ]
  },
  "proposed_fix": {
    "approach": "Snapshot desktop.iterdir() inside a narrow try/except OSError (children = [] on failure) before iterating, matching the narrow-exception style merged in PR #603 for the sibling walkers in this same file. Smallest possible change; behavior for a readable or missing Desktop is unchanged, and an unreadable Desktop now degrades gracefully to the installed-module fallback paths.",
    "files_expected": [
      "src/spark_cli/system_map.py",
      "tests/test_system_map.py"
    ],
    "tests_or_smoke": "python -m pytest tests/test_system_map.py::SparkSystemMapTests::test_discover_repo_paths_tolerates_unreadable_desktop -v  (fails with PermissionError before the fix, passes after). Smoke: `spark os capabilities` exits 0 after the fix."
  },
  "pr": {
    "branch": "fix/cli-os-desktop-permissionerror",
    "title_prefix": "[spark-compete]",
    "author_github": "banse",
    "body_must_include": [
      "packet",
      "team",
      "pr_author",
      "repo",
      "actual_behavior",
      "expected_behavior",
      "repro_steps",
      "before_after_proof",
      "tests_or_smoke",
      "duplicate_notes",
      "risk_notes",
      "review_claim"
    ],
    "url": "https://github.com/vibeforge1111/spark-cli/pull/619"
  },
  "review_claim": {
    "impact_claim": "high",
    "evidence_types": [
      "failing_test",
      "passing_test",
      "redacted_terminal_excerpt"
    ],
    "duplicate_notes": "Direct relationship to merged PR #603 ('Adopt narrow health and system-map exception handling'), which hardened the sibling filesystem walkers in this same file against PermissionError but MISSED discover_repo_paths. This PR completes that exact coverage with the same OSError pattern. Open PRs #475/#530/#314/#306 touch spark os / desktop discovery for unrelated concerns (JSON ok-field/exit-code, WSL desktop path, source-path resolution) and do not guard discover_repo_paths against PermissionError; closed PRs #536/#532/#534 narrow bare-excepts elsewhere in the file. Open PR #60 ('Handle os compile output permission failures') handles a PermissionError when WRITING the compiled system-map.json output in cmd_os_compile (cli.py); it does not touch system_map.py discover_repo_paths and does not prevent this read-side crash \u2014 `spark os capabilities` still crashes during ~/Desktop discovery even with #60 applied. Distinct root cause (read/scan of an unreadable ~/Desktop vs write of the output file). None fix this macOS TCC Desktop discovery crash. Re-checked against master immediately before submission.",
    "risk_notes": "Low risk. Change is confined to one helper and only adds a narrow OSError guard around a directory scan; it never broadens behavior, touches no secrets/credentials/installer/CI, and falls back to the already-supported installed-module path set. No security-sensitive surface; not a prompt-injection or network path.",
    "review_state_requested": "pr_review"
  }
}

Repro

  • Actual behavior: discover_repo_paths() in src/spark_cli/system_map.py guards on desktop.exists() but then calls desktop.iterdir() with no error handling. On a default macOS account the ~/Desktop directory is TCC-protected: .exists() returns True while .iterdir() raises PermissionError ([Errno 1] Operation not permitted). The error is uncaught, so spark os capabilities (and compile/authority/trace/memory, which all walk the desktop for repo discovery) crash with a raw Python traceback and exit 1.

  • Expected behavior: An unreadable Desktop should be tolerated the same way a missing Desktop already is: skip Desktop-based repo discovery, fall back to installed-module paths, and exit 0 with normal output. No traceback.

  • Steps a reviewer can follow:

    1. On a default macOS account where the terminal/agent has not been granted Files-and-Folders / Full Disk Access, ~/Desktop is TCC-protected.
    2. From an installed Spark or a spark-cli checkout run: spark os capabilities (equivalently python -m spark_cli.cli os capabilities).
    3. Observe a PermissionError traceback originating in discover_repo_paths (system_map.py, the for child in desktop.iterdir() line); process exits 1.
    4. The same crash affects spark os compile, spark os authority, spark os trace, and spark os memory.

Safe Evidence

  • Before / after proof: BEFORE: spark os capabilities on the installed CLI exits 1 with a redacted, bounded traceback ending in PermissionError: [Errno 1] Operation not permitted: '<HOME>/Desktop' raised at system_map.py discover_repo_paths. A new regression test reproduces the same PermissionError before the fix. AFTER: the regression test passes and spark os capabilities exits 0 with clean output and empty stderr. Full suite delta: baseline 36 failed / 660 passed vs with-fix 36 failed / 661 passed (the +1 is the new passing test; the 36 pre-existing failures are an unrelated unittest.mock import quirk present on clean main).

  • Tests or smoke command: python -m pytest tests/test_system_map.py::SparkSystemMapTests::test_discover_repo_paths_tolerates_unreadable_desktop -v (fails with PermissionError before the fix, passes after). Smoke: spark os capabilities exits 0 after the fix.

Scope And Risk

  • Owner surface: spark-cli
  • Files changed: src/spark_cli/system_map.py, tests/test_system_map.py
  • Security-sensitive areas touched: none — Low risk. Change is confined to one helper and only adds a narrow OSError guard around a directory scan; it never broadens behavior, touches no secrets/credentials/installer/CI, and falls back to the already-supported installed-module path set. No security-sensitive surface; not a prompt-injection or network path.
  • Rollback note: revert the single commit on branch fix/cli-os-desktop-permissionerror.

Duplicate Search

Contributor Checklist

  • One focused fix, not a stack.
  • Packet above is complete and valid JSON (validated status: pass).
  • Evidence is GitHub links + redacted bounded terminal/test excerpts only.
  • No secrets, tokens, raw logs, private maps, or downloadable proof files.
  • I actually ran the listed tests/smoke checks.
  • Duplicate notes explain what was searched and the new value.
  • Risk notes cover security/installer/CI/dependency surfaces.

discover_repo_paths in system_map.py checked desktop.exists() then
iterated desktop.iterdir() with no error handling. On macOS the default
~/Desktop is TCC-protected: .exists() returns True but .iterdir() raises
PermissionError ([Errno 1] Operation not permitted). That uncaught error
crashed `spark os capabilities|compile|authority|trace|memory` with a raw
traceback (exit 1). A missing Desktop was already handled gracefully; an
unreadable one now is too.

Merged PR vibeforge1111#603 hardened the sibling filesystem walkers in this file
against PermissionError but missed discover_repo_paths. This completes
that coverage with the same narrow OSError handling pattern.

Fix: snapshot desktop.iterdir() inside try/except OSError -> [] before
iterating, mirroring the surrounding narrow-exception style.

Adds a regression test that drives discover_repo_paths with a desktop
whose iterdir() raises PermissionError plus a non-empty installed dict,
asserting it returns the installed paths without raising.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@banse

banse commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Closing this PR — the fix was adopted and merged to master via #620 (maintainer adoption of #619, credited to @banse). The narrow OSError guard in discover_repo_paths is now on master. Thanks!

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.

1 participant