Fix clone_repo to Always Clone from Remote URL#368
Conversation
- Add TestResolveCloneSource with 3 unit tests for the simplified helper (2-param signature: no branch, always returns URL when configured) - Add T1-D: test_clone_uses_remote_when_branch_not_on_remote verifies that clone_repo raises RuntimeError (not silently clones local) when branch is missing on remote - Update test_strategy_proceed_bypasses_unpublished_guard → renamed to test_strategy_proceed_with_unpublished_branch_fails_from_remote to reflect correct post-fix behavior (RuntimeError expected) Tests fail against current codebase — confirms they are properly anchored.
…figured Remove the branch parameter and ls-remote check from _resolve_clone_source. When a remote URL is configured, it is now always used as the clone source — no fallback to local path based on branch availability or network reachability. The only legitimate local-path fallback remains: when no remote origin is configured at all. This eliminates two silent-failure bugs: - Branch not on remote (ls-remote exit 2) → was silently cloning local - Network timeout during ls-remote → was silently cloning local Both scenarios now surface as RuntimeError from git clone, making the isolation violation explicit rather than silent. Also update docstrings: module note scoped to clone_local strategy only; clone_repo docstring reflects the new deterministic remote-first behavior.
…ns_remote_url After _resolve_clone_source simplification, clone always targets the remote URL when configured. The test set up an empty bare remote and never pushed the local commit, causing git clone to fail (no 'main' on remote). Push the commit before cloning so the remote has the expected branch.
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit PR Review — Verdict: changes_requested (REQUEST_CHANGES unavailable on own PR; posting as COMMENT)
| remote URL when one is configured, so git clone fails (correctly) instead | ||
| of silently cloning local state. | ||
| """ | ||
| with patch( |
There was a problem hiding this comment.
[warning] tests: test_clone_uses_remote_when_branch_not_on_remote substantially overlaps with test_strategy_proceed_with_unpublished_branch_fails_from_remote (same fixture, branch, expected RuntimeError). The only unique value — asserting clone source — is undermined by the fragile index noted separately.
| clone_calls = [ | ||
| call | ||
| for call in spy.call_args_list | ||
| if call[0] and isinstance(call[0][0], list) and call[0][0][:2] == ["git", "clone"] |
There was a problem hiding this comment.
[warning] tests: Fragile positional index clone_calls[0][0][0][-2] extracts clone source without validating command list length; silently returns the wrong element if git clone gains/loses arguments (e.g. --no-hardlinks, --depth). Use a named extraction relative to known flags instead.
| source = tmp_path / "repo" | ||
| assert _resolve_clone_source(source, "") == str(source) | ||
|
|
||
| def test_url_returned_regardless_of_branch_existence(self, tmp_path: Path) -> None: |
There was a problem hiding this comment.
[info] tests: test_url_returned_regardless_of_branch_existence is redundant with test_returns_url_when_configured: both pass a non-empty URL and assert it is returned. _resolve_clone_source no longer accepts a branch parameter, so there is nothing to distinguish the two tests.
| @@ -1272,3 +1286,62 @@ def test_clone_result_remote_url_correct_after_remote_clone( | |||
| assert result["remote_url"] == str(bare_remote) | |||
There was a problem hiding this comment.
[info] cohesion: TestResolveCloneSource imports _resolve_clone_source inside each individual test method instead of at module scope, inconsistent with the import-locality pattern used elsewhere in the test suite.
| from autoskillit.workspace.clone import _resolve_clone_source | ||
|
|
||
| source = tmp_path / "repo" | ||
| assert _resolve_clone_source(source, "") == str(source) |
There was a problem hiding this comment.
[info] cohesion: test_url_returned_regardless_of_branch_existence name implies branch-parameter testing, but the function no longer accepts a branch parameter after simplification; the name is misleading.
| network reachability — when a remote URL is known, it is always | ||
| used as the clone source. The clone_local strategy bypasses this | ||
| function entirely (shutil.copytree, always local). | ||
| """ |
There was a problem hiding this comment.
[info] slop: Docstring for _resolve_clone_source is over-verbose for a one-liner function; four sentences including a redundant note about clone_local that duplicates the module docstring.
| """strategy='proceed' with unpublished branch now fails — remote is always used. | ||
|
|
||
| Previously 'proceed' bypassed the guard by silently falling back to the | ||
| local path. After the fix the clone always targets the remote, so a branch |
There was a problem hiding this comment.
[info] slop: Docstring contains historical 'Previously... After the fix' narrative; tests should document the current invariant, not a changelog.
| # T1-D | ||
| def test_clone_uses_remote_when_branch_not_on_remote( | ||
| self, local_with_remote: Path, bare_remote: Path | ||
| ) -> None: |
There was a problem hiding this comment.
[info] slop: Docstring contains historical 'Previously... After the fix' narrative; only the current invariant belongs in a test docstring.
…gile positional index Replaces clone_calls[0][0][0][-2] with a flag-aware walk of the git clone command arguments. The new extraction skips flag/value pairs (--branch, --depth, --no-hardlinks, etc.) and collects positional args, so the clone source is always positional[0] regardless of how many optional flags are present. Addresses reviewer findings: - Fragile positional index silently breaks if git clone args change - Makes the spy assertion robust, restoring the unique value of test_clone_uses_remote_when_branch_not_on_remote
…o_returns_remote_url The bare repo was initialized without --initial-branch=main, leaving HEAD pointing to master. The push used HEAD:main, so the bare repo had a main branch but a master HEAD. git clone then failed with "Remote branch master not found" because master didn't exist. Setting --initial-branch=main at init time aligns HEAD with the branch that gets pushed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, Headless Isolation (#404) ## Summary Integration rollup of **43 PRs** (#293–#406) consolidating **62 commits** across **291 files** (+27,909 / −6,040 lines). This release advances AutoSkillit from v0.2.0 to v0.3.1 with GitHub merge queue integration, sub-recipe composition, a PostToolUse output reformatter, headless session isolation guards, and comprehensive pipeline observability — plus 24 new bundled skills, 3 new MCP tools, and 47 new test files. --- ## Major Features ### GitHub Merge Queue Integration (#370, #362, #390) - New `wait_for_merge_queue` MCP tool — polls a PR through GitHub's merge queue until merged, ejected, or timed out (default 600s). Uses REST + GraphQL APIs with stuck-queue detection and auto-merge re-enrollment - New `DefaultMergeQueueWatcher` L1 service (`execution/merge_queue.py`) — never raises; all outcomes are structured results - `parse_merge_queue_response()` pure function for GraphQL queue entry parsing - New `auto_merge` ingredient in `implementation.yaml` and `remediation.yaml` — enrolls PRs in the merge queue after CI passes - Full queue-mode path added to `merge-prs.yaml`: detect queue → enqueue → wait → handle ejections → re-enter - `analyze-prs` skill gains Step 0.5 (merge queue detection) and Step 1.5 (CI/review eligibility filtering) ### Sub-Recipe Composition (#380) - Recipe steps can now reference sub-recipes via `sub_recipe` + `gate` fields — lazy-loaded and merged at validation time - Composition engine in `recipe/_api.py`: `_merge_sub_recipe()` inlines sub-recipe steps with safe name-prefixing and route remapping (`done` → parent's `on_success`, `escalate` → parent's `on_failure`) - `_build_active_recipe()` evaluates gate ingredients against overrides/defaults; dual validation runs on both active and combined recipes - First sub-recipe: `sprint-prefix.yaml` — triage → plan → confirm → dispatch workflow, gated by `sprint_mode` ingredient (hidden, default false) - Both `implementation.yaml` and `remediation.yaml` gain `sprint_entry` placeholder step - New semantic rules: `unknown-sub-recipe` (ERROR), `circular-sub-recipe` (ERROR) with DFS cycle detection ### PostToolUse Output Reformatter (#293, #405) - `pretty_output.py` — new 671-line PostToolUse hook that rewrites raw MCP JSON responses to Markdown-KV before Claude consumes them (30–77% token overhead reduction) - Dedicated formatters for 11 high-traffic tools (`run_skill`, `run_cmd`, `test_check`, `merge_worktree`, `get_token_summary`, etc.) plus a generic KV formatter for remaining tools - Pipeline vs. interactive mode detection via hook config file - Unwraps Claude Code's `{"result": "<json-string>"}` envelope before dispatching - 1,516-line test file with 40+ behavioral tests ### Headless Session Isolation (#359, #393, #397, #405, #406) - **Env isolation**: `build_sanitized_env()` strips `AUTOSKILLIT_PRIVATE_ENV_VARS` from subprocess environments, preventing `AUTOSKILLIT_HEADLESS=1` from leaking into test runners - **CWD path contamination defense**: `_inject_cwd_anchor()` anchors all relative paths to session CWD; `_validate_output_paths()` checks structured output tokens against CWD prefix; `_scan_jsonl_write_paths()` post-session scanner catches actual Write/Edit/Bash tool calls outside CWD - **Headless orchestration guard**: new PreToolUse hook blocks `run_skill`/`run_cmd`/`run_python` when `AUTOSKILLIT_HEADLESS=1`, enforcing Tier 1/Tier 2 nesting invariant - **`_require_not_headless()` server-side guard**: blocks 10 orchestration-only tools from headless sessions at the handler layer - **Unified error response contract**: `headless_error_result()` produces consistent 9-field responses; `_build_headless_error_response()` canonical builder for all failure paths in `tools_integrations.py` ### Cook UX Overhaul (#375, #363) - `open_kitchen` now accepts optional `name` + `overrides` — opens kitchen AND loads recipe in a single call - Pre-launch terminal preview with ANSI-colored flow diagram and ingredients table via new `cli/_ansi.py` module - `--dangerously-skip-permissions` warning banner with interactive confirmation prompt - Randomized session greetings from themed pools - Orchestrator prompt rewritten: recipe YAML no longer injected via `--append-system-prompt`; session calls `open_kitchen('{recipe_name}')` as first action - Conversational ingredient collection replaces mechanical per-field prompting --- ## New MCP Tools | Tool | Gate | Description | |------|------|-------------| | `wait_for_merge_queue` | Kitchen | Polls PR through GitHub merge queue (REST + GraphQL) | | `set_commit_status` | Kitchen | Posts GitHub Commit Status to a SHA for review-first gating | | `get_quota_events` | Ungated | Surfaces quota guard decisions from `quota_events.jsonl` | --- ## Pipeline Observability (#318, #341) - **`TelemetryFormatter`** (`pipeline/telemetry_fmt.py`) — single source of truth for all telemetry rendering; replaces dual-formatter anti-pattern. Four rendering modes: Markdown table, terminal table, compact KV (for PostToolUse hook) - `get_token_summary` and `get_timing_summary` gain `format` parameter (`"json"` | `"table"`) - `wall_clock_seconds` merged into token summary output — see duration alongside token counts in one call - **Telemetry clear marker**: `write_telemetry_clear_marker()` / `read_telemetry_clear_marker()` prevent token accounting drift on MCP server restart after `clear=True` - **Quota event logging**: `quota_check.py` hook now writes structured JSONL events (`cache_miss`, `parse_error`, `blocked`, `approved`) to `quota_events.jsonl` --- ## CI Watcher & Remote Resolution Fixes (#395, #406) - **`CIRunScope` value object** — carries `workflow` + `head_sha` scope; replaces bare `head_sha` parameter across all CI watcher signatures - **Workflow filter**: `wait_for_ci` and `get_ci_status` accept `workflow` parameter (falls back to project-level `config.ci.workflow`), preventing unrelated workflows (version bumps, labelers) from satisfying CI checks - **`FAILED_CONCLUSIONS` expanded**: `failure` → `{failure, timed_out, startup_failure, cancelled}` - **Canonical remote resolver** (`execution/remote_resolver.py`): `resolve_remote_repo()` with `REMOTE_PRECEDENCE = (upstream, origin)` — correctly resolves `owner/repo` after `clone_repo` sets `origin` to `file://` isolation URL - **Clone isolation fix**: `clone_repo` now always clones from remote URL (never local path); sets `origin=file:///<clone>` for isolation and `upstream=<real_url>` for push/CI operations --- ## PR Pipeline Gates (#317, #343) - **`pipeline/pr_gates.py`**: `is_ci_passing()`, `is_review_passing()`, `partition_prs()` — partitions PRs into eligible/CI-blocked/review-blocked with human-readable reasons - **`pipeline/fidelity.py`**: `extract_linked_issues()` (Closes/Fixes/Resolves patterns), `is_valid_fidelity_finding()` schema validation - **`check_pr_mergeable`** now returns `mergeable_status` field alongside boolean - **`release_issue`** gains `target_branch` + `staged_label` parameters for staged issue lifecycle on non-default branches (#392) --- ## Recipe System Changes ### Structural - `RecipeIngredient.hidden` field — excluded from ingredients table (used for internal flags like `sprint_mode`) - `Recipe.experimental` flag parsed from YAML - `_TERMINAL_TARGETS` moved to `schema.py` as single source of truth - `format_ingredients_table()` with sorted display order (required → auto-detect → flags → optional → constants) - Diagram rendering engine (~670 lines) removed from `diagrams.py` — rendering now handled by `/render-recipe` skill; format version bumped to v7 ### Recipe YAML Changes - **Deleted**: `audit-and-fix.yaml`, `batch-implementation.yaml`, `bugfix-loop.yaml` - **Renamed**: `pr-merge-pipeline.yaml` → `merge-prs.yaml` - **`implementation.yaml`**: merge queue steps, `auto_merge`/`sprint_mode` ingredients, `base_branch` default → `""` (auto-detect), CI workflow filter, `extract_pr_number` step - **`remediation.yaml`**: `topic` → `task` rename, merge queue steps, `dry_walkthrough` retries:3 with forward-only routing, `verify` → `test` rename - **`merge-prs.yaml`**: full queue-mode path, `open-integration-pr` step (replaces `create-review-pr`), post-PR mergeability polling, review cycle with `resolve-review` retries ### New Semantic Rules - `missing-output-patterns` (WARNING) — flags `run_skill` steps without `expected_output_patterns` - `unknown-sub-recipe` (ERROR) — validates sub-recipe references exist - `circular-sub-recipe` (ERROR) — DFS cycle detection - `unknown-skill-command` (ERROR) — validates skill names against bundled set - `telemetry-before-open-pr` (WARNING) — ensures telemetry step precedes `open-pr` --- ## New Skills (24) ### Architecture Lens Family (13) `arch-lens-c4-container`, `arch-lens-concurrency`, `arch-lens-data-lineage`, `arch-lens-deployment`, `arch-lens-development`, `arch-lens-error-resilience`, `arch-lens-module-dependency`, `arch-lens-operational`, `arch-lens-process-flow`, `arch-lens-repository-access`, `arch-lens-scenarios`, `arch-lens-security`, `arch-lens-state-lifecycle` ### Audit Family (5) `audit-arch`, `audit-bugs`, `audit-cohesion`, `audit-defense-standards`, `audit-tests` ### Planning & Diagramming (3) `elaborate-phase`, `make-arch-diag`, `make-req` ### Bug/Guard Lifecycle (2) `design-guards`, `verify-diag` ### Pipeline (1) `open-integration-pr` — creates integration PRs with per-PR details, arch-lens diagrams, carried-forward `Closes #N` references, and auto-closes collapsed PRs ### Sprint Planning (1 — gated by sub-recipe) `sprint-planner` — selects a focused, conflict-free sprint from a triage manifest --- ## Skill Modifications (Highlights) - **`analyze-prs`**: merge queue detection, CI/review eligibility filtering, queue-mode ordering - **`dry-walkthrough`**: Step 4.5 Historical Regression Check (git history mining + GitHub issue cross-reference) - **`review-pr`**: deterministic diff annotation via `diff_annotator.py`, echo-primary-obligation step, post-completion confirmation, degraded-mode narration - **`collapse-issues`**: content fidelity enforcement — per-issue `fetch_github_issue` calls, copy-mode body assembly (#388) - **`prepare-issue`**: multi-keyword dedup search, numbered candidate selection, extend-existing-issue flow - **`resolve-review`**: GraphQL thread auto-resolution after addressing findings (#379) - **`resolve-merge-conflicts`**: conflict resolution decision report with per-file log (#389) - **Cross-skill**: output tokens migrated to `key = value` format; code-index paths made generic with fallback notes; arch-lens references fully qualified; anti-prose guards at loop boundaries --- ## CLI & Hooks ### New CLI Commands - `autoskillit install` — plugin installation + cache refresh - `autoskillit upgrade` — `.autoskillit/scripts/` → `.autoskillit/recipes/` migration ### CLI Changes - `doctor`: plugin-aware MCP check, PostToolUse hook scanning, `--fix` flag removed - `init`: GitHub repo prompt, `.secrets.yaml` template, plugin-aware registration - `chefs-hat`: pre-launch banner, `--dangerously-skip-permissions` confirmation - `recipes render`: repurposed from generator to viewer (delegates to `/render-recipe`) - `serve`: server import deferred to after `configure_logging()` to prevent stdout corruption ### New Hooks - `branch_protection_guard.py` (PreToolUse) — denies `merge_worktree`/`push_to_remote` targeting protected branches - `headless_orchestration_guard.py` (PreToolUse) — blocks orchestration tools in headless sessions - `pretty_output.py` (PostToolUse) — MCP JSON → Markdown-KV reformatter ### Hook Infrastructure - `HookDef.event_type` field — registry now handles both PreToolUse and PostToolUse - `generate_hooks_json()` groups entries by event type - `_evict_stale_autoskillit_hooks` and `sync_hooks_to_settings` made event-type-agnostic --- ## Core & Config ### New Core Modules - `core/branch_guard.py` — `is_protected_branch()` pure function - `core/github_url.py` — `parse_github_repo()` + `normalize_owner_repo()` canonical parsers ### Core Type Expansions - `AUTOSKILLIT_PRIVATE_ENV_VARS` frozenset - `WORKER_TOOLS` / `HEADLESS_BLOCKED_UNGATED_TOOLS` split from `UNGATED_TOOLS` - `TOOL_CATEGORIES` — categorized listing for `open_kitchen` response - `CIRunScope` — immutable scope for CI watcher calls - `MergeQueueWatcher` protocol - `SkillResult.cli_subtype` + `write_path_warnings` fields - `SubprocessRunner.env` parameter ### Config - `safety.protected_branches`: `[main, integration, stable]` - `github.staged_label`: `"staged"` - `ci.workflow`: workflow filename filter (e.g., `"tests.yml"`) - `branching.default_base_branch`: `"integration"` → `"main"` - `ModelConfig.default`: `str | None` → `str = "sonnet"` --- ## Infrastructure & Release ### Version - `0.2.0` → `0.3.1` across `pyproject.toml`, `plugin.json`, `uv.lock` - FastMCP dependency: `>=3.0.2` → `>=3.1.1,<4.0` (#399) ### CI/CD Workflows - **`version-bump.yml`** (new) — auto patch-bumps `main` on integration PR merge, force-syncs integration branch one patch ahead - **`release.yml`** (new) — minor version bump + GitHub Release on merge to `stable` - **`codeql.yml`** (new) — CodeQL analysis for `stable` PRs (Python + Actions) - **`tests.yml`** — `merge_group:` trigger added; multi-OS now only for `stable` ### PyPI Readiness - `pyproject.toml`: `readme`, `license`, `authors`, `keywords`, `classifiers`, `project.urls`, `hatch.build.targets.sdist` inclusion list ### readOnlyHint Parallel Execution Fix - All MCP tools annotated `readOnlyHint=True` — enables Claude Code parallel tool execution (~7x speedup). One deliberate exception: `wait_for_merge_queue` uses `readOnlyHint=False` (actually mutates queue state) ### Tool Response Exception Boundary - `track_response_size` decorator catches unhandled exceptions and serializes them as `{"success": false, "subtype": "tool_exception"}` — prevents FastMCP opaque error wrapping ### SkillResult Subtype Normalization (#358) - `_normalize_subtype()` gate eliminates dual-source contradiction between CLI subtype and session outcome - Class 2 upward: `SUCCEEDED + error_subtype → "success"` (drain-race artifact) - Class 1 downward: `non-SUCCEEDED + "success" → "empty_result"` / `"missing_completion_marker"` / `"adjudicated_failure"` --- ## Test Coverage **47 new test files** (+12,703 lines) covering: | Area | Key Tests | |------|-----------| | Merge queue watcher state machine | `test_merge_queue.py` (226 lines) | | Clone isolation × CI resolution | `test_clone_ci_contract.py`, `test_remote_resolver.py` | | PostToolUse hook | `test_pretty_output.py` (1,516 lines, 40+ cases) | | Branch protection + headless guards | `test_branch_protection_guard.py`, `test_headless_orchestration_guard.py` | | Sub-recipe composition | 5 test files (schema, loading, validation, sprint mode × 2) | | Telemetry formatter | `test_telemetry_formatter.py` (281 lines) | | PR pipeline gates | `test_analyze_prs_gates.py`, `test_review_pr_fidelity.py` | | Diff annotator | `test_diff_annotator.py` (242 lines) | | Skill compliance | Output token format, genericization, loop-boundary guards | | Release workflows | Structural contracts for `version-bump.yml`, `release.yml` | | Issue content fidelity | Body-assembling skills must call `fetch_github_issue` per-issue | | CI watcher scope | `test_ci_params.py` — workflow_id query param composition | --- ## Consolidated PRs #293, #295, #314, #315, #316, #317, #318, #319, #323, #332, #336, #337, #338, #339, #341, #343, #351, #358, #359, #360, #361, #362, #363, #366, #368, #370, #375, #377, #378, #379, #380, #388, #389, #390, #391, #392, #393, #395, #396, #397, #399, #405, #406 --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
clone_repopreviously fell back to the local filesystem path as the clone source in two bug scenarios inside_resolve_clone_source: when a branch was not found on the remote (ls-remote exit code 2) and when a network timeout occurred during ls-remote. When a remote URL is configured, the clone source must always be the remote — never the local filesystem — to preserve the isolation guarantee.The fix simplifies
_resolve_clone_sourceto a single-expression function: remove thebranchparameter and the ls-remote network check, and always return the remote URL when one is available. The no-remote fallback to local path is retained as the only legitimate use of local as a clone source.Architecture Impact
Process Flow Diagram
%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; %% TERMINALS %% START([clone_repo called]) SUCCESS([return clone_path + remote_url]) WARN_UNCOMMITTED([return uncommitted_changes dict]) WARN_UNPUBLISHED([return unpublished_branch dict]) WARN_REWRITE([return remote_url_rewrite_failed dict]) ERROR_VAL([raise ValueError]) ERROR_RT([raise RuntimeError]) subgraph Resolution ["Source Resolution"] direction TB AutoDetect{"source_dir<br/>━━━━━━━━━━<br/>empty?"} CwdDetect["detect_source_dir(cwd)<br/>━━━━━━━━━━<br/>git rev-parse --show-toplevel"] ValidateDir{"Path.is_dir()?"} BranchDetect{"branch<br/>━━━━━━━━━━<br/>empty?"} DetectBranch["detect_branch(source)<br/>━━━━━━━━━━<br/>git rev-parse --abbrev-ref HEAD"] end subgraph Guards ["Strategy Guards"] direction TB StratCheck{"strategy<br/>━━━━━━━━━━<br/>== ''?"} UncommittedGuard["detect_uncommitted_changes<br/>━━━━━━━━━━<br/>git status --porcelain"] DirtyCheck{"changes<br/>━━━━━━━━━━<br/>found?"} UnpublishedGuard["detect_unpublished_branch<br/>━━━━━━━━━━<br/>git ls-remote origin"] UnpubCheck{"branch missing<br/>━━━━━━━━━━<br/>on remote?"} end subgraph CloneSetup ["Clone Setup"] direction TB TimestampPath["compute clone_path<br/>━━━━━━━━━━<br/>runs_parent / run_name-timestamp"] GetRemoteURL["git remote get-url origin<br/>━━━━━━━━━━<br/>detected_url from source<br/>timeout=30s"] end subgraph StrategyBranch ["Strategy Branch"] direction TB StrategyCheck{"● strategy<br/>━━━━━━━━━━<br/>== clone_local?"} LocalCopy["shutil.copytree<br/>━━━━━━━━━━<br/>copy working tree"] ResolveSource["● _resolve_clone_source<br/>━━━━━━━━━━<br/>detected_url truthy?"] UseRemote["Use remote URL<br/>━━━━━━━━━━<br/>always when URL known<br/>— no ls-remote check"] UseLocal["Use local path<br/>━━━━━━━━━━<br/>only when no remote<br/>URL configured"] GitClone["git clone [--branch B]<br/>━━━━━━━━━━<br/>clone_source → clone_path"] CloneOK{"returncode<br/>━━━━━━━━━━<br/>== 0?"} end subgraph PostClone ["Post-Clone"] direction TB EffectiveURL["effective_url<br/>━━━━━━━━━━<br/>remote_url arg or detected_url"] RewriteOrigin["git remote set-url origin<br/>━━━━━━━━━━<br/>enforce effective_url"] RewriteOK{"rewrite<br/>━━━━━━━━━━<br/>succeeded?"} Decontam["decontaminate<br/>━━━━━━━━━━<br/>untrack + delete<br/>generated files"] end %% FLOW: Source Resolution %% START --> AutoDetect AutoDetect -->|"yes"| CwdDetect AutoDetect -->|"no"| ValidateDir CwdDetect --> ValidateDir ValidateDir -->|"false"| ERROR_VAL ValidateDir -->|"true"| BranchDetect BranchDetect -->|"yes"| DetectBranch BranchDetect -->|"no"| StratCheck DetectBranch --> StratCheck %% FLOW: Guards %% StratCheck -->|"yes"| UncommittedGuard StratCheck -->|"no (proceed / clone_local)"| TimestampPath UncommittedGuard --> DirtyCheck DirtyCheck -->|"yes"| WARN_UNCOMMITTED DirtyCheck -->|"no"| UnpublishedGuard UnpublishedGuard --> UnpubCheck UnpubCheck -->|"yes"| WARN_UNPUBLISHED UnpubCheck -->|"no"| TimestampPath %% FLOW: Clone Setup %% TimestampPath --> GetRemoteURL GetRemoteURL --> StrategyCheck %% FLOW: Strategy Branch %% StrategyCheck -->|"yes"| LocalCopy StrategyCheck -->|"no"| ResolveSource ResolveSource -->|"URL configured"| UseRemote ResolveSource -->|"no URL"| UseLocal UseRemote --> GitClone UseLocal --> GitClone LocalCopy --> EffectiveURL GitClone --> CloneOK CloneOK -->|"no"| ERROR_RT CloneOK -->|"yes"| EffectiveURL %% FLOW: Post-Clone %% EffectiveURL --> RewriteOrigin RewriteOrigin --> RewriteOK RewriteOK -->|"failed + caller supplied remote_url"| WARN_REWRITE RewriteOK -->|"ok or auto-detected"| Decontam Decontam --> SUCCESS %% CLASS ASSIGNMENTS %% class START,SUCCESS,WARN_UNCOMMITTED,WARN_UNPUBLISHED,WARN_REWRITE,ERROR_VAL,ERROR_RT terminal; class AutoDetect,BranchDetect,StratCheck,DirtyCheck,UnpubCheck,StrategyCheck,CloneOK,RewriteOK stateNode; class CwdDetect,DetectBranch,TimestampPath,GetRemoteURL,GitClone,LocalCopy,EffectiveURL,RewriteOrigin,Decontam handler; class UncommittedGuard,UnpublishedGuard detector; class ResolveSource stateNode; class UseRemote,UseLocal phase;Color Legend:
Closes #367
Implementation Plan
Plan file:
/home/talon/projects/autoskillit-runs/clone-fix-20260312-173207/temp/make-plan/clone_remote_fix_plan_2026-03-12_173652.mdToken Usage Summary
No token data accumulated for this pipeline run.
🤖 Generated with Claude Code via AutoSkillit