Skip to content

fix(audio): establish canonical local-audio resource policy - #866

Draft
seonghobae wants to merge 314 commits into
developfrom
fix/audio-resource-policy-781
Draft

fix(audio): establish canonical local-audio resource policy#866
seonghobae wants to merge 314 commits into
developfrom
fix/audio-resource-policy-781

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Canonical #781 Resource Admission & Decode lane

This remains the single BandScope source owner for local-audio Resource Admission & Decode under #781. Current exact head: 91b64cf72977e3483723537a1385dc72c7402439; protected base remains develop@314ddeae7b775a4957594b599358c8255617eb2e. Keep Draft. Predecessor/sibling/downstream checks and reviews do not transfer.

Current product/scientific contract

The stack retains one versioned admission boundary: exact 100 MiB encoded ceiling; bounded logical EOF after encoded admission; source-container metadata preflight; canonical 44.1 kHz mono policy-v3 native NumPy float32; pre-copy sample/visible-byte/predicted canonical-byte admission; owned canonicalization of admitted non-owning views; bounded finiteness validation; YouTube parity; and path-free crash-durable publication identity.

Canonical decoded-buffer limits are artifact limits, not end-to-end process-memory promises. Decoder/resampler intermediates, coexistence of decoder and canonical buffers, downstream NumPy/PyTorch/model/accelerator allocations and full-song peak RSS/VRAM remain separately measurable acceptance.

Latest review → RED → repair

Fresh current-head review found a Resource Admission containment gap outside the already repaired analysis runner: import_youtube_url delegates the allowlisted bandscope_analysis.youtube importer to bandscope_desktop_core::wait_for_process_output, but that helper killed/reaped only its direct Python child before joining stdout/stderr readers. The Python importer uses yt-dlp with FFmpegExtractAudio, so an ordinary helper descendant could retain inherited output handles after the Python parent died and make the advertised timeout wait for that descendant.

RED 65c94f72c1b87ce6a7be1c7f316409530330bf5e adds a real Unix subprocess regression: a shell creates a five-second descendant that retains inherited output pipes; the product timeout is 50 ms and cleanup must return in under one second. Production 81a489026ce26fd14cf38bf1ea334e85d37346ee moves the GUI-independent timed importer onto a dedicated Unix process group before spawn and routes timeout/wait-error cleanup through group-targeted termination plus direct-child reap. Direct-child kill/reap remains the fail-closed fallback.

Shell-gate descendant 9c36cf0a0b427e2383a128202db832b268d2f791 mirrors the same executable regression through the Tauri native-shell test target. Documentation descendants e8b887c5…, 5f7692ff…, 066d8911…, 76ca2c06…, and final doctoring/TRACEABILITY 91b64cf7… make the product-gap baseline, CHANGELOG, ARCHITECTURE, app-security and evidence note code-current.

No hosted RED failure is claimed because RED and production repair were consecutive ordinary descendants under cancel-in-progress.

Claim boundary / remaining repair finding

Linux/macOS analysis and timed YouTube import now terminate ordinary descendants that retain the owned process group before returning from timeout/error cleanup. This is not sandbox/container containment and does not cover descendants that deliberately change process group/session. Windows remains direct-child-only until a race-free Job Object creation/assignment boundary exists.

One DDD/SOLID repair remains before protected adoption: the Tauri analysis runner and GUI-independent YouTube helper currently implement equivalent low-level Unix process-group setup/termination primitives in parallel. They must converge on one shared owner rather than drift as two security-sensitive implementations. Do not copy either implementation into downstream #970/#1160.

Exact-head review and gate

Only exact 91b64cf72977e3483723537a1385dc72c7402439 may establish current GREEN. CodeRabbit has been explicitly requested to re-review this exact head, including the RED/production/shell-gate lineage and the duplicate-primitive repair finding; no final response or qualifying independent formal APPROVED is claimed yet.

Fresh exact-head workflows are still settling:

  • build-baseline: in progress on real GitHub-hosted macOS/Windows runners; macOS lanes have reached native-shell build and Windows lanes are active in setup/dependency/build progression;
  • ci: queued;
  • sbom: pending;
  • Security Scan: queued;
  • SAST Semgrep: queued;
  • CodeQL PR: queued.

No predecessor GREEN transfers to this head.

Foundation / succession

Protected authority remains develop@314ddeae7b775a4957594b599358c8255617eb2e, protected=true, with required contexts still naming retired Analyze (javascript-typescript) / Analyze (python).

#1176 remains the sole one-file inherited Ruff formatter repair owner at exact 8fe6b6d99c009527ef0bcba419e6f6debdb23c23; do not copy that patch here. Its repository CI/build/SBOM/Security/SAST are terminal-success, while CodeQL remains a central verdict-publication failure and qualifying independent approval is absent.

Central .github#1902 is now Draft exact 1a3133f5efd147bc6892a2920749bf103f4683da on main@7fd571dbcdbae6acf29d8f4ee704d7ba6297e4db. It remains the canonical CodeQL recovery owner and currently records incomplete direct-evidence pagination as a blocking repair finding. BandScope does not copy its mutable implementation or synthesize statuses.

Project Persistence #970 and Active Player #1160 consume Resource Admission only after ordinary protected ancestry: control-plane/#1176#866#970#1160. Preservation lane #985 retains only unsuperseded M4A fallback requirement/evidence rather than acting as a parallel #781 writer. Reusable Slider #1188 remains separate Draft UI work at exact 21a84ecec2c1b2d1b0574e55e47c4553b20b6977; its repaired primitive is not actual-audio Active Player acceptance.

Fresh release truth remains immutable BandScope 0.1.3 / v0.1.3, published 2026-04-28T23:47:45Z. Commercial release remains independently blocked by #1129 audio-I/O licensing/format parity and #1181 pretrained-model rights; #1180 owns immutable commercially admissible model distribution/update semantics.

Residual #781 boundary

Causal source order is: consolidate the two equivalent Unix containment primitives into one owner; implement race-free Windows Job Object containment; then run rights-cleared full-length rehearsal-audio acceptance measuring cancellation latency, inherited pipe/handle release, descendant/temp cleanup, decoder/resampler/downstream peak RSS/VRAM and explicit per-job CPU/GPU budgets.

Security Notes

The attack surface is the native analysis/import subprocess boundary and ordinary decoder/download helper descendants. The WebView retains only BandScope-owned job/import product authority; no PID, process-group id, generic kill or exec capability crosses IPC. Linux/macOS containment is established before execution and group signalling precedes output-reader joins/direct-child reap. Windows and intentionally escaped Unix descendants remain outside the claim. Real-audio resource return, temporary artifacts and inherited-handle cleanup remain unproven commercial acceptance.

Merge gate

Keep Draft until the final exact head has terminal applicable repository/security/build evidence, zero valid unresolved findings, qualifying independent current-head approval, duplicate process-control ownership repaired, and protected-base/control-plane prerequisites resolved through canonical owners. No force-push, destructive rebase, self-approval, synthetic status, duplicate scanner, no-op retry commit, bypass or gate weakening.

@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seonghobae
seonghobae changed the base branch from develop to fix/bounded-score-pdf-read-864 August 16, 2026 14:38
@seonghobae
seonghobae changed the base branch from fix/bounded-score-pdf-read-864 to develop August 16, 2026 14:42

@cursor cursor Bot 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.

Stale comment

Reviewed exact head 3f976e55. Local-file Python, TypeScript, and Rust encoded-byte ceilings match (100 MiB, exclusive upper bound, exact ceiling accepted). Do not mark Ready or merge this draft until YouTube download uses that same ceiling and #865 is in protected develop.

Request changes: import_youtube_url now calls validate_local_audio_file_size only after youtube.py has already finished. That module still downloads with no yt-dlp max_filesize and then rejects > 50 * 1024 * 1024. A 60–100 MiB import that policy-v1 would accept is still rejected with a 50 MB message. A multi-gigabyte transfer can fill the cache root before the new native check ever runs, so the new YouTube-path size check is dead for oversized inputs.

Doctoring residual-risk text on this head still says the desktop/Rust intake path is not established, which is no longer true for local-file bootstrap.

The successor branch cursor/bc-977eae6a-247d-427f-a2eb-533a75284f2e-6591 drives YouTube admission from DEFAULT_MAX_ENCODED_FILE_BYTES, aborts in-flight, and updates the evidence note. Apply that here or reconstruct this branch onto it before Ready.

Checks on this synchronization were still queued at review time. Queued, skipped, predecessor, or draft-skipped CodeRabbit evidence is not success.

Open in Web View Automation 

Sent by Cursor Automation: fix all

Comment thread apps/desktop/src-tauri/src/main.rs
Comment thread docs/doctoring/audio-resource-policy.md Outdated
Comment thread CHANGELOG.md

@cursor cursor Bot 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.

Stale comment

Reviewed exact head 1f3fdb8b. The prior 50 MB / missing max_filesize / stale doctoring findings are fully addressed: YouTube download now uses DEFAULT_MAX_ENCODED_FILE_BYTES, rejects announced oversize before download=True, aborts from the progress hook, and revalidates the written file. Do not mark Ready or merge this draft until #865 is in protected develop and the abort-path cache leak below is on this head.

Request changes: in-flight abort still returns size_exceeded without deleting bytes already written. yt-dlp HttpFD writes the current block, then calls the hook; on exception it only closes the stream. The post-download path deletes an oversize final artifact; the abort path does not. Each rejected import can leave *.part, *-Frag*, and *.ytdl in a fresh project cache.

Successor cursor/bc-75568fe4-aa90-4cf7-bb40-c9d68be95b82-b46f at 5e8fa77f deletes owned siblings that stay inside that import out_dir and ignores escaped paths. Apply that here or reconstruct this branch onto it before Ready.

Queued, skipped, predecessor, or draft-skipped CodeRabbit evidence is not success.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread services/analysis-engine/src/bandscope_analysis/youtube.py

@cursor cursor Bot 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.

Reviewed exact head 5e8fa77f on fix/audio-resource-policy-781 (base develop@acdbea63). The prior in-flight abort finding is fully addressed on this head: _abort_over_budget_download deletes owned siblings before the fail-closed size_exceeded raise. _owned_file_path realpaths the candidate and the import out_dir, rejects the directory root, and requires resolved.startswith(root + os.sep), so a path or symlink that escapes that import directory is ignored. _remove_download_artifacts stems tmpfilename / filename (one .part strip) and removes matching stem, stem.*, and stem-* entries, which covers .part, .ytdl, and -Frag*. test_download_youtube_audio_progress_hook_deletes_partial_artifacts proves those three are gone after abort while keep-me.txt and an outsider .part remain.

The earlier 50 MB post-write, missing Rust intake doctoring, CHANGELOG 50 MB, and progress-hook int-only items stay fixed. YouTube admission uses DEFAULT_MAX_ENCODED_FILE_BYTES (100 MiB) in Python, desktop analysis.ts, and native audio_resource.rs. Announced oversize rejects before download=True. Exact 100 MiB is accepted; 60 MiB is accepted; 100 MiB + 1 is rejected. Closed #875 is the same tree as this head — do not reopen a competing abort-cleanup owner.

Next action: keep this Draft. Integrate #865 into protected develop first, then reconstruct and revalidate this stack on the unchanged resulting exact head. Do not mark Ready or merge on queued, skipped, predecessor, or CodeRabbit draft-skipped evidence. Remaining #781 channel/rate contracts and decoded-memory / CPU/GPU admission budgets are still out of this draft's claim — do not treat policy-v1 encoded-byte admission as full #781 closure.

Residual (not a change request): a process kill, a locked Windows .part, or a differently named format-id fragment can still leave cache bytes until that per-project import directory is removed. Generic DownloadError / timeout paths do not sweep unnamed artifacts. Admission still fails closed.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread services/analysis-engine/tests/test_youtube_duration_contract.py Fixed
Comment thread services/analysis-engine/tests/test_youtube_duration_contract.py Fixed
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="7a0387f298f9040b587d0af7684c10d8dd08c2b6"
red="f551694e1db95456928aaf506863adf376abf694"
fix="$head"
source="services/analysis-engine/src/bandscope_analysis/youtube.py"
baseline="docs/product-technical-gap-baseline.md"
doctoring="docs/doctoring/audio-resource-policy.md"
changelog="CHANGELOG.md"

echo "== Exact checkout =="
git rev-parse HEAD
git status --short
test "$(git rev-parse HEAD)" = "$head"
git show -s --format='%H%n%P%n%s%n%b' "$head"

echo "== RED and production fix patches =="
for commit in "$red" "$fix"; do
  echo "-- $commit"
  git show -s --format='%H%n%P%n%s%n%b' "$commit"
  git show --format=fuller --find-renames --unified=180 "$commit" -- \
    "$source" services/analysis-engine/tests "$baseline" "$doctoring" "$changelog"
done

echo "== Source structure and focused cleanup implementation =="
ast-grep outline "$source" --items all
rg -n -A150 -B35 \
  '(_artifact_name|_cleanup_stem|_owned_transient_video_file_path|_owned_completed_video_file_path|_remove_download_artifacts|Frag|\.part|\.ytdl|video_id|_remove_video_owned_file)' \
  "$source"
sed -n '250,475p' "$source" | cat -n

echo "== Relevant YouTube cleanup tests =="
fd -t f -e py . services/analysis-engine/tests | rg -i 'youtube|fragment|cleanup' | sort | while IFS= read -r file; do
  if rg -q -e 'Frag' -e 'cleanup' -e 'transient' -e 'ytdl' -e '\.part' "$file"; then
    echo "-- $file"
    cat -n "$file"
  fi
done

echo "== Grammar and cleanup authority inventory =="
rg -n -C 12 \
  -e 'isdigit\s*\(' -e '\\d' -e '\[0-9\]' -e '\-Frag' -e '\.part' -e '\.ytdl' \
  -e '_cleanup_stem\s*\(' -e '_owned_transient_video_file_path\s*\(' \
  -e '_remove_download_artifacts\s*\(' -e '_remove_video_owned_file\s*\(' \
  services/analysis-engine/src services/analysis-engine/tests \
  --glob '!**/__pycache__/**' || true

echo "== Documentation claim audit =="
for file in "$baseline" "$doctoring" "$changelog" TRACEABILITY.md; do
  if [ -f "$file" ]; then
    echo "-- $file"
    rg -n -i -C 10 \
      '(yt-dlp|FragmentFD|fragment|Frag|ASCII|Unicode|digit|cleanup|transient|%d|authority|part|ytdl)' \
      "$file" || true
  fi
done

echo "== Current-head PR, check, and review state =="
gh pr view 866 --repo "$repo" \
  --json isDraft,headRefOid,baseRefOid,mergeStateStatus,reviewDecision,statusCheckRollup,url
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'
gh api graphql -F owner="ContextualWisdomLab" -F name="bandscope" -F number=866 -f query='
query($owner:String!, $name:String!, $number:Int!) {
  repository(owner:$owner,name:$name) {
    pullRequest(number:$number) {
      reviewThreads(first:100) { nodes { isResolved } }
      reviews(first:100) { nodes { author { login } state commit { oid } submittedAt } }
    }
  }
}' --jq '
  .data.repository.pullRequest as $pr |
  {
    unresolved_threads: ([$pr.reviewThreads.nodes[] | select(.isResolved == false)] | length),
    reviews: $pr.reviews.nodes
  }'

echo "== Whitespace and final exact-head check =="
git diff --check "${head}^" "$head"
test "$(git rev-parse HEAD)" = "$head"

Length of output: 203582


@seonghobae I found no new actionable issue in the requested scope at exact head 7a0387f298f9040b587d0af7684c10d8dd08c2b6.

The prior P1 is closed.

  • _owned_transient_video_file_path first validates cache containment and the leased video ID.
  • It then examines only the artifact-name portion after {video_id}..
  • If that portion contains -Frag, it accepts only a complete ASCII fragment tail: [0-9]+ or [0-9]+.part.
  • Therefore, abc-Frag123.m4a.part-Frag2.part is rejected before generic .part cleanup can authorize deletion.
  • Normal artifact names with no -Frag token still retain .part and .ytdl cleanup support.

_cleanup_stem uses the same identity boundary.

  • It preserves legal -Frag text inside the valid video ID abc-Frag123.
  • It normalizes abc-Frag123.m4a.part-Frag2 and abc-Frag123.m4a.part-Frag2.part to abc-Frag123.m4a.
  • It leaves the full-width-digit forms unchanged. These forms cannot create sibling-sweep deletion authority.

The new regression is causal.

  • It verifies direct preservation of the reported ...-Frag2.part decoy.
  • It verifies preservation of the same-stem ...-Frag2.ytdl decoy.
  • It retains removal coverage for valid .part, ASCII -Frag2, and ASCII -Frag2.part artifacts.
  • It retains preservation coverage for a same-ID nontransient file.

The documentation remains consistent with the implementation.

  • The baseline and doctoring note limit fragment cleanup to terminal ASCII-decimal -Frag[0-9]+ and -Frag[0-9]+.part forms.
  • CHANGELOG.md states that legal -Frag text remains part of the YouTube video identity.
  • These documents do not extend deletion authority to Unicode lookalikes.

git diff --check reported no whitespace error. I used static inspection only.

The PR remains Draft and blocked. At inspection, ci / build-and-test, platform builds, Semgrep, and the Rust gate were still in progress. The current-head CodeQL compatibility checks had failed. No qualifying independent current-head approval was present. I did not use predecessor checks or reviews as current-head evidence.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Exact-head focus: 4e0aa5d74c3b1dc86e47227b3abd262e536859f1. Please review only the new cancellation-containment delta (af94694f… RED contract → 4e0aa5d… production fix): Unix process_group(0) setup, group-targeted termination/fallback/reap semantics, cross-platform compile safety, and any unintended signal/process-lifecycle consequence. Do not treat Windows as descendant-contained; that remains a separate Job Object gap.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="4e0aa5d74c3b1dc86e47227b3abd262e536859f1"
red_prefix="af94694f"

echo "== Exact checkout =="
git rev-parse HEAD
git status --short
test "$(git rev-parse HEAD)" = "$head"
git show -s --format='%H%n%P%n%s%n%b' "$head"

echo "== Resolve RED and show requested patches =="
red=$(git rev-list --all | grep -i "^${red_prefix}" | head -n 1 || true)
printf 'RED=%s\n' "$red"
test -n "$red"
for commit in "$red" "$head"; do
  echo "-- commit $commit"
  git show -s --format='%H%n%P%n%s%n%b' "$commit"
  git diff-tree --no-commit-id --name-status -r "$commit"
  git show --format=fuller --find-renames --unified=180 "$commit"
done

echo "== Find candidate process-management files =="
fd -t f . | rg -i '(tauri|sidecar|process|cancel|separat|analysis|command|cargo\.toml|rust)'

echo "== Outline changed Rust files =="
git diff-tree --no-commit-id --name-only -r "$head" | while IFS= read -r file; do
  case "$file" in
    *.rs)
      echo "-- $file"
      ast-grep outline "$file" --items all
      ;;
  esac
done

echo "== Process group / signal / child lifecycle inventory =="
rg -n -C 16 \
  '(process_group|CommandExt|pre_exec|setpgid|killpg|SIGTERM|SIGKILL|signal|terminate|cancel|Child|wait\(|try_wait|kill\(|spawn\(|tokio::process|std::process|cfg\(|unix|windows)' \
  . \
  --glob '*.rs' --glob 'Cargo.toml' --glob '!**/target/**' --glob '!**/node_modules/**'

echo "== Read changed source and related tests =="
git diff-tree --no-commit-id --name-only -r "$head" | while IFS= read -r file; do
  case "$file" in
    *.rs|Cargo.toml)
      echo "-- $file"
      cat -n "$file"
      ;;
  esac
done
fd -t f -e rs . | while IFS= read -r file; do
  if rg -q '(process_group|killpg|SIGTERM|SIGKILL|cancel|termination|descendant|reap)' "$file"; then
    echo "-- related $file"
    cat -n "$file"
  fi
done

echo "== Cargo target and dependency configuration =="
fd -t f 'Cargo.toml' . | while IFS= read -r file; do
  echo "-- $file"
  cat -n "$file"
done
rg -n -C 8 '(nix|libc|windows-sys|cfg\(unix\)|cfg\(windows\)|target_family)' \
  . --glob 'Cargo.toml' --glob '*.rs' --glob '!**/target/**'

echo "== Current-head-only PR state, checks, and reviews =="
gh pr view 866 --repo "$repo" \
  --json number,isDraft,headRefOid,baseRefName,baseRefOid,mergeStateStatus,reviewDecision,statusCheckRollup,url
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'
gh api graphql -F owner="ContextualWisdomLab" -F name="bandscope" -F number=866 -f query='
query($owner:String!, $name:String!, $number:Int!) {
  repository(owner:$owner,name:$name) {
    pullRequest(number:$number) {
      reviewThreads(first:100) { nodes { isResolved } }
      reviews(first:100) { nodes { author { login } state commit { oid } submittedAt } }
    }
  }
}' --jq '
  .data.repository.pullRequest as $pr |
  {
    unresolved_threads: ([$pr.reviewThreads.nodes[] | select(.isResolved == false)] | length),
    reviews: $pr.reviews.nodes
  }'

echo "== Whitespace and final exact-head confirmation =="
git diff --check "${head}^" "$head"
test "$(git rev-parse HEAD)" = "$head"

Length of output: 134454


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="4e0aa5d74c3b1dc86e47227b3abd262e536859f1"
red=$(git rev-list --all | grep -i '^af94694f' | head -n 1)
log="/tmp/coderabbit-shell-logs/shell-output-utvM86"
source="apps/desktop/src-tauri/src/main.rs"
contract_test="apps/desktop/src-tauri/tests/analysis_job_cancellation_contract.rs"

test "$(git rev-parse HEAD)" = "$head"

echo "== Exact RED and fix patches, focused =="
for commit in "$red" "$head"; do
  echo "-- $commit"
  git show --format=fuller --find-renames --unified=220 "$commit" -- \
    "$source" "$contract_test" \
    apps/desktop/src-tauri/Cargo.toml \
    docs/product-technical-gap-baseline.md \
    docs/doctoring/audio-resource-policy.md \
    CHANGELOG.md
done

echo "== Parent to exact-head delta, focused =="
git diff --find-renames --unified=220 "${head}^" "$head" -- \
  "$source" "$contract_test" apps/desktop/src-tauri/Cargo.toml

echo "== Full relevant functions and all termination call sites =="
sed -n '1,90p' "$source" | cat -n
sed -n '590,785p' "$source" | cat -n
rg -n -C 10 'terminate_analysis_process\s*\(|configure_analysis_process\s*\(|process\.kill\(|process\.wait\(|try_wait\(' \
  "$source" "$contract_test"

echo "== Test type and runtime fixture inventory =="
cat -n "$contract_test"
rg -n -C 8 \
  '(Command::new|process_group|setsid|fork|sleep|spawn|killpg|SIGKILL|descendant|include_str!)' \
  apps/desktop/src-tauri/tests apps/desktop/src-tauri/src \
  --glob '*.rs'

echo "== Extract first script's patch and process sections, if present =="
for marker in "== Resolve RED and show requested patches ==" "== Outline changed Rust files ==" "== Process group / signal / child lifecycle inventory ==" ; do
  line=$(rg -n -F "$marker" "$log" | head -n 1 | cut -d: -f1 || true)
  if [ -n "$line" ]; then
    echo "-- $marker (line $line)"
    sed -n "${line},$((line + 650))p" "$log"
  fi
done

echo "== Exact head remains selected =="
test "$(git rev-parse HEAD)" = "$head"

Length of output: 167898


🤖 web_search executed:

Rust standard library std::os::unix::process::CommandExt process_group documentation supported platforms macOS Linux

Length of output: 9576


@seonghobae I found no new actionable issue in the requested scope at exact head 4e0aa5d74c3b1dc86e47227b3abd262e536859f1.

The Unix process-group setup is correct.

  • configure_analysis_process calls command.process_group(0) before command.spawn().
  • Rust documents process_group(0) as setting the child PID as its process-group ID.
  • The spawned Python analysis engine becomes the leader of a fresh process group.
  • Normal descendants retain that process group unless they explicitly change it.

The termination path is correct for Linux and macOS.

  • terminate_analysis_process derives the group ID from the native-owned Child.
  • It calls posix_kill(-process_group_id, SIGKILL).
  • The negative ID selects the owned process group.
  • A successful group signal is followed by process.wait().
  • If group signaling fails or the PID conversion fails, the code falls back to process.kill() and process.wait().

The platform guards are correct.

  • CommandExt and c_int are imported only under #[cfg(unix)].
  • SIGKILL, posix_kill, and group termination compile only on Linux and macOS.
  • Windows does not compile Unix-specific APIs or FFI declarations.
  • Windows remains direct-child-only. This change does not claim Windows descendant containment.

The lifecycle behavior is bounded as intended.

  • Cancellation, timeout, stdin-write failure, and try_wait failure all route through terminate_analysis_process.
  • The runner does not bypass the helper with a direct process.kill() call.
  • The helper waits for the directly owned child before the caller joins the output-reader threads.
  • A descendant that creates a new process group can escape this Unix group boundary. The implementation and documentation limit the claim to descendants that retain the inherited group.

The RED contract matches the implementation. The test verifies pre-spawn group setup, negative-group signaling, helper-only termination, and direct-child reaping. It is a source-contract test. It does not provide runtime process-tree evidence.

git diff --check reported no whitespace errors. I used static inspection and Rust standard-library documentation. Current exact-head build and security checks were queued or in progress. The PR remains Draft and blocked.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Please verify final exact head 143f04b86d7269711e912c9754f99d3399cc4022. Source delta is unchanged from reviewed 4e0aa5d…; review the documentation descendants for claim accuracy against that implementation and confirm no new actionable source/docs finding. Keep the boundary narrow: Linux/macOS ordinary inherited process-group descendants only; Windows still direct-child-only pending a race-free Job Object; no runtime/full-length-audio cleanup or peak-resource claim.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="143f04b86d7269711e912c9754f99d3399cc4022"
source="apps/desktop/src-tauri/src/main.rs"
contract_test="apps/desktop/src-tauri/tests/analysis_job_cancellation_contract.rs"

echo "== Exact checkout =="
git rev-parse HEAD
git status --short
test "$(git rev-parse HEAD)" = "$head"
git show -s --format='%H%n%P%n%s%n%b' "$head"

echo "== Documentation-descendant history from reviewed source fix =="
git log --format='%H%n%P%n%s%n%b' --ancestry-path \
  4e0aa5d74c3b1dc86e47227b3abd262e536859f1.."$head"

echo "== Files changed after the reviewed source fix =="
git diff --name-status 4e0aa5d74c3b1dc86e47227b3abd262e536859f1 "$head"

echo "== Source containment implementation and all cleanup call sites =="
ast-grep outline "$source" --items all
rg -n -A110 -B25 \
  '(configure_analysis_process|terminate_analysis_process|process_group|posix_kill|SIGKILL|process\.kill\(|process\.wait\(|try_wait|cancel|timeout|stdin)' \
  "$source"
cat -n "$contract_test"

echo "== Documentation claims =="
for file in \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md
do
  if [ -f "$file" ]; then
    echo "-- $file"
    rg -n -i -C 12 \
      '(process.group|process group|descendant|cancellation|cancel|SIGKILL|reap|Windows|Job Object|job object|Linux|macOS|pipe|handle|temporary|cleanup|full.length|full-length|RSS|VRAM|runtime|containment)' \
      "$file" || true
  fi
done

echo "== Current source/docs diff and whitespace =="
git diff --find-renames --unified=120 \
  4e0aa5d74c3b1dc86e47227b3abd262e536859f1 "$head" -- \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md
git diff --check 4e0aa5d74c3b1dc86e47227b3abd262e536859f1 "$head"

echo "== Exact-head PR state only =="
gh pr view 866 --repo "$repo" \
  --json isDraft,headRefOid,baseRefOid,mergeStateStatus,reviewDecision,statusCheckRollup,url
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'

echo "== Reconfirm exact checkout =="
test "$(git rev-parse HEAD)" = "$head"

Length of output: 201746


@seonghobae I found no new actionable source or documentation finding at exact head 143f04b86d7269711e912c9754f99d3399cc4022.

The documentation descendants match the reviewed implementation.

  • configure_analysis_process applies process_group(0) before Command::spawn().
  • On Linux and macOS, terminate_analysis_process signals -process.id() with SIGKILL.
  • The helper then reaps the directly owned child.
  • If group signaling fails, the helper falls back to direct-child kill and reap.

The documents keep the containment boundary narrow.

  • They cover only ordinary descendants that retain the inherited Unix process group.
  • They exclude descendants that change process group or session.
  • They state that Windows remains direct-child-only.
  • They require a race-free Windows Job Object creation and assignment path before any Windows process-tree claim.

The documents also preserve the unproven acceptance work.

  • They do not claim runtime descendant cleanup.
  • They do not claim inherited pipe or handle release.
  • They do not claim child-created temporary-artifact cleanup.
  • They do not claim cancellation-latency bounds.
  • They do not claim decoder, process RSS, or VRAM peak-resource bounds.
  • They require rights-cleared full-length audio measurements for those conditions.

git diff --check reported no whitespace errors. I used static inspection only.

PR #866 remains Draft and blocked. The exact-head platform, CI, security, and review checks were still queued or in progress at inspection. I did not use predecessor review or check evidence as current-head evidence.

You are interacting with an AI system.

@seonghobae seonghobae 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.

P1 Resource Admission subprocess-containment gap on the current exact head: import_youtube_url still hands bandscope_analysis.youtube to bandscope_desktop_core::wait_for_process_output, and that helper kills/reaps only the direct child on try_wait failure or timeout before joining stdout/stderr reader threads. youtube.py uses yt-dlp with FFmpegExtractAudio, so an inherited helper process can outlive the Python child; if it retains a pipe/handle, the reader join can remain blocked after the advertised timeout. The Unix process-group repair in run_analysis_engine therefore does not cover the YouTube intake path. Add a RED contract for this path, then make the owned YouTube subprocess use the same pre-spawn containment/termination semantics (prefer one canonical helper rather than duplicating security-sensitive process control). Keep the claim narrow: ordinary Unix descendants that retain the group; Windows still needs a race-free Job Object boundary. Do not treat current analysis-runner containment as whole Resource Admission process-tree containment.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Please review exact 81a489026ce26fd14cf38bf1ea334e85d37346ee, especially the new real subprocess regression apps/desktop/core/tests/youtube_process_containment.rs and the Unix containment change in bandscope_desktop_core::wait_for_process_output. Check timeout/error termination ordering, inherited stdout/stderr liveness, process-group targeting/fallback/reap semantics, portability, and whether the helper creates a broader process authority than intended. Windows descendant containment is intentionally not claimed.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="81a489026ce26fd14cf38bf1ea334e85d37346ee"
core_dir="apps/desktop/core"
test_file="apps/desktop/core/tests/youtube_process_containment.rs"

echo "== Exact checkout =="
git rev-parse HEAD
git status --short
test "$(git rev-parse HEAD)" = "$head"
git show -s --format='%H%n%P%n%s%n%b' "$head"

echo "== Exact-head changed files and focused patch =="
git diff-tree --no-commit-id --name-status -r "$head"
git show --format=fuller --find-renames --unified=220 "$head" -- \
  "$core_dir" \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md

echo "== Core source and test structure =="
ast-grep outline "$core_dir" --items all
ast-grep outline "$test_file" --items all

echo "== wait_for_process_output and process termination implementation =="
rg -n -A190 -B45 \
  '(fn wait_for_process_output|wait_for_process_output\s*\(|terminate.*process|process_group|CommandExt|SIGKILL|kill\(|wait\(|try_wait|Child|stdout|stderr|JoinHandle|recv_timeout|timeout|cfg\()' \
  "$core_dir" \
  --glob '*.rs' --glob 'Cargo.toml'

echo "== Full containment regression =="
cat -n "$test_file"

echo "== Call sites and all direct child lifecycle operations =="
rg -n -C 14 \
  '(wait_for_process_output\s*\(|\.kill\(\)|\.wait\(\)|\.try_wait\(\)|process_group\(|posix_kill|SIGKILL|ChildStdout|ChildStderr|thread::spawn|join\(\))' \
  "$core_dir" \
  --glob '*.rs'

echo "== Platform and dependency configuration =="
fd -t f -a 'Cargo.toml' "$core_dir" -x sh -c 'echo "-- $1"; cat -n "$1"' sh {}
rg -n -C 10 \
  '(cfg\(unix\)|cfg\(windows\)|target_os|CommandExt|libc|nix|windows-sys|process_group)' \
  "$core_dir" \
  --glob '*.rs' --glob 'Cargo.toml'

echo "== Related subprocess tests and fixture behavior =="
fd -t f -e rs "$core_dir" | sort | while IFS= read -r file; do
  if rg -q '(wait_for_process_output|process_group|descendant|stdout|stderr|timeout|youtube_process)' "$file"; then
    echo "-- $file"
    rg -n -A90 -B20 \
      '(wait_for_process_output|process_group|descendant|stdout|stderr|timeout|SIGKILL|sleep|Command::new)' \
      "$file"
  fi
done

echo "== Documentation claim audit =="
for file in \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md
do
  if [ -f "$file" ]; then
    echo "-- $file"
    rg -n -i -C 10 \
      '(wait_for_process_output|youtube|process.group|process group|descendant|timeout|stdout|stderr|pipe|SIGKILL|reap|Windows|Job Object|containment|RSS|VRAM)' \
      "$file" || true
  fi
done

echo "== Current exact-head state only =="
gh pr view 866 --repo "$repo" \
  --json isDraft,headRefOid,baseRefOid,mergeStateStatus,reviewDecision,statusCheckRollup,url
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'
gh api graphql -F owner="ContextualWisdomLab" -F name="bandscope" -F number=866 -f query='
query($owner:String!, $name:String!, $number:Int!) {
  repository(owner:$owner,name:$name) {
    pullRequest(number:$number) {
      reviewThreads(first:100) { nodes { isResolved } }
      reviews(first:100) { nodes { author { login } state commit { oid } submittedAt } }
    }
  }
}' --jq '
  .data.repository.pullRequest as $pr |
  {
    unresolved_threads: ([$pr.reviewThreads.nodes[] | select(.isResolved == false)] | length),
    reviews: $pr.reviews.nodes
  }'

echo "== Whitespace and final exact-head confirmation =="
git diff --check "${head}^" "$head"
test "$(git rev-parse HEAD)" = "$head"

Length of output: 4100


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="81a489026ce26fd14cf38bf1ea334e85d37346ee"
core_dir="apps/desktop/core"
test_file="apps/desktop/core/tests/youtube_process_containment.rs"

echo "== Requested commit availability and identity =="
git cat-file -e "${head}^{commit}"
git show -s --format='%H%n%P%n%s%n%b' "$head"
parent="$(git rev-parse "${head}^")"
printf 'parent=%s\n' "$parent"
printf 'checked_out_head=%s\n' "$(git rev-parse HEAD)"

echo "== Requested exact-commit changed files and patch =="
git diff-tree --no-commit-id --name-status -r "$head"
git show --format=fuller --find-renames --unified=240 "$head" -- \
  "$core_dir" \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md

echo "== Exact source and regression at requested commit =="
git show "${head}:${test_file}" | nl -ba
git ls-tree -r --name-only "$head" "$core_dir" | sort

echo "== Locate implementation files at requested commit =="
git grep -n -E \
  '(fn wait_for_process_output|wait_for_process_output[[:space:]]*\(|process_group|CommandExt|SIGKILL|posix_kill|ChildStdout|ChildStderr)' \
  "$head" -- "$core_dir" ':(exclude)**/target/**' || true

echo "== Relevant implementation excerpts at requested commit =="
while IFS=: read -r file line rest; do
  case "$file" in
    *:*)
      path="${file#${head}:}"
      start=$(( line > 70 ? line - 70 : 1 ))
      end=$(( line + 210 ))
      echo "-- ${path}:${line}"
      git show "${head}:${path}" | nl -ba | sed -n "${start},${end}p"
      ;;
  esac
done < <(
  git grep -n -E \
    '(fn wait_for_process_output|fn terminate|process_group|posix_kill)' \
    "$head" -- "$core_dir" || true
)

echo "== Lifecycle call-site inventory at requested commit =="
git grep -n -E \
  '(wait_for_process_output[[:space:]]*\(|\.kill\(\)|\.wait\(\)|\.try_wait\(\)|process_group\(|posix_kill|SIGKILL|thread::spawn|\.join\(\))' \
  "$head" -- "$core_dir" ':(exclude)**/target/**' || true

echo "== Platform imports and Cargo configuration at requested commit =="
git grep -n -E \
  '(cfg\(unix\)|cfg\(windows\)|target_os|CommandExt|libc|nix|windows-sys|process_group)' \
  "$head" -- "$core_dir" -- '*.rs' 'Cargo.toml' || true
git ls-tree -r --name-only "$head" "$core_dir" | grep '/Cargo.toml$' | while IFS= read -r file; do
  echo "-- $file"
  git show "${head}:${file}" | nl -ba
done

echo "== Related tests at requested commit =="
git ls-tree -r --name-only "$head" "$core_dir" \
  | grep -E '\.rs$' \
  | while IFS= read -r file; do
      if git show "${head}:${file}" | rg -q \
        '(wait_for_process_output|process_group|descendant|stdout|stderr|timeout|youtube_process)'; then
        echo "-- $file"
        git show "${head}:${file}" | nl -ba | rg -n -A90 -B20 \
          '(wait_for_process_output|process_group|descendant|stdout|stderr|timeout|SIGKILL|sleep|Command::new)' || true
      fi
    done

echo "== Documentation claims at requested commit =="
for file in \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md
do
  if git cat-file -e "${head}:${file}" 2>/dev/null; then
    echo "-- $file"
    git show "${head}:${file}" | nl -ba | rg -n -i -C 10 \
      '(wait_for_process_output|youtube|process.group|process group|descendant|timeout|stdout|stderr|pipe|SIGKILL|reap|Windows|Job Object|containment|RSS|VRAM)' || true
  fi
done

echo "== Requested commit whitespace =="
git diff --check "$parent" "$head"

echo "== GitHub requested-commit check runs and PR ref state =="
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'
gh pr view 866 --repo "$repo" \
  --json isDraft,headRefOid,baseRefOid,mergeStateStatus,reviewDecision,url

Length of output: 33147


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="81a489026ce26fd14cf38bf1ea334e85d37346ee"
parent="$(git rev-parse "${head}^")"
core_dir="apps/desktop/core"
source="apps/desktop/core/src/lib.rs"
test_file="apps/desktop/core/tests/youtube_process_containment.rs"

echo "== Exact commit and file presence =="
git show -s --format='%H%n%P%n%s' "$head"
printf 'checkout=%s\n' "$(git rev-parse HEAD)"
git cat-file -e "${head}:${source}"
git cat-file -e "${head}:${test_file}"
git diff --name-status "$parent" "$head"

echo "== Exact test lineage and complete regression =="
git log --format='%H %P %s' --all -- "$test_file" | head -n 20
git log -p -n 8 --format=fuller -- "$test_file"
git show "${head}:${test_file}" | cat -n

echo "== Exact implementation: imports, helpers, and wait function =="
git show "${head}:${source}" | cat -n | sed -n '1,75p'
git show "${head}:${source}" | cat -n | sed -n '285,435p'

echo "== Exact implementation symbol and call-site inventory =="
git grep -n -E \
  '(configure_owned_process|terminate_owned_process|wait_for_process_output|\.kill\(\)|\.wait\(\)|\.try_wait\(\)|process_group\(|posix_kill|SIGKILL|thread::spawn|\.join\(\))' \
  "$head" -- "$core_dir" -- '*.rs' || true

echo "== All commands that call wait_for_process_output =="
git grep -n -E 'wait_for_process_output[[:space:]]*\(' \
  "$head" -- . -- '*.rs' || true

echo "== Exact Cargo manifests and platform gates =="
git ls-tree -r --name-only "$head" "$core_dir" | grep -E '(^|/)Cargo\.toml$' | while IFS= read -r file; do
  echo "-- $file"
  git show "${head}:${file}" | cat -n
done
git grep -n -E \
  '(cfg\(unix\)|cfg\(windows\)|target_os|CommandExt|libc|nix|windows-sys|process_group)' \
  "$head" -- "$core_dir" -- '*.rs' 'Cargo.toml' || true

echo "== Related exact tests =="
git ls-tree -r --name-only "$head" "$core_dir" | grep -E '(^|tests/).*\.rs$' | while IFS= read -r file; do
  if git show "${head}:${file}" | rg -q \
    '(wait_for_process_output|configure_owned_process|terminate_owned_process|process_group|descendant|stdout|stderr)'; then
    echo "-- $file"
    git show "${head}:${file}" | cat -n | rg -n -A100 -B25 \
      '(wait_for_process_output|configure_owned_process|terminate_owned_process|process_group|descendant|stdout|stderr|timeout|SIGKILL|Command::new)' || true
  fi
done

echo "== Requested commit documentation claim matches =="
for file in \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md
do
  if git cat-file -e "${head}:${file}" 2>/dev/null; then
    echo "-- $file"
    git show "${head}:${file}" | cat -n | rg -n -i -C 8 \
      '(youtube|process.group|process group|descendant|timeout|stdout|stderr|pipe|SIGKILL|reap|Windows|Job Object|containment)' || true
  fi
done

echo "== Whitespace and requested-commit GitHub evidence =="
git diff --check "$parent" "$head"
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'
gh pr view 866 --repo "$repo" \
  --json isDraft,headRefOid,baseRefOid,mergeStateStatus,reviewDecision,url

Length of output: 124266


@seonghobaeCHANGES_REQUESTED for exact commit 81a489026ce26fd14cf38bf1ea334e85d37346ee.

  • P1 — Use matching platform guards for the containment regression and group termination. In apps/desktop/core/src/lib.rs, Line 443 configures process_group(0) on every #[cfg(unix)] target. However, Lines 454-462 compile group-targeted SIGKILL only on Linux and macOS. In apps/desktop/core/tests/youtube_process_containment.rs, Line 1 enables the real descendant-and-inherited-pipe regression on every Unix target.

    On another Unix target, such as FreeBSD, wait_for_process_output kills and reaps only the direct shell process. The background sleep still holds inherited stdout and stderr. Lines 554-555 then join readers that cannot reach EOF until that descendant exits. The regression therefore exceeds its one-second limit and the timeout path is not bounded by containment.

    Either implement the same negative-process-group kill() path for every Unix target where CommandExt::process_group(0) and the test apply, or restrict the group configuration and youtube_process_containment.rs to #[cfg(any(target_os = "linux", target_os = "macos"))]. The latter matches the documented product scope.

The Linux/macOS behavior is otherwise correct.

  • wait_for_process_output creates the process group before spawn.
  • On timeout and try_wait error, it terminates the owned boundary before joining stdout and stderr readers.
  • A successful group signal is followed by direct-child reap.
  • If group signaling fails, direct-child kill/reap remains the fallback.
  • The real regression uses a background descendant that retains inherited pipes.
  • The helper derives the signal target only from the native-owned Child. It does not accept a renderer-controlled PID or generic process authority.
  • The implementation does not claim Windows descendant containment.

git diff --check found no whitespace error. I used static inspection only. The requested commit is no longer the current PR head. Its workflows were cancelled after supersession, so they do not provide verification evidence for this review.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Please re-review final exact 91b64cf72977e3483723537a1385dc72c7402439. The semantic source delta is RED 65c94f72… → production 81a48902…, plus the shell-gate regression and code-current baseline/CHANGELOG/ARCHITECTURE/app-security/doctoring descendants. Focus on the timed YouTube importer: ordinary descendant pipe-holder termination before reader joins, process-group setup/targeting/fallback/reap semantics, portability, and claim boundaries. Also check the documented remaining finding that analysis and YouTube paths still duplicate the low-level Unix containment primitive and must converge before protected adoption. Windows process-tree containment is not claimed.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="91b64cf72977e3483723537a1385dc72c7402439"
source_red_prefix="65c94f72"
source_fix="81a489026ce26fd14cf38bf1ea334e85d37346ee"
core_dir="apps/desktop/core"
core_source="apps/desktop/core/src/lib.rs"
containment_test="apps/desktop/core/tests/youtube_process_containment.rs"

echo "== Exact checkout =="
git rev-parse HEAD
git status --short
test "$(git rev-parse HEAD)" = "$head"
git show -s --format='%H%n%P%n%s%n%b' "$head"

echo "== Resolve requested RED and show lineage =="
red="$(git rev-list --all | grep -i "^${source_red_prefix}" | head -n1 || true)"
printf 'red=%s\n' "$red"
test -n "$red"
git log --format='%H %P %s' --ancestry-path "${source_fix}..${head}"
for commit in "$red" "$source_fix" "$head"; do
  echo "-- $commit"
  git show -s --format='%H%n%P%n%s%n%b' "$commit"
  git diff-tree --no-commit-id --name-status -r "$commit"
done

echo "== Final delta after production fix =="
git diff --name-status "$source_fix" "$head"
git diff --find-renames --unified=180 "$source_fix" "$head" -- \
  "$core_dir" \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md ARCHITECTURE.md TRACEABILITY.md \
  .github

echo "== Source RED and production patches =="
for commit in "$red" "$source_fix"; do
  echo "-- $commit"
  git show --format=fuller --find-renames --unified=220 "$commit" -- \
    "$core_source" "$containment_test" "$core_dir" .github
done

echo "== Source structure and lifecycle implementation at final head =="
ast-grep outline "$core_source" --items all
git show "${head}:${core_source}" | nl -ba | sed -n '1,90p'
git show "${head}:${core_source}" | nl -ba | sed -n '270,590p'
git grep -n -E \
  '(wait_for_process_output[[:space:]]*\(|configure_owned_process|terminate_owned_process|process_group\(|posix_kill|SIGKILL|\.kill\(\)|\.wait\(\)|\.try_wait\(\)|thread::spawn|\.join\(\)|recv_timeout)' \
  "$head" -- "$core_dir" -- '*.rs' || true

echo "== Containment and shell-gate regressions at final head =="
git cat-file -e "${head}:${containment_test}"
git show "${head}:${containment_test}" | nl -ba
git ls-tree -r --name-only "$head" .github "$core_dir" \
  | grep -Ei '(shell|gate|containment|youtube.*process|process.*containment)' \
  | while IFS= read -r file; do
      echo "-- $file"
      git show "${head}:${file}" | nl -ba
    done

echo "== Platform guards and manifest configuration =="
git grep -n -E \
  '(cfg\(unix\)|cfg\(windows\)|target_os|target_family|CommandExt|process_group|posix_kill|SIGKILL|libc|nix|windows-sys)' \
  "$head" -- "$core_dir" -- '*.rs' 'Cargo.toml' || true
git ls-tree -r --name-only "$head" "$core_dir" \
  | grep -E '(^|/)Cargo\.toml$' \
  | while IFS= read -r file; do
      echo "-- $file"
      git show "${head}:${file}" | nl -ba
    done

echo "== Callers and error/timeout ordering =="
git grep -n -E 'wait_for_process_output[[:space:]]*\(' "$head" -- . -- '*.rs' || true
git grep -n -E \
  '(timeout|timed out|recv_timeout|try_wait|reader|stdout|stderr|join|terminate_owned_process)' \
  "$head" -- "$core_dir" -- '*.rs' || true

echo "== Documentation claim audit at final head =="
for file in \
  docs/product-technical-gap-baseline.md \
  docs/doctoring/audio-resource-policy.md \
  docs/security/app-security.md \
  CHANGELOG.md \
  ARCHITECTURE.md \
  TRACEABILITY.md
do
  if git cat-file -e "${head}:${file}" 2>/dev/null; then
    echo "-- $file"
    git show "${head}:${file}" | nl -ba | rg -n -i -C 12 \
      '(youtube|process.group|process group|descendant|pipe|stdout|stderr|timeout|SIGKILL|reap|Linux|macOS|Windows|Job Object|duplicate|duplicat|converg|RSS|VRAM|runtime|cleanup)' || true
  fi
done

echo "== Exact-head PR status, checks, threads, reviews =="
gh pr view 866 --repo "$repo" \
  --json isDraft,headRefOid,baseRefName,baseRefOid,mergeStateStatus,reviewDecision,statusCheckRollup,url
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'
gh api graphql -F owner="ContextualWisdomLab" -F name="bandscope" -F number=866 -f query='
query($owner:String!, $name:String!, $number:Int!) {
  repository(owner:$owner,name:$name) {
    pullRequest(number:$number) {
      reviewThreads(first:100) { nodes { isResolved } }
      reviews(first:100) { nodes { author { login } state commit { oid } submittedAt } }
    }
  }
}' --jq '
  .data.repository.pullRequest as $pr |
  {
    unresolved_threads: ([$pr.reviewThreads.nodes[] | select(.isResolved == false)] | length),
    reviews: $pr.reviews.nodes
  }'

echo "== Whitespace and final confirmation =="
git diff --check "${head}^" "$head"
test "$(git rev-parse HEAD)" = "$head"

Length of output: 99286


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/bandscope"
head="91b64cf72977e3483723537a1385dc72c7402439"
core_dir="apps/desktop/core"
core_source="apps/desktop/core/src/lib.rs"
containment_test="apps/desktop/core/tests/youtube_process_containment.rs"
runtime_test="apps/desktop/src-tauri/tests/youtube_process_containment_runtime.rs"

test "$(git rev-parse HEAD)" = "$head"

echo "== Complete current containment implementation =="
git show "${head}:${core_source}" | cat -n | sed -n '430,570p'

echo "== All core process lifecycle operations and call sites =="
git grep -n -E \
  '(configure_owned_process|terminate_owned_process|wait_for_process_output|process_group\(|posix_kill|SIGKILL|\.kill\(\)|\.wait\(\)|\.try_wait\(\)|thread::spawn|\.join\(\)|read_to_end)' \
  "$head" -- "$core_dir" -- '*.rs' || true

echo "== Containment test after final descendants =="
git show "${head}:${containment_test}" | cat -n

echo "== Runtime/shell-gate test after final descendants =="
git cat-file -e "${head}:${runtime_test}"
git show "${head}:${runtime_test}" | cat -n
git grep -n -E \
  '(youtube_process_containment_runtime|youtube_process_containment|process containment|containment runtime|--test)' \
  "$head" -- .github apps scripts package.json Cargo.toml 2>/dev/null || true

echo "== Platform guard and crate configuration audit =="
git grep -n -E \
  '(cfg\(unix\)|cfg\(any\(target_os = "linux", target_os = "macos"\)\)|cfg\(all\(target_os|cfg\(windows\)|CommandExt|process_group|posix_kill|SIGKILL)' \
  "$head" -- "$core_dir" apps/desktop/src-tauri -- '*.rs' 'Cargo.toml' || true
git show "${head}:apps/desktop/core/Cargo.toml" | cat -n
git show "${head}:apps/desktop/src-tauri/Cargo.toml" | cat -n

echo "== Every current caller and caller setup =="
git grep -n -E 'wait_for_process_output[[:space:]]*\(' "$head" -- . -- '*.rs' || true
git grep -n -A45 -B25 'wait_for_process_output[[:space:]]*\(' \
  "$head" -- apps/desktop/core apps/desktop/src-tauri -- '*.rs' || true

echo "== Analysis-runner duplicate primitive comparison =="
git grep -n -A90 -B20 \
  '(configure_analysis_process|terminate_analysis_process|process_group\(|posix_kill|SIGKILL)' \
  "$head" -- apps/desktop/src-tauri/src -- '*.rs' || true

echo "== Documentation claims at current exact head =="
for file in \
  ARCHITECTURE.md CHANGELOG.md \
  docs/product-technical-gap-baseline.md \
  docs/security/app-security.md \
  docs/doctoring/youtube-process-containment.md \
  docs/doctoring/audio-resource-policy.md \
  TRACEABILITY.md
do
  if git cat-file -e "${head}:${file}" 2>/dev/null; then
    echo "-- $file"
    git show "${head}:${file}" | cat -n | rg -n -i -C 10 \
      '(youtube|process.group|process group|descendant|pipe|stdout|stderr|timeout|SIGKILL|reap|Linux|macOS|Windows|Job Object|duplicate|consolidat|RSS|VRAM|runtime|full.length|full-length)' || true
  fi
done

echo "== Exact final delta and whitespace =="
git diff --find-renames --unified=140 "${head}^" "$head" -- \
  apps/desktop docs ARCHITECTURE.md CHANGELOG.md TRACEABILITY.md .github
git diff --check "${head}^" "$head"

echo "== Current exact-head PR and check evidence =="
gh pr view 866 --repo "$repo" \
  --json isDraft,headRefOid,baseRefName,baseRefOid,mergeStateStatus,reviewDecision,statusCheckRollup,url
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'
gh api graphql -F owner="ContextualWisdomLab" -F name="bandscope" -F number=866 -f query='
query($owner:String!, $name:String!, $number:Int!) {
  repository(owner:$owner,name:$name) {
    pullRequest(number:$number) {
      reviewThreads(first:100) { nodes { isResolved } }
      reviews(first:100) { nodes { author { login } state commit { oid } submittedAt } }
    }
  }
}' --jq '
  .data.repository.pullRequest as $pr |
  {
    unresolved_threads: ([$pr.reviewThreads.nodes[] | select(.isResolved == false)] | length),
    reviews: $pr.reviews.nodes
  }'

echo "== Confirm exact checkout =="
test "$(git rev-parse HEAD)" = "$head"

Length of output: 120314


@seonghobaeCHANGES_REQUESTED for exact head 91b64cf72977e3483723537a1385dc72c7402439.

  • P1 — Align the Unix guards for process-group setup, group termination, and both containment regressions. In apps/desktop/core/src/lib.rs, Line 443, configure_owned_process calls command.process_group(0) on every #[cfg(unix)] target. In contrast, Line 454 enables negative-process-group SIGKILL only on Linux and macOS. Both apps/desktop/core/tests/youtube_process_containment.rs, Line 1, and apps/desktop/src-tauri/tests/youtube_process_containment_runtime.rs, Line 1, run on every Unix target.

    On another Unix target, the timeout path kills and reaps only the direct shell process. The five-second background sleep can retain stdout and stderr. The reader joins then remain live until that descendant exits. The under-one-second regression fails, and timeout cleanup is not bounded by the claimed containment behavior.

    Restrict process_group(0) and both pipe-holder regressions to #[cfg(any(target_os = "linux", target_os = "macos"))], or implement compatible group termination on every Unix target where the setup and regressions apply. The first option matches the documented product scope.

The Linux/macOS implementation is otherwise correct.

  • wait_for_process_output creates the owned process group before spawn.
  • Timeout and try_wait error paths call terminate_owned_process before stdout/stderr reader joins.
  • A successful negative-group signal is followed by direct-child reap.
  • Group-signal failure uses direct-child kill and reap as a fail-closed fallback.
  • The signal target derives only from the native-owned Child.
  • The helper does not accept renderer-controlled PIDs or generic process authority.
  • The documentation correctly excludes Windows process-tree containment and descendants that leave the inherited process group.
  • The documentation also correctly identifies the duplicated low-level Unix containment primitives as a required consolidation before protected adoption.
  • git diff --check found no whitespace error.

I used static inspection only. The PR remains Draft and blocked. Exact-head CI, security, and platform checks were queued or in progress at inspection.


✏️ Learnings added
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/bandscope/pull/866

Timestamp: 2026-09-08T04:16:34.587Z
Learning: In BandScope PR `#866`, the analysis runner and `apps/desktop/core/src/lib.rs` timed YouTube helper have equivalent low-level Unix process-group setup and termination primitives. Documentation requires consolidation into one GUI-independent owner before protected adoption to prevent security-semantics drift.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: medium Normal-priority or P2 work status: draft Draft pull request type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant