Skip to content

Implementation Plan: Staged Issue Lifecycle for Non-Default Branch PRs#392

Merged
Trecek merged 11 commits intointegrationfrom
pipeline-leaves-issues-open-and-releases-claims-prematurely/382
Mar 15, 2026
Merged

Implementation Plan: Staged Issue Lifecycle for Non-Default Branch PRs#392
Trecek merged 11 commits intointegrationfrom
pipeline-leaves-issues-open-and-releases-claims-prematurely/382

Conversation

@Trecek
Copy link
Collaborator

@Trecek Trecek commented Mar 15, 2026

Summary

The implementation pipeline fails to manage GitHub issue state correctly when PRs target
non-default branches (e.g., integration). Two bugs exist:

  1. implementation.yaml has no release_issue_success step — the in-progress label is
    never removed on the success path, leaving issues permanently marked "in-progress" after
    merge into integration.
  2. GitHub only auto-closes issues via "Closes #N" when PRs merge into the default branch
    (main). PRs targeting integration merge successfully but leave linked issues open with
    no indication of their actual status.

The fix introduces a three-point staged lifecycle:

  • Success into non-default branch → remove in-progress, apply staged (work is done, waiting for promotion to main)
  • Failure at any stage → remove in-progress only (leave open for retry)
  • Integration PR to main → carry forward "Closes #N" so issues auto-close (already handled by open-integration-pr)

Additionally, the open-pr skill now blocks PRs that target stable from any branch other than main.

Requirements

STAGE — Staged Label Lifecycle

  • REQ-STAGE-001: When a PR merges into integration, the pipeline must remove the in-progress label and apply a staged label to the linked issue.
  • REQ-STAGE-002: The staged label must be created automatically (via gh label create --force) if it does not already exist.
  • REQ-STAGE-003: The issue must remain open while the staged label is applied — it is only closed when the code reaches main.

CLOSE — Issue Close Behavior

  • REQ-CLOSE-001: Issues must only be closed when the associated code merges into main (the default branch).
  • REQ-CLOSE-002: The integration PR (merging integration → main) must carry forward all Closes #N references from the constituent PRs so GitHub auto-closes the issues.
  • REQ-CLOSE-003: If Closes #N auto-close does not trigger (e.g. due to reference format), the pipeline must explicitly close the issue via gh issue close after confirming the merge to main.

RELEASE — Claim Release

  • REQ-RELEASE-001: On the success path, release_issue must transition the issue from in-progress to staged when the target branch is not main.
  • REQ-RELEASE-002: On the success path targeting main, release_issue must remove in-progress and allow the issue to close normally.
  • REQ-RELEASE-003: On the failure path, release_issue must remove the in-progress label but must not apply staged, leaving the issue open for retry.

GUARD — Branch Enforcement

  • REQ-GUARD-001: PRs targeting stable must be blocked unless the source branch is main.
  • REQ-GUARD-002: The enforcement must be implemented via GitHub branch protection rules if possible, or via a PreToolUse hook or recipe-level guard as a fallback.
  • REQ-GUARD-003: The open-pr skill or merge_worktree tool must reject attempts to target stable from any branch other than main with a clear error message.

Architecture Impact

State Lifecycle Diagram

%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 70, 'curve': 'basis'}}}%%
flowchart TB
    %% CLASS DEFINITIONS %%
    classDef cli 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 newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff;
    classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff;
    classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff;
    classDef gap fill:#ff6f00,stroke:#ffa726,stroke-width:2px,color:#000;
    classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff;

    %% TERMINAL %%
    IssueOpen(["Issue: Open<br/>━━━━━━━━━━<br/>No pipeline labels"])

    subgraph InitOnly ["INIT_ONLY FIELDS (set once)"]
        direction LR
        InputURL["inputs.issue_url<br/>━━━━━━━━━━<br/>INIT_ONLY<br/>Never modified"]
        InputBranch["inputs.base_branch<br/>━━━━━━━━━━<br/>INIT_ONLY<br/>Never modified"]
    end

    subgraph ClaimGate ["VALIDATION GATE: claim_issue"]
        direction TB
        IdempotencyCheck{"Label already<br/>in-progress?<br/>━━━━━━━━━━<br/>Idempotency Guard"}
        ClaimTool["● claim_issue MCP tool<br/>━━━━━━━━━━<br/>ensure_label: in-progress<br/>add_labels: in-progress"]
        EscalateStop["escalate_stop<br/>━━━━━━━━━━<br/>Another session owns it<br/>→ abort pipeline"]
    end

    subgraph InProgressState ["MUTABLE STATE: In-Progress"]
        InProgressLabel["Issue Label: in-progress<br/>━━━━━━━━━━<br/>MUTABLE<br/>Added by claim_issue"]
    end

    subgraph BranchGate ["★ VALIDATION GATE: Branch Check (NEW)"]
        direction TB
        BranchDerived{"target_branch<br/>━━━━━━━━━━<br/>DERIVED<br/>== default_base_branch?"}
    end

    subgraph ReleaseGate ["★ VALIDATION GATE: release_issue (EXTENDED)"]
        direction TB
        SkipCheck{"skip_when_false:<br/>inputs.issue_url<br/>━━━━━━━━━━<br/>Guard: no URL → skip"}
        ReleaseSuccess["★ release_issue_success step<br/>━━━━━━━━━━<br/>tool: release_issue<br/>with: target_branch=inputs.base_branch"]
        ReleaseFailure["release_issue_failure step<br/>━━━━━━━━━━<br/>tool: release_issue<br/>no target_branch → failure path"]
    end

    subgraph StagedConfig ["APPEND_ONLY CONFIG"]
        direction TB
        StagedLabelCfg["★ github.staged_label<br/>━━━━━━━━━━<br/>APPEND_ONLY config<br/>Default: 'staged'<br/>Color: #0075ca"]
        EnsureLabel["● release_issue MCP tool<br/>━━━━━━━━━━<br/>ensure_label (422-safe)<br/>add_labels: staged<br/>remove_label: in-progress"]
    end

    subgraph IssueStates ["FINAL ISSUE STATES"]
        direction LR
        StateStagedNew["★ Issue: open + staged<br/>━━━━━━━━━━<br/>Merged to integration<br/>Awaiting promotion to main"]
        StateOpenRetry["Issue: open<br/>━━━━━━━━━━<br/>Failure path: no staged<br/>Available for retry"]
        StateClosed["Issue: closed<br/>━━━━━━━━━━<br/>Merged to main<br/>Closes #N triggered"]
    end

    subgraph StableGuard ["★ STABLE BRANCH GUARD (NEW)"]
        StableCheck{"base_branch == stable<br/>AND feature != main?<br/>━━━━━━━━━━<br/>open-pr pre-flight"}
        GuardExit["Exit code 1<br/>━━━━━━━━━━<br/>Block PR to stable<br/>from non-main source"]
    end

    %% FLOW %%
    IssueOpen --> InitOnly
    InputURL --> IdempotencyCheck
    InputBranch --> BranchDerived

    IdempotencyCheck -->|"not claimed"| ClaimTool
    IdempotencyCheck -->|"already claimed"| EscalateStop
    ClaimTool --> InProgressLabel

    InProgressLabel --> SkipCheck
    SkipCheck -->|"issue_url present"| ReleaseSuccess
    SkipCheck -->|"no issue_url"| StateClosed

    ReleaseSuccess --> BranchDerived
    BranchDerived -->|"target == main (default)"| StateOpenRetry
    BranchDerived -->|"target != main (staging)"| StagedLabelCfg

    StagedLabelCfg --> EnsureLabel
    EnsureLabel --> StateStagedNew

    StateOpenRetry -->|"Closes #N on main merge"| StateClosed

    InProgressLabel -->|"any failure path"| ReleaseFailure
    ReleaseFailure --> StateOpenRetry

    StateStagedNew -->|"Integration PR → main"| StateClosed

    %% STABLE GUARD %%
    InputBranch --> StableCheck
    StableCheck -->|"stable + non-main source"| GuardExit
    StableCheck -->|"allowed"| ClaimTool

    %% CLASS ASSIGNMENTS %%
    class IssueOpen terminal;
    class InputURL,InputBranch detector;
    class IdempotencyCheck,SkipCheck,BranchDerived,StableCheck detector;
    class ClaimTool,ReleaseFailure phase;
    class InProgressLabel stateNode;
    class ReleaseSuccess,StagedLabelCfg,EnsureLabel,StableGuard,BranchGate,ReleaseGate newComponent;
    class StateStagedNew newComponent;
    class StateOpenRetry stateNode;
    class StateClosed output;
    class EscalateStop,GuardExit gap;
Loading

Process Flow Diagram

%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 70, 'curve': 'basis'}}}%%
flowchart TB
    %% CLASS DEFINITIONS %%
    classDef cli 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 newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff;
    classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff;
    classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff;
    classDef gap fill:#ff6f00,stroke:#ffa726,stroke-width:2px,color:#000;
    classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff;

    PipelineStart(["Pipeline Start<br/>━━━━━━━━━━<br/>inputs: issue_url, base_branch"])

    subgraph StableGuardPhase ["★ STABLE BRANCH GUARD (open-pr/SKILL.md)"]
        direction TB
        StableCheck{"● open-pr pre-flight<br/>━━━━━━━━━━<br/>base_branch == 'stable'<br/>AND feature != 'main'?"}
        BlockedExit["Exit code 1<br/>━━━━━━━━━━<br/>Error: stable-only from main<br/>No PR created"]
    end

    subgraph PipelineCore ["PIPELINE CORE (plan → implement → test → merge)"]
        direction TB
        PipelineSteps["plan / implement / test<br/>━━━━━━━━━━<br/>Worktree creation, implementation,<br/>test_check, merge_worktree, push_to_remote"]
        FailureAny["Any step on_failure<br/>━━━━━━━━━━<br/>Routes: release_issue_failure"]
    end

    subgraph MergeQueueRouting ["MERGE QUEUE ROUTING (● implementation.yaml)"]
        direction TB
        CheckMQ["check_merge_queue step<br/>━━━━━━━━━━<br/>gh api GraphQL<br/>captures: queue_available"]
        RouteMQ{"● route_queue_mode<br/>━━━━━━━━━━<br/>queue_available == true?"}
        EnableAutoMerge["enable_auto_merge step<br/>━━━━━━━━━━<br/>Sets up auto-merge on PR"]
        WaitForQueue["wait_for_queue step<br/>━━━━━━━━━━<br/>wait_for_merge_queue tool<br/>captures: pr_state"]
        QueueResult{"● wait_for_queue<br/>━━━━━━━━━━<br/>pr_state == ?"}
        QueueEjected["queue_ejected_fix<br/>━━━━━━━━━━<br/>Resolve ejection → retry"]
    end

    subgraph ReleaseRouting ["★ RELEASE ROUTING (NEW in implementation.yaml)"]
        direction TB
        ReleaseSuccess["★ release_issue_success step<br/>━━━━━━━━━━<br/>tool: release_issue<br/>with: target_branch=inputs.base_branch<br/>optional: true"]
        SkipGuard{"skip_when_false:<br/>━━━━━━━━━━<br/>inputs.issue_url present?"}
    end

    subgraph ReleaseTool ["★ release_issue MCP TOOL LOGIC (● tools_integrations.py)"]
        direction TB
        RemoveInProgress["remove_label: in-progress<br/>━━━━━━━━━━<br/>Always called on success path"]
        BranchDecision{"★ Branch Gate<br/>━━━━━━━━━━<br/>target_branch<br/>== default_base_branch?"}
        EnsureStaged["★ ensure_label: staged<br/>━━━━━━━━━━<br/>422-safe create<br/>Color: #0075ca"]
        AddStaged["★ add_labels: staged<br/>━━━━━━━━━━<br/>Apply staged label<br/>to issue"]
        ReturnStaged["return staged=True<br/>━━━━━━━━━━<br/>staged_label in response"]
        ReturnNoStaged["return staged=False<br/>━━━━━━━━━━<br/>No staging for main target"]
    end

    subgraph FailurePath ["FAILURE PATH (release_issue_failure)"]
        direction TB
        ReleaseFailureTool["release_issue_failure step<br/>━━━━━━━━━━<br/>tool: release_issue<br/>NO target_branch → no staging"]
        CleanupFailure["cleanup_failure<br/>━━━━━━━━━━<br/>Worktree teardown"]
    end

    subgraph SuccessPath ["SUCCESS TERMINAL"]
        direction TB
        ConfirmCleanup["confirm_cleanup<br/>━━━━━━━━━━<br/>Worktree teardown"]
        IssueStaged["Issue: staged<br/>━━━━━━━━━━<br/>★ staged label applied<br/>Awaiting promotion"]
        IssueDone["Issue: released<br/>━━━━━━━━━━<br/>in-progress removed<br/>Normal close flow"]
    end

    %% FLOW %%
    PipelineStart --> StableCheck
    StableCheck -->|"blocked"| BlockedExit
    StableCheck -->|"allowed"| PipelineSteps

    PipelineSteps --> CheckMQ
    PipelineSteps --> FailureAny
    FailureAny --> ReleaseFailureTool
    ReleaseFailureTool --> CleanupFailure

    CheckMQ --> RouteMQ
    CheckMQ -->|"on_failure"| ReleaseSuccess

    RouteMQ -->|"queue == true"| EnableAutoMerge
    RouteMQ -->|"queue == false (● was confirm_cleanup)"| ReleaseSuccess

    EnableAutoMerge --> WaitForQueue
    WaitForQueue --> QueueResult

    QueueResult -->|"merged (● was confirm_cleanup)"| ReleaseSuccess
    QueueResult -->|"ejected"| QueueEjected
    QueueResult -->|"timeout/other (● was confirm_cleanup)"| ReleaseSuccess
    QueueEjected -->|"retry"| WaitForQueue

    ReleaseSuccess --> SkipGuard
    SkipGuard -->|"no issue_url"| ConfirmCleanup
    SkipGuard -->|"issue_url present"| RemoveInProgress

    RemoveInProgress --> BranchDecision
    BranchDecision -->|"target != main"| EnsureStaged
    BranchDecision -->|"target == main"| ReturnNoStaged
    EnsureStaged --> AddStaged
    AddStaged --> ReturnStaged
    ReturnStaged --> IssueStaged
    ReturnNoStaged --> IssueDone
    IssueStaged --> ConfirmCleanup
    IssueDone --> ConfirmCleanup

    %% CLASS ASSIGNMENTS %%
    class PipelineStart terminal;
    class StableCheck,BranchDecision,QueueResult,SkipGuard,RouteMQ detector;
    class BlockedExit gap;
    class PipelineSteps,FailureAny,CheckMQ,EnableAutoMerge,WaitForQueue,QueueEjected handler;
    class ReleaseSuccess,RemoveInProgress,EnsureStaged,AddStaged,ReturnStaged,IssueStaged newComponent;
    class ReleaseFailureTool,CleanupFailure,ConfirmCleanup phase;
    class ReturnNoStaged,IssueDone stateNode;
Loading

Closes #382

Implementation Plan

Plan file: /home/talon/projects/autoskillit-runs/impl-382-20260314-205351-453631/temp/make-plan/pipeline_staged_issue_lifecycle_plan_2026-03-14_210500.md

Token Usage Summary

No token data collected.

🤖 Generated with Claude Code via AutoSkillit

Trecek and others added 7 commits March 14, 2026 22:22
…entation.yaml

- test_bundled_recipes: assert release_issue_success IS in implementation steps
- test_issue_url_pipeline: decouple step-presence lists from ci_watch routing lists;
  add RECIPES_WITH_RELEASE_SUCCESS_STEP including implementation, update
  test_release_issue_steps_present and test_release_issue_success_routes_to_confirm_cleanup
- test_open_pr_contracts: widen search window from 3000→4000 chars to account for
  new Step 0 content added to open-pr/SKILL.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Collaborator Author

@Trecek Trecek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit PR Review — Verdict: changes_requested (8 actionable findings, 3 decision findings)

release_issue_success:
description: "Transition issue from in-progress to staged after successful pipeline completion"
tool: release_issue
optional: true
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning] cohesion: release_issue_success has optional:true but the visible sibling release_issue_failure does not appear to have optional:true. Symmetric counterparts should have matching optionality, or document the asymmetry.

Copy link
Collaborator Author

@Trecek Trecek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit review found 8 blocking issues. See inline comments.

Verdict: changes_requested

8 actionable findings (clear fixes), 3 decision findings (require human judgment).

Key issues:

  • tools_integrations.py: Inconsistent error response schema — remove_label/staging failure paths omit fields present in success path
  • tests/server/test_tools_integrations_release.py: Weak mock assertions (no arg verification on remove_label/ensure_label)
  • implementation.yaml: Timeout path routes to release_issue_success identically to merge success — consider whether timed-out PRs should receive the staged label

@Trecek Trecek enabled auto-merge March 15, 2026 06:38
@Trecek Trecek added this pull request to the merge queue Mar 15, 2026
Merged via the queue into integration with commit 391f2ae Mar 15, 2026
2 checks passed
@Trecek Trecek deleted the pipeline-leaves-issues-open-and-releases-claims-prematurely/382 branch March 15, 2026 06:41
Trecek added a commit that referenced this pull request Mar 15, 2026
…, 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>
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