Skip to content

Implementation Plan: token_summary_appender — Replace gh pr edit/view with REST API#551

Merged
Trecek merged 3 commits intointegrationfrom
token-summary-appender-replace-gh-pr-edit-with-rest-api-grap/550
Mar 28, 2026
Merged

Implementation Plan: token_summary_appender — Replace gh pr edit/view with REST API#551
Trecek merged 3 commits intointegrationfrom
token-summary-appender-replace-gh-pr-edit-with-rest-api-grap/550

Conversation

@Trecek
Copy link
Copy Markdown
Collaborator

@Trecek Trecek commented Mar 28, 2026

Summary

token_summary_appender.py calls gh pr view and gh pr edit --body, both of which invoke
the updatePullRequest GraphQL mutation internally. GitHub now returns a fatal error on repos
that ever had Projects Classic enabled:

GraphQL: Projects (classic) is being deprecated in favor of the new Projects experience
(repository.pullRequest.projectCards)
EXIT: 1

The fix adds _parse_pr_url_parts(pr_url) to extract (owner, repo, number) from the PR URL, then replaces gh pr view with gh api repos/{owner}/{repo}/pulls/{number} --jq '.body' and gh pr edit --body with gh api repos/{owner}/{repo}/pulls/{number} --method PATCH --field body=<new_body>. All test mocks are updated to match the new REST API call signatures, and a source-level guard test verifies that the broken GraphQL-backed commands are absent.

Requirements

GH (GitHub API Migration)

TEST (Verification)

  • REQ-TEST-001: An integration test must verify that the REST API path is used for both read and write operations (no gh pr edit or gh pr view subprocess calls remain).
  • REQ-TEST-002: The existing test_token_summary_appender.py tests must be updated to mock the REST API calls instead of gh pr edit/gh pr view.

Architecture Impact

Process Flow Diagram

%%{init: {'flowchart': {'nodeSpacing': 40, 'rankSpacing': 50, '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;

    START([PostToolUse fires])

    subgraph Ingestion ["Event Ingestion"]
        direction TB
        ParseEvent["● main()<br/>━━━━━━━━━━<br/>stdin → tool_name, tool_response_raw"]
        ExtractURL["_extract_pr_url()<br/>━━━━━━━━━━<br/>regex search for github.com PR URL"]
        NoURL{"PR URL<br/>found?"}
        ParseParts["● _parse_pr_url_parts()<br/>━━━━━━━━━━<br/>extract owner, repo, number<br/>from PR URL"]
        NoParts{"parts<br/>valid?"}
    end

    subgraph SessionLoad ["Session Aggregation"]
        direction TB
        LoadSessions["_load_sessions()<br/>━━━━━━━━━━<br/>filter sessions.jsonl by pipeline_id<br/>aggregate token_usage.json per step"]
        NoSessions{"sessions<br/>found?"}
    end

    subgraph GitHubRead ["● GitHub REST Read"]
        direction TB
        APIRead["● gh api repos/{owner}/{repo}/pulls/{number}<br/>━━━━━━━━━━<br/>--jq '.body'<br/>(was: gh pr view --json body --jq .body)"]
        ReadFail{"returncode<br/>== 0?"}
        AlreadyHas{"body has<br/>## Token Usage<br/>Summary?"}
        EmptyBody{"body<br/>empty?"}
    end

    subgraph TableBuild ["Table Construction"]
        FormatTable["_format_table()<br/>━━━━━━━━━━<br/>build markdown token table<br/>from aggregated step data"]
        Concat["concat body + table<br/>━━━━━━━━━━<br/>new_body = current_body + table"]
    end

    subgraph GitHubWrite ["● GitHub REST Write"]
        direction TB
        APIPatch["● gh api repos/{owner}/{repo}/pulls/{number}<br/>━━━━━━━━━━<br/>--method PATCH --field body=new_body<br/>(was: gh pr edit --body)"]
        WriteFail{"CalledProcess<br/>Error?"}
    end

    EXIT0([EXIT 0])
    EXIT0_DIAG([EXIT 0 + stderr diagnostic])
    EXIT1([EXIT 1])
    COMPLETE([EXIT 0 — summary appended])

    START --> ParseEvent
    ParseEvent --> ExtractURL
    ExtractURL --> NoURL
    NoURL -->|"no URL"| EXIT0
    NoURL -->|"URL found"| ParseParts
    ParseParts --> NoParts
    NoParts -->|"parse failed"| EXIT0
    NoParts -->|"owner/repo/number"| LoadSessions
    LoadSessions --> NoSessions
    NoSessions -->|"empty"| EXIT0
    NoSessions -->|"sessions present"| APIRead
    APIRead --> ReadFail
    ReadFail -->|"rc != 0"| EXIT0_DIAG
    ReadFail -->|"rc == 0"| AlreadyHas
    AlreadyHas -->|"yes — idempotent"| EXIT0
    AlreadyHas -->|"no"| EmptyBody
    EmptyBody -->|"empty body"| EXIT0_DIAG
    EmptyBody -->|"has content"| FormatTable
    FormatTable --> Concat
    Concat --> APIPatch
    APIPatch --> WriteFail
    WriteFail -->|"raised"| EXIT1
    WriteFail -->|"success"| COMPLETE

    class START,EXIT0,EXIT0_DIAG,EXIT1,COMPLETE terminal;
    class ParseEvent,ExtractURL,LoadSessions,FormatTable,Concat handler;
    class NoURL,NoParts,NoSessions,ReadFail,AlreadyHas,EmptyBody,WriteFail stateNode;
    class ParseParts,APIRead,APIPatch newComponent;
Loading

Color Legend:

Color Category Description
Dark Blue Terminal START, EXIT, and COMPLETE states
Orange Handler Existing processing nodes
Teal State Decision / routing gates
Green New Component — modified subprocess calls and helper

Closes #550

Implementation Plan

Plan file: /home/talon/projects/autoskillit-runs/impl-20260327-205423-846457/.autoskillit/temp/make-plan/token_summary_appender_rest_api_plan_2026-03-27_000000.md

🤖 Generated with Claude Code via AutoSkillit

…appender

Bypasses the broken GraphQL path that fatal-errors on repos with Projects
Classic history. Adds _parse_pr_url_parts() helper, replaces subprocess
calls with gh api REST equivalents, and updates all test mocks accordingly.

REQ-TEST-001: guard test added to assert gh pr edit/view are absent from source.
Copy link
Copy Markdown
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

Comment thread src/autoskillit/hooks/token_summary_appender.py

REQ-TEST-001: verifies both read and write operations use gh api (REST).
"""
from autoskillit.core.paths import pkg_root
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.

[warning] slop: Redundant local import inside function body. pkg_root is already imported at module level (used in test_tsa1_token_summary_appender_script_exists at line 105). Remove the from autoskillit.core.paths import pkg_root local import.

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.

Investigated — this is intentional. Module-level imports (lines 7-15) contain no 'from autoskillit.core.paths import pkg_root'. The import at line 103 (inside test_tsa1_token_summary_appender_script_exists) is itself a local function-body import, not a module-level import. Same pattern at line 132. The local import at line 113 is consistent with the file-wide convention of lazy per-function imports, not a redundancy.

Copy link
Copy Markdown
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 2 blocking issues. See inline comments.

@Trecek Trecek added this pull request to the merge queue Mar 28, 2026
Merged via the queue into integration with commit 7242d86 Mar 28, 2026
2 checks passed
@Trecek Trecek deleted the token-summary-appender-replace-gh-pr-edit-with-rest-api-grap/550 branch March 28, 2026 04:35
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